Annotated Listing of The System
Annotated Listing of The System
from tkinter import * # This imports the tkinter library so that the program can start being constructed.
from tkinter import ttk # This import is used for styling widgets.
import tkinter as tk # This import provides aesthetical features to the widgets that use it.
import tkinter.messagebox # Imported in order to be able to display message boxes to the user.
from tkinter import messagebox,filedialog # This import is necessary for the email and stock windows, it is used
for the opening and saving of files.
from datetime import datetime # Used to validate the date format of the date entry field.
import cusDatabase_BackEnd # Impots the customer database python file into the main program.
import serDatabase_BackEnd # Impots the service database python file into the main program.
import priDatabase_BackEnd # Impots the principal database python file into the main program.
import random # Allows for random numbers to be generated.
import calcDatabase_Backend # Impots the calculator database python file into the main program.
from email.message import EmailMessage # This import allows for emails to be sent from the program.
import smtplib # Used for the email sender which requires access to the smtp servers
import os # Used for accessign files and I have used it to attach files to the emails in my confirmation email
window.
import imghdr # Used to determine what filetype the desired image is when it is attached to the email in the
confirmation window.
import time # Imports time to allow for me to work with the time in various respects. I have used it to display the
current date automatically into the stock management window.
import re # Imports regex which I have used for format validation techniques.
check = False # This is here to let the program know that unless the an attachment has been selected by the user,
no attachment will be sent with along with the email.
# String Variables to be assigned as textvariables to the entry fields used in the appointment windows.
CustomerID = StringVar()
CustomerName = StringVar()
CustomerDOB = StringVar()
gender = StringVar()
email = StringVar()
phone = StringVar()
PrincipalID = StringVar()
ServiceID = StringVar()
Time = StringVar()
ServiceName = StringVar()
ServicePrice = StringVar()
Date = StringVar()
ExtraProducts = StringVar()
# This opens the windows directory window from which the user can select their desired document attachment.
filepath = filedialog.askopenfilename(initialdir='c:/', title='Select a file')
filetype = filepath.split('.')
filetype = filetype[1]
# This allows the function to attach a file from the computer by having access to its filepath and the os
import allows the contents of the file to be fetched.
filename = os.path.basename(filepath)
textarea.insert(END,
f'\n{filename}\n') # This shows the name of the selected file in the email textbox so that
the user knows what he has selected.
# This function enables the confirmation emails to be sent via the smtp servers.
def sendingEmail(toAddress, subject, body):
user_details = open('credentials.txt', 'r')
for i in user_details:
credentials = i.split(',')
# This composes the actual email message that is to be sent to the customer.
message = EmailMessage()
message['subject'] = subject # Forwards what is in the subject field as the subject.
message['to'] = toAddress # Forwards what is in the recipient field as the name of the recipent email
address.
message['from'] = credentials[
0] # Forwards what is in the Expedient field as the expedient of the email, needs to be read from the
"credentials.txt" file.
message.set_content(body) # #Forwards what is in the body field as the body of the email message.
# This states that if the user has chosen to make any attachments by having confirmed the "check" variable
then the file will be added.
# the file will only be added and sent if it is a png, jpg or jpeg document.
if check:
if filetype == 'png' or filetype == 'jpg' or filetype == 'jpeg':
file_path = open(filepath, 'rb')
file_data = file_path.read()
subtype = imghdr.what(
filepath) # This is so that when the user selects the desired document, it's filepath becomes
known so that it's contents can be fetched.
message.add_attachment(file_data, maintype='image', subtype=subtype,
filename=filename) # Attaches the attachment for sending.
def send_email():
# This stops the process and prints an error message if the email, subject and the body fields have not
beeen inputted.
if RecipientEntryField.get() == '' or subjectEntryField.get() == '' or textarea.get(1.0, END) == '\n':
messagebox.showerror('Error', 'All Fields Are Required', parent=window)
else:
if RecipientEntryField.get():
result = sendingEmail(RecipientEntryField.get(), subjectEntryField.get(), textarea.get(1.0, END))
# It determines if the email was sent or it failed from the sending Email function.
if result == 'sent':
# This displays an success message if the email was sent.
messagebox.showinfo('Success',
'Email was sent successfully!') # This displays a success message if the
email was sent.
# It determines if the email was sent or it failed from the sending Email function.
if result == 'failed':
# This displays an error message if the email was not sent.
messagebox.showerror('Error', 'Email could not send, please wait and a few seconds and try
again!')
# This creates the setting window which will store and be able to change the sender's email
address and the password provided by Gmail
def settings():
def clear1():
ExpedientEntryField.delete(0, END)
KeyEntryField.delete(0,
END) # This Allows the "Clear" button to clear the settings entry boxes in the
settings window on click.
def save():
# An error message is displayed if the user attempts to save the data and not both of the entry fields
are completed.
if ExpedientEntryField.get() == '' or KeyEntryField.get() == '':
messagebox.showerror('Error', 'All Fields Are Required', parent=window1)
else:
user_details = open('credentials.txt',
'w') # A text file was created to store the Gmail address and the password of
the user for use in the settings window.
user_details.write(ExpedientEntryField.get() + ',' + KeyEntryField.get())
user_details.close()
messagebox.showinfo('Credential information', 'Credentials saved successfully!', parent=window1)
# This imports the Hair craft logo into the program for use on this window
HairCraftImage3 = PhotoImage(file='Hair_Craft_logo3.png')
titlelabel = Label(window1, text='User Credentials', image=HairCraftImage3,
# This inserts the logo image of Hair Carft for cosmetic purposes and it also sets the
title of the window.
compound=LEFT, font=('goudy old style', 28, 'bold'),
fg='black', bg='white').grid(padx=60)
# This creates the labelframe in which the user will input their own email address.
ExpedientLabelFrame = LabelFrame(window1, text='Expedient (Email Address)',
font=('times new roman', 16, 'bold'), bd=5, fg='white',
bg='dodger blue2')
ExpedientLabelFrame.grid(row=1, column=0, pady=20)
# This creates the expedient's email entry field.
ExpedientEntryField = Entry(ExpedientLabelFrame, font=('times new roman', 18, 'bold'),
width=30) # Creates entry field for the sender's Gmail address.
ExpedientEntryField.grid(row=0, column=0)
# This creates the labelframe in which the user will insert their two step verification key.
KeyLabelFrame = LabelFrame(window1, text='Verification key', font=('times new roman', 16, 'bold'), bd=5,
fg='white',
bg='dodger blue2')
KeyLabelFrame.grid(row=2, column=0, pady=20)
# Creates entry field for the sender's Gmail password and displays the password as "*" for security.
KeyEntryField = Entry(KeyLabelFrame, font=('times new roman', 18, 'bold'), width=30, show='*')
KeyEntryField.grid(row=0, column=0)
# This button allows the entered data to be stored in the "credentials.txt" file for later use.
saveImage = PhotoImage(file='Save.png') # Imports the "Save" image into the program.
btn_Save = Button(window1, image=saveImage, bd=0, bg='dodger blue2', cursor='hand2',
activebackground='dodger blue2'
, command=save).place(x=150, y=270)
# This reads from the "credentials" text file and splits it's content's as to always show the user's email
address and their password in asterix.
user_details = open('credentials.txt', 'r')
for i in user_details:
credentials = i.split(',')
ExpedientEntryField.insert(0, credentials[0])
KeyEntryField.insert(0, credentials[1])
window1.mainloop()
# This function allows the user to close the sendeer on button click by clicking the exit button and then
clicking yes in the messagebox asking them if they are sure that the want to exit.
def iexit():
result = messagebox.askyesno('Notification', 'Are you sure you want to exit?')
if result > 0:
window.destroy()
else:
pass
# This creates the main window on which the the email widgets are placed.
window = Toplevel()
window.title('Confirmation Email')
window.geometry('780x620+100+50')
window.resizable(0, 0)
window.config(bg='dodger blue2')
# This creates the title label which showcases the logo, window title and the settings button.
titleFrame = Frame(window, bg='white')
titleFrame.grid(row=0, column=0)
# This imports and places the logo and the title of the window in the title frame of the window.
HairCraftImage2 = PhotoImage(file='Hair_Craft_logo2.png')
Emailtitle = Label(titleFrame, text=' Confirmation Email', image=HairCraftImage2,
compound=LEFT, font=('Goudy Old Style', 28, 'bold'),
bg='white', fg='black')
Emailtitle.grid(row=0, column=0)
# Creates the button which will open the credential settings window.
btn_settings = Button(titleFrame, image=settingImage, bd=0, bg='white', cursor='hand2', activebackground='white'
, command=settings).grid(row=0, column=1, padx=20)
# This try statement allows the program to retrieve the email address of the last appointment made and
automatically put it
# in the Recipient's entry field.
RecipientEntryField = Entry(RecipientLabelFrame, font=('times new roman', 18, 'bold'), width=30)
RecipientEntryField.grid(row=0, column=0)
try:
with open("lastEmail.txt", "r") as f:
RecipientEntryField.insert(END, f.read())
except:
pass
# This creates the subject labelframe which holds the email subject entry field.
subjectLabelFrame = LabelFrame(window, text='Email Subject', font=('times new roman', 16, 'bold'), bd=5,
fg='white',
bg='dodger blue2')
subjectLabelFrame.grid(row=3, column=0, pady=10)
# This creates the subject entry field with a pre-written subject for the user.
subjectEntryField = Entry(subjectLabelFrame, font=('times new roman', 18, 'bold'), width=30)
subjectEntryField.insert(END, "Hair Craft Appointment Details")
subjectEntryField.grid(row=0, column=0)
# This creates the email labelframe which holds the email body text box in which the main message will be
composed.
emailLabelFrame = LabelFrame(window, text='E-mail message', font=('times new roman', 16, 'bold'), bd=5,
fg='white',
bg='dodger blue2')
emailLabelFrame.grid(row=4, column=0, padx=20)
# This creates the text box in which the body of the email is written.
textarea = Text(emailLabelFrame, font=('times new roman', 14,), height=8)
textarea.grid(row=1, column=0, columnspan=2)
# This takes the information that was inputted in the appointment window, reads it and compiles it into the
message shown above.
# This is so that the confirmation emails will be both informative and similar to one another as only the
appointment information will differ.
try:
email_details = open("emaildetails.txt", "r")
for i in email_details:
emaildetails = i.split(
',') # This splits the gathered data from the main appointment information like a 1D array.
name = (0, emaildetails[0])
service_name = (0, emaildetails[1])
Service_price = (0, emaildetails[2])
appointment_date = (0, emaildetails[3])
appointment_time = (0, emaildetails[4])
textarea.insert(END, "Thank you " + emaildetails[
0] + " for choosing Hair Craft as your Hairdresser salon. You have been successfully booked on the " +
emaildetails[3] \
+ " at " + emaildetails[4] + " for your " + emaildetails[
1] + " Service. The cost of your appointment is £ " + emaildetails[2] \
+ ". Kind regards Ceri McKim and the Hair Craft team, see you soon!")
except:
pass
# This places the "send" image as a button and assigns it's function to it.
sendImage = PhotoImage(file='send.png') # Imports the "send" image into the program.
btn_send = Button(window, image=sendImage, bd=0, bg='dodger blue2', cursor='hand2', activebackground='dodger
blue2'
, command=send_email).place(x=490, y=540)
# This places the "clear" image as a button and assigns it's function to it.
clearImage = PhotoImage(file='clear.png') # Imports "clear" image into the program.
btn_Clear = Button(window, image=clearImage,
bd=0, bg='dodger blue2', cursor='hand2', activebackground='dodger blue2'
, command=clear).place(x=590, y=550)
# This places the "exit" image as a button and assigns it's function to it.
exitImage = PhotoImage(file='exit.png') # Imports the "exit" image into the program.
btn_Exit = Button(window, image=exitImage,
bd=0, bg='dodger blue2', cursor='hand2', activebackground='dodger blue2'
, command=iexit).place(x=690, y=550)
window.mainloop()
# This function allows the user to clear the entry fields on this window on click
def clearData_Main():
principal_ID.delete(0, tkinter.END)
date_entry2.delete(0, tkinter.END)
ID_entry2.delete(0, tkinter.END)
service_name2.delete(0, tkinter.END)
# This function displays the information saved into the principal database on click.
def DisplayInfo_Main():
Principallist.delete(0, END)
for rows in priDatabase_BackEnd.viewMainData():
Principallist.insert(END, "%d,|%s| |%s| |%s| |%s| |%s| |%s| |£%s|" % rows)
# This function allows you to delete a record by typing in the number of the record o you want to delete into
the Patient Id box and pressing Delete.
def DeletePrincipalData():
if principal_ID.get():
priDatabase_BackEnd.deleteMainRec(principal_ID.get())
clearData_Main()
DisplayInfo_Main()
# This function allows the user to search for records by their date, Customer ID or service name.
def searchPrincipal():
Principallist.delete(0, END)
for rows in priDatabase_BackEnd.searchDataMain(date_entry2.get(), ID_entry2.get(), service_name2.get()):
rows = list(rows)
for i in range(len(rows)):
rows[i] = str(rows[i]).strip()
Principallist.insert(END, rows, str(""))
# ======================================Navigation bar========================================================
# Those are the functions which are to be assigned to the navigation bar's buttons
def mainmenu_wind():
window.destroy()
staff_menu()
def makeappointment_wind():
window.destroy()
Staff_appointment_wind()
# Allows the user to exit the window if the button that it is assigned to is clicked.
def iExit():
iExit = tkinter.messagebox.askyesno("Customer Registration Form", "Confirm that you want to exit")
if iExit > 0:
window.destroy()
return
# This function just resets the entry fields to blank when the button that it is assigned to is clicked.
def clearData():
ID_entry.delete(0, tkinter.END)
name_entry.delete(0, tkinter.END)
DOB.delete(0, tkinter.END)
phone_nr.delete(0, tkinter.END)
email_ad.delete(0, tkinter.END)
cus_gender.delete(0, tkinter.END)
# This function allows you to cancel an appointment by typing in the number of the record of the appointment you
want to delete into the Customer Id box
# and pressing "Delete Record".
def DeleteData():
if ID_entry.get():
cusDatabase_BackEnd.deleteRec(ID_entry.get())
clearData()
DisplayInfo()
# This performs a search on the ID entry and name_entry, which means that if either one is entered into
# their respective entry fields and the search button is clicked then the search will be performed.
def search():
Customerlist.delete(0, END)
for rows in cusDatabase_BackEnd.searchData(ID_entry.get(), name_entry.get()):
rows = list(rows)
for i in range(len(rows)):
rows[i] = str(rows[i]).strip()
Customerlist.insert(END, rows, str(""))
# This creates the frame in which the on screen validation will be displayed.
CustomerValidationFrame = LabelFrame(window, bd=5)
CustomerValidationFrame.place(x=1150, y=260, width=600)
try:
gender_style = r"^[a-zA-Z]+(([',. -][a-zA-Z ])?[a-zA-Z]*)*$"
if re.search(gender_style, cus_gender.get()):
answer4.config(text="The gender entry is valid!")
# If the input does not respect the validation requirements then a negative message which will let
the user know what has gone wrong
# will be displayed on screen and a return statement will be used so that the program will not get
to the add data part and as such
# not add any of the data to the database if all of the entries have not been found to be valid yet.
else:
answer4.config(text="The gender entry is not valid because it may contain integer characters!")
return
except ValueError:
pass
# The email entry must repect this regex email style in order for it to be considered valid and for the
entry of the data to occur.
try:
email_style = r"^([\w\.]+)\@([\w\.]+)(\.\w+)$"
if re.search(email_style, email_ad.get()):
answer5.config(text="That is a valid email record format!")
# If the format is not respected then this negative validation message will be displayed and the
return statement will take action
# hence not adding any of the data to the database.
else:
answer5.config(text="That is not a valid email address due to it's format!")
return
except ValueError:
pass
# The DOB entry must repect the dd/mm/yyyy format in order for it to be considered valid and for the
entry of the data to occur.
i = str(DOB.get())
try:
date_start = datetime.strptime(i,
'%d/%m/%Y') # DOB entry must conform to this format otherwise it
willbe considered invalid.
answer6.config(text="That is a valid date of birth format!")
# If the entry does not respect the date format then the except statement is called along the return
statement
# and the record will not be added to the database.
except ValueError:
answer6.config(text="Incorrect date of birth data format, it should be DD/MM/YYYY instead!")
return
try:
# The ID entry is defined as an integer so that the validation routine can check for length and data
type
# with the help of the try/except statement.
i = int(ID_entry.get())
if i < 100000:
answer2.config(
text="The ID is too short!") # If the ID is less than 6 digits long then this message will
be shown on screen.
return
if i > 999999:
answer2.config(
text="The ID is too long!") # If the ID is more than 6 digits long then this message will
be shown on screen.
return
if 99999 < i < 1000000:
answer2.config(
text="That is valid ID!") # If the ID is 6 digits long then this message will be shown on
screen.
# Only if all of the entry fields have been entered and all of the entries have been validated
respectively will the data be added to the database.
# And only then will the newlyadded record be displayed on the main screen.
tkinter.messagebox.showinfo("Customer information added", "Customer information succesfully added!")
cusDatabase_BackEnd.addCustomerRec(ID_entry.get(), DOB.get(), name_entry.get(), phone_nr.get(),
email_ad.get(), cus_gender.get())
Customerlist.delete(0, END)
Customerlist.insert(END, (
ID_entry.get(), DOB.get(), name_entry.get(), phone_nr.get(), email_ad.get(), cus_gender.get()))
# This takes the email address supplied and writes it to a text file in order for it to be entered into
the email window.
email = str(email_ad.get())
email_ad.set = (email)
lastEmail = open("lastEmail.txt", "w")
lastEmail.write(str(email))
lastEmail.close()
# This places the validation answers into the Validation label frame.
answer = Label(CustomerValidationFrame, text="")
answer.pack()
# This creates the "Customer's E-mail" of the customer entry box and label
Email_label = Label(window, text="Customer E-mail:", font=('arial', 13, 'bold'))
Email_label.place(x=30, y=220)
email_ad = ttk.Entry(window, textvariable=email)
email_ad.place(x=210, y=220)
email_ad.focus()
# This creates the "Phone number" of the customer entry box and label
Phone_label = Label(window, text="Phone number:", font=('arial', 13, 'bold'))
Phone_label.place(x=30, y=260)
phone_nr = ttk.Entry(window, textvariable=phone)
phone_nr.place(x=210, y=260)
phone_nr.focus()
# Creates the validation frame where the validation messages will be displayed for the entries.
ServiceValidationFrame = LabelFrame(window, bd=5)
ServiceValidationFrame.place(x=1150, y=750, width=600)
def AddInfo_Service():
# This ensures that presence checks will take place on all of the entries.
if service_ID.get() == '' or product_name.get() == '' or service_name.get() == '' or service_price.get() ==
'' or date_entry.get() == '' \
or Time_entry.get() == '':
tkinter.messagebox.showerror("Error", "Please make sure to complete all of the entry fields!")
else:
try:
# This ensures that the Service_ID will be an integer.
i = int(service_ID.get())
# If the ID is less than 6 digits long then this message will be shown on screen the program will
stop because of the "return".
if i < 100000:
answer12.config(text="The ID is too short!")
return
# If the ID is more than 6 digits long then this message will be shown on screen & the program will
stop because of the "return".
if i > 999999:
answer12.config(text="The ID is too long!")
return
if 99999 < i < 1000000:
answer12.config(
text="That is valid ID!") # If the ID is 6 digits long then this message will be shown on
screen.
# If the entry is not an integer then this negative message will be displayed.
except ValueError:
answer12.config(
text="That is not a valid ID entry because it contains other characters that are not integers!")
return
try:
service_name_style = r"^[a-zA-Z]+(([',. -][a-zA-Z ])?[a-zA-Z]*)*$" # This just sets the format that
the service name must respect.
if re.search(service_name_style, service_name.get()):
answer7.config(text="That is a valid service name!")
else:
# If the entry doesn't respect this format then this negative message will be displayed.
answer7.config(text="That is not a valid service name because it may contain integers!")
return
except ValueError:
pass
try:
int(service_price.get()) # This just states that the service price must be an integer.
answer8.config(text="That is a valid service price!")
# If the entry is not an integer then this negative message will be displayed.
except ValueError:
answer8.config(
text="That is not a valid service price because it contains other characters that are not
integers!")
return
timeformat = "%H:%M" # This sets out the format that the appointment time must follow for it to be
considered valid.
i = str(Time_entry.get())
try:
validtime = datetime.strptime(i, timeformat)
answer10.config(
text="That is a valid time entry!") # This message will be displayed if the HH:MM format is
respected.
# If the entry doesn't respect this time format then this negative message will be displayed.
except ValueError:
answer10.config(
text="The time entry is not valid because it is not in the correct format!") # This message
will be displayed if the HH:MM format is not respected.
return
try:
product_name_style = r"^[a-zA-Z]+(([',. -][a-zA-Z ])?[a-zA-Z]*)*$" # This just sets the style that
the product name must respect.
if re.search(product_name_style, product_name.get()):
answer11.config(
text="That is a valid product name!") # If the style is respected then this message will be
displayed.
else:
answer11.config(
text="That is not a valid product name because it may contain integers!") # If the entry
doesn't respect this format then this negative message will be displayed.
return
except ValueError:
pass
i = str(date_entry.get())
try:
date_start = datetime.strptime(i,
'%d/%m/%Y') # This is states the format that the appointment date
must respect in order for validation to be granted.
answer9.config(text="That is a valid date format!")
# If the entry doesn't respect this date format then this negative message will be displayed.
except ValueError:
answer9.config(text="Incorrect date format, it should be DD/MM/YYYY instead!")
return
# This allows for information to be added to the database and be displayed after it is successfully
validated.
tkinter.messagebox.showinfo("Appointment Made", "Appointment Successfully Made!")
serDatabase_BackEnd.addServiceRec(service_ID.get(), product_name.get(), service_name.get(),
service_price.get(), date_entry.get(), Time_entry.get())
Servicelist.delete(0, END)
Servicelist.insert(END, (
service_ID.get(), product_name.get(), service_name.get(), service_price.get(), date_entry.get(),
Time_entry.get()))
# This creates a text file that writes various appointment related details which will be used
# to create a confirmation message for the user in a mannered & semi-automatic way.
email_details = open('emaildetails.txt', 'w')
email_details.write(
name_entry.get() + ',' + service_name.get() + ',' + service_price.get() + ',' + date_entry.get() +
',' + Time_entry.get())
email_details.close()
# This lists all of the records in the database on demand by retrieving them from the database.
# and inserting them into the Servicelist listbox.
def DisplayInfo_Service():
Servicelist.delete(0, END)
for row in serDatabase_BackEnd.viewServiceData():
Servicelist.insert(END, "%d,|%s| |%s| |%s| |£%s| |%s| |%s|" % row)
# This allows for any data item in the database to be searched for as long as it is inputted in it's determined
entry field.
# It does this by fetching the record/records with the matching searched item and displayong the entire record
into the listbox.
def searchService():
Servicelist.delete(0, END)
for rows in serDatabase_BackEnd.searchDataService(Time_entry.get(), service_ID.get(), product_name.get(),
service_name.get(), service_price.get(),
date_entry.get()):
rows = list(rows)
for i in range(len(rows)):
rows[i] = str(rows[i]).strip()
Servicelist.insert(END, rows, str(""))
# This function allows you to cancel an appointment by typing in the record ID of the appointment you want to
delete into the Service Id box
# and pressing "Delete Record".
def DeleteServiceData():
if service_ID.get():
serDatabase_BackEnd.deleteServiceRec(service_ID.get())
clearData_Service()
DisplayInfo_Service()
# This places the validation answers into the Validation label frame.
answer12 = Label(ServiceValidationFrame, text="")
answer12.pack()
# This ensures that if there are any empty required fields an error message will appear even though the finish
appointment button must be clicked after
# the Customer snd Service information has been validated and added to the databases.
def Finish_Appointment():
if service_ID.get() == '' or product_name.get() == '' or service_name.get() == '' or service_price.get() ==
'' or date_entry.get() == '' \
or Time_entry.get() == '' or name_entry.get() == '':
tkinter.messagebox.showerror("Error", "Error, please complete all entry fields!")
# This function adds the key appointment details into the Principal database whic can be used to view and
search for appointment detais in an easy and convenient way.
else:
tkinter.messagebox.showinfo("Appointment Made", "Process complete!")
priDatabase_BackEnd.addMainRec(date_entry.get(), Time_entry.get(), service_ID.get(), name_entry.get(),
product_name.get(), service_name.get(), service_price.get())
Principallist.insert(END, (
date_entry.get(), Time_entry.get(), service_ID.get(), name_entry.get(), product_name.get(),
service_name.get(), service_price.get()))
# This button allows the user to delete a record based on the record id that they enter.
btnDeleteS = tk.Button(window, text="Delete Record", command=DeleteServiceData, cursor='hand2')
btnDeleteS.place(x=1300, y=530, width=175, height=50)
# This button is used to display all of the records held in the database.
btnDisplayS = tk.Button(window, text="Display", command=DisplayInfo_Service, cursor='hand2')
btnDisplayS.place(x=1150, y=530, width=125, height=50)
# This allows for the user to clear the entry fields on click.
btnClearS = tk.Button(window, text="Clear", command=clearData_Service, cursor='hand2')
btnClearS.place(x=1150, y=580, width=125, height=50)
# This allows for the search function being called for the user to use to search for a record.
btnSearchS = tk.Button(window, text="Search record", command=searchService, cursor='hand2')
btnSearchS.place(x=1150, y=630, width=125, height=50)
# This button is used to finnish the appointment which means that the important data is entered into the main
appointment details table.
btnFinishS = tk.Button(window, text="Finish appointment", command=Finish_Appointment, cursor='hand2')
btnFinishS.place(x=1300, y=630, width=175, height=50)
# This creates the ID Generator button which redirects the user to the ID generator when clicked.
btnGenerate = tk.Button(window, text="Generate ID", command=generate_ID, cursor='hand2')
btnGenerate.place(x=1150, y=680, width=125, height=50)
# This creates the "Confirmation Eamil button which opens the email window when clicked.
btnemail_send = tk.Button(window, text="Confirmation Email", command=email_wind, cursor='hand2')
btnemail_send.place(x=1300, y=680, width=175, height=50)
# ====================================Navigation bar=================================
# Those are the functions which are to be assigned to the navigation bar's buttons
def LExit():
iExit = tkinter.messagebox.askyesno("Log-out", "Are you sure that you want to log-out?")
if iExit > 0:
window.destroy()
return
def mainmenu_wind():
window.destroy()
staff_menu()
def appointment_details_wind():
window.destroy()
Staff_customer()
window.mainloop()
# This creates the labels that indicate what the user should enter in each entry box.
pass_name_label = Label(window, text="Enter Username:", font=('arial', 13, 'bold'))
pass_name_label.place(x=30, y=90)
pass_label = Label(window, text="Enter Password:", font=('arial', 13, 'bold'))
pass_label.place(x=30, y=130)
# This function matches the user's credentials to the real ones and determines if the user can be logged in or
not.
def login_staff():
user_name = user_name_entry.get()
password = password_entry.get()
if user_name == "Staff" and password == "ST123": # This sets the username and the password to something
pre-defined.
result = tkinter.messagebox.showinfo("Success", "Login Successful")
if result:
window.destroy()
staff_menu()
return
# If the provided password and username are correct the option to close the page after log in is given
to the user.
else:
tkinter.messagebox.showerror("Failed",
"Login Failed. Please make sure that the username and password are both
present & correct.")
# If the username or password is wrong it will show this messagebox, it will also ask the user if they
want to close the longin window.
# This creates the button which kicks off the "login_staff" function.
CheckPass = Button(window, text="Check credentials", command=login_staff, cursor='hand2').place(x=150, y=200)
user_name_entry = Entry(window)
user_name_entry.place(x=200, y=90)
# This creates the labels that indicate what the user should enter in each entry box.
pass_name_label = Label(window, text="Enter Username:", font=('arial', 13, 'bold'))
pass_name_label.place(x=30, y=90)
pass_label = Label(window, text="Enter Password:", font=('arial', 13, 'bold'))
pass_label.place(x=30, y=130)
# This function matches the user's credentials to the real ones and determines if the user can be logged in or
not.
def login_admin():
user_name = user_name_entry.get()
password = password_entry.get()
if user_name == "Admin" and password == "AD123": # This sets the username and the password to something
pre-defined.
result = tkinter.messagebox.showinfo("Success", "Login Successful")
if result:
window.destroy()
admin_menu()
return
# If the password and username is correct then a success message will be displayed and the window will close
itself and the admin menu window will open.
else:
tkinter.messagebox.showerror("Failed",
"Login Failed. Please make sure that the username and password are both
present & correct.")
# If the username or password is wrong it will show this messagebox, it will also ask the user if they want
to destroy the longin window.
# ========================================================================================
Admin appointment details window:
def Admin_customer(): # This creates the appointment details window for the admin.
# This code creates the title, size and style of the window.
window = Tk()
window.title("Principal window")
window.geometry('1200x700')
titlelabel = Label(window, text="Appointment details", font=('arial', 20, 'bold'), bg="black", fg="white")
titlelabel.pack(side=TOP, fill=X)
bar_label = Label(window, text="", font=('arial', 15, 'bold'), bg="black", fg="white")
bar_label.pack(side=BOTTOM, fill=X)
# ================================Listbox creation for database displaying====================================
# This creates the Listbox for the main appointment details to be displayed in.
DataFrame = LabelFrame(window, bd=1, width=500, height=250, padx=20, pady=3, relief=RIDGE, bg="Ghost White",
font=('arial', 15, 'bold'), text="Appointment Details\n")
DataFrame.place(x=450, y=50)
Principallist = Listbox(DataFrame, width=58, height=16, font=('arial', 15, 'bold'))
Principallist.grid(row=0, column=0, padx=8)
# This function allows the user to clear all of the entry fields of the window on click
def clearData_Main():
principal_ID.delete(0, tkinter.END)
date_entry2.delete(0, tkinter.END)
service_name2.delete(0, tkinter.END)
ID_entry2.delete(0, tkinter.END)
# This function displays the database's contents in the window's listbox on click
def DisplayInfo_Main():
Principallist.delete(0, END)
for rows in priDatabase_BackEnd.viewMainData():
Principallist.insert(END, "%d,|%s| |%s| |%s| |%s| |%s| |%s| |£%s|" % rows)
# This function allows you to delete a record by typing in the number of the record o you want to delete into
the Patient Id box and pressing Delete.
def DeletePrincipalData():
if principal_ID.get():
priDatabase_BackEnd.deleteMainRec(principal_ID.get())
clearData_Main()
DisplayInfo_Main()
# This function allows the user to search for records by their date, Customer ID or service name.
def searchPrincipal():
Principallist.delete(0, END)
for rows in priDatabase_BackEnd.searchDataMain(date_entry2.get(), principal_ID.get(), service_name2.get()):
rows = list(rows)
for i in range(len(rows)):
rows[i] = str(rows[i]).strip()
Principallist.insert(END, rows, str(""))
# ===============================Navigation bar=============================================================
# Those are the functions which are to be assigned to the navigation bar's buttons
def mainmenu_wind():
window.destroy()
admin_menu()
def makeappointment_wind():
window.destroy()
Admin_appointment_wind()
def income_calc_wind():
window.destroy()
income_calculator()
def stock_management_wind():
window.destroy()
stock_management()
e_Reconstructing_Shampoo.set('0')
e_Htreatment.set('0')
e_conditioner.set('0')
e_moisturiser.set('0')
e_WhippedCream.set('0')
e_HydratingCream.set('0')
e_moisture_shampoo.set('0')
e_Moroccanoil.set('0')
e_HairDye.set('0')
e_Hair_food.set('0')
e_argan_oil.set('0')
e_curl_cream.set('0')
e_comb.set('0')
e_tesxturising_cream.set('0')
e_Essential_oil.set('0')
e_anti_hairloss.set('0')
e_grooming_cream.set('0')
e_Care_bundle.set('0')
# This sets all of the products to be cleared and disbled when the clear button is clicked.
textHgel.config(state=DISABLED)
textGspray.config(state=DISABLED)
textWax.config(state=DISABLED)
textpomade.config(state=DISABLED)
textclay.config(state=DISABLED)
textHregrowth.config(state=DISABLED)
textBeard_and_skin.config(state=DISABLED)
textBeard_oil.config(state=DISABLED)
textHlotion.config(state=DISABLED)
textReconstructing_Shampoo.config(state=DISABLED)
textHtreatment.config(state=DISABLED)
textconditioner.config(state=DISABLED)
textmoisturiser.config(state=DISABLED)
textWhippedCream.config(state=DISABLED)
textHydratingCream.config(state=DISABLED)
textmoisture_shampoo.config(state=DISABLED)
textMoroccanoil.config(state=DISABLED)
textHairDye.config(state=DISABLED)
textargan_oil.config(state=DISABLED)
textcurl_cream.config(state=DISABLED)
textHair_food.config(state=DISABLED)
textcomb.config(state=DISABLED)
texttesxturising_cream.config(state=DISABLED)
textEssential_oil.config(state=DISABLED)
textanti_hairloss.config(state=DISABLED)
textgrooming_cream.config(state=DISABLED)
textCare_bundle.config(state=DISABLED)
costofFemaleproductsvar.set('')
costofMaleproductsvar.set('')
costofEssentialsvar.set('')
subtotalvar.set('')
salesubtotalvar.set('')
Numofproductsvar.set('')
# This function generates the report which displays all sorts of information about the stock
# such as total number of products, purchase prices, selling prices, individual costs, etc...
def report():
if costofMaleproductsvar.get() != '' or costofFemaleproductsvar.get() != '' or costofEssentialsvar.get() !=
'':
textReport.delete(1.0, END)
x = random.randint(1000, 9999) # This generates the random report ID
Reportnumber = 'REPORT' + str(x) # This adds the word "Record" to the generated report ID.
date = time.strftime('%d/%m/%Y') # This generates the current date.
report_time = time.strftime('%H:%M') # This generates the current time.
# Shows that the owner has generated the report and the time at which it was generated.
textReport.insert(END, 'Report genrated by:\t\t Ceri McKim' + '\t' + "at" + '\t' + report_time + '\n')
textReport.insert(END, '=========================================\n')
textReport.insert(END,
'Report Ref:\t\t' + Reportnumber + '\t\t' + date + '\n') # Displays the generated
report ID into the report text box along with the date it was generated on.
textReport.insert(END, '=========================================\n')
textReport.insert(END, 'Products:\t\t Number of products\n')
textReport.insert(END, '=========================================\n')
# Those if statements show that if a certain product has been selected by the user then the number of
items
# will be retrieved to be displayed in the report alongisde the name of the selcted product for each
product in part in any combination.
if e_Hgel.get() != '0':
textReport.insert(END, f'Hair gel\t\t\t{int(e_Hgel.get()) * 1}\n\n')
if e_Gspray.get() != '0':
textReport.insert(END, f'Grooming spray\t\t\t{int(e_Gspray.get()) * 1}\n\n')
if e_pomade.get() != '0':
textReport.insert(END, f'Pomade\t\t\t{int(e_pomade.get()) * 1}\n\n')
if e_Hlotion.get() != '0':
textReport.insert(END, f'Hair lotion:\t\t\t{int(e_Hlotion.get()) * 1}\n\n')
if e_Wax.get() != '0':
textReport.insert(END, f'Wax:\t\t\t{int(e_Wax.get()) * 1}\n\n')
if e_Hregrowth.get() != '0':
textReport.insert(END, f'Hair regrowth oil:\t\t\t{int(e_Hregrowth.get()) * 1}\n\n')
if e_clay.get() != '0':
textReport.insert(END, f'Hair clay:\t\t\t{int(e_clay.get()) * 1}\n\n')
if e_Beard_and_skin.get() != '0':
textReport.insert(END, f'Beard & skin oil:\t\t\t{int(e_Beard_and_skin.get()) * 1}\n\n')
if e_Beard_oil.get() != '0':
textReport.insert(END, f'Beard oil:\t\t\t{int(e_Beard_oil.get()) * 1}\n\n')
if e_Reconstructing_Shampoo.get() != '0':
textReport.insert(END, f'Reconstructing shampoo:\t\t\t{int(e_Reconstructing_Shampoo.get()) *1}\n\n')
if e_Htreatment.get() != '0':
textReport.insert(END, f'Hair treatment:\t\t\t{int(e_Htreatment.get()) * 1}\n\n')
if e_conditioner.get() != '0':
textReport.insert(END, f'Conditioner:\t\t\t{int(e_conditioner.get()) * 1}\n\n')
if e_WhippedCream.get() != '0':
textReport.insert(END, f'Whipped cream:\t\t\t{int(e_WhippedCream.get()) * 1}\n\n')
if e_HydratingCream.get() != '0':
textReport.insert(END, f'Hydrating cream:\t\t\t{int(e_HydratingCream.get()) * 1}\n\n')
if e_moisturiser.get() != '0':
textReport.insert(END, f'Moisturiser:\t\t\t{int(e_moisturiser.get()) * 1}\n\n')
if e_moisture_shampoo.get() != '0':
textReport.insert(END, f'Moisturising shampoo:\t\t\t{int(e_moisture_shampoo.get()) * 1}\n\n')
if e_Moroccanoil.get() != '0':
textReport.insert(END, f'Moroccan oil:\t\t\t{int(e_Moroccanoil.get()) * 1}\n\n')
if e_HairDye.get() != '0':
textReport.insert(END, f'Hair dye:\t\t\t{int(e_HairDye.get()) * 1}\n\n')
if e_argan_oil.get() != '0':
textReport.insert(END, f'Argan oil:\t\t\t{int(e_argan_oil.get()) * 1}\n\n')
if e_curl_cream.get() != '0':
textReport.insert(END, f'Curl cream:\t\t\t{int(e_curl_cream.get()) * 1}\n\n')
if e_Hair_food.get() != '0':
textReport.insert(END, f'Hair food:\t\t\t{int(e_Hair_food.get()) * 1}\n\n')
if e_tesxturising_cream.get() != '0':
textReport.insert(END, f'Tesxturising cream:\t\t\t{int(e_tesxturising_cream.get()) * 1}\n\n')
if e_Essential_oil.get() != '0':
textReport.insert(END, f'Essential oil:\t\t\t{int(e_Essential_oil.get()) * 1}\n\n')
if e_anti_hairloss.get() != '0':
textReport.insert(END, f'Anti-hairloss oil:\t\t\t{int(e_anti_hairloss.get()) * 1}\n\n')
if e_grooming_cream.get() != '0':
textReport.insert(END, f'Grooming cream:\t\t\t{int(e_grooming_cream.get()) * 1}\n\n')
if e_Care_bundle.get() != '0':
textReport.insert(END, f'Care bundle:\t\t\t{int(e_Care_bundle.get()) * 1}\n\n')
if e_comb.get() != '0':
textReport.insert(END, f'Comb:\t\t\t{int(e_comb.get()) * 1}\n\n')
textReport.insert(END, '=========================================\n')
# This cost here does something similar to the code above except that it gets the number of each item
selected
# and multiplies them by their purchase price as to calculate how much was spent on each individual type
of item.
textReport.insert(END, 'Products:\t\t Cost of products\n') # Creates the Cost of products heading
textReport.insert(END, '=========================================\n')
# This code multiplies the selected products by their purchase price for the report.
if e_Hgel.get() != '0':
textReport.insert(END, f'Hair gel\t\t\t£{int(e_Hgel.get()) * 3.50}\n\n')
if e_Gspray.get() != '0':
textReport.insert(END, f'Grooming spray\t\t\t£{int(e_Gspray.get()) * 6.95}\n\n')
if e_pomade.get() != '0':
textReport.insert(END, f'Pomade\t\t\t£{int(e_pomade.get()) * 23.50}\n\n')
if e_Hlotion.get() != '0':
textReport.insert(END, f'Hair lotion:\t\t\t£{int(e_Hlotion.get()) * 6.30}\n\n')
if e_Wax.get() != '0':
textReport.insert(END, f'Wax:\t\t\t£{int(e_Wax.get()) * 10.95}\n\n')
if e_Hregrowth.get() != '0':
textReport.insert(END, f'Hair regrowth oil:\t\t\t£{int(e_Hregrowth.get()) * 16}\n\n')
if e_clay.get() != '0':
textReport.insert(END, f'Hair clay:\t\t\t£{int(e_clay.get()) * 11.10}\n\n')
if e_Beard_and_skin.get() != '0':
textReport.insert(END, f'Beard & skin oil:\t\t\t£{int(e_Beard_and_skin.get()) * 12.85}\n\n')
if e_Beard_oil.get() != '0':
textReport.insert(END, f'Beard oil:\t\t\t£{int(e_Beard_oil.get()) * 13.30}\n\n')
if e_Reconstructing_Shampoo.get() != '0':
textReport.insert(END,
f'Reconstructing shampoo:\t\t\t£{int(e_Reconstructing_Shampoo.get()) * 8.90}\n\n')
if e_Htreatment.get() != '0':
textReport.insert(END, f'Hair treatment:\t\t\t£{int(e_Htreatment.get()) * 5}\n\n')
if e_conditioner.get() != '0':
textReport.insert(END, f'Conditioner:\t\t\t£{int(e_conditioner.get()) * 15}\n\n')
if e_WhippedCream.get() != '0':
textReport.insert(END, f'Whipped cream:\t\t\t£{int(e_WhippedCream.get()) * 12}\n\n')
if e_HydratingCream.get() != '0':
textReport.insert(END, f'Hydrating cream:\t\t\t£{int(e_HydratingCream.get()) * 24.50}\n\n')
if e_moisturiser.get() != '0':
textReport.insert(END, f'Moisturiser:\t\t\t£{int(e_moisturiser.get()) * 5.50}\n\n')
if e_moisture_shampoo.get() != '0':
textReport.insert(END, f'Moisturising shampoo:\t\t\t£{int(e_moisture_shampoo.get()) * 6}\n\n')
if e_Moroccanoil.get() != '0':
textReport.insert(END, f'Moroccan oil:\t\t\t£{int(e_Moroccanoil.get()) * 11.25}\n\n')
if e_HairDye.get() != '0':
textReport.insert(END, f'Hair dye:\t\t\t£{int(e_HairDye.get()) * 5.50}\n\n')
if e_argan_oil.get() != '0':
textReport.insert(END, f'Argan oil:\t\t\t£{int(e_argan_oil.get()) * 9}\n\n')
if e_curl_cream.get() != '0':
textReport.insert(END, f'Curl cream:\t\t\t£{int(e_curl_cream.get()) * 4}\n\n')
if e_Hair_food.get() != '0':
textReport.insert(END, f'Hair food:\t\t\t£{int(e_Hair_food.get()) * 3.50}\n\n')
if e_tesxturising_cream.get() != '0':
textReport.insert(END, f'Tesxturising cream:\t\t\t£{int(e_tesxturising_cream.get()) * 5.50}\n\n')
if e_Essential_oil.get() != '0':
textReport.insert(END, f'Essential oil:\t\t\t£{int(e_Essential_oil.get()) * 4}\n\n')
if e_anti_hairloss.get() != '0':
textReport.insert(END, f'Anti-hairloss oil:\t\t\t£{int(e_anti_hairloss.get()) * 20}\n\n')
if e_grooming_cream.get() != '0':
textReport.insert(END, f'Grooming cream:\t\t\t£{int(e_grooming_cream.get()) * 10.25}\n\n')
if e_Care_bundle.get() != '0':
textReport.insert(END, f'Care bundle:\t\t\t£{int(e_Care_bundle.get()) * 29}\n\n')
if e_comb.get() != '0':
textReport.insert(END, f'Comb:\t\t\t£{int(e_comb.get()) * 9}\n\n')
textReport.insert(END, '=========================================\n')
if costofMaleproductsvar.get() != '£ 0':
textReport.insert(END, f'Cost Of Male Products\t\t\t£{priceofProductsM}\n\n')
if costofFemaleproductsvar.get() != '£ 0':
textReport.insert(END, f'Cost Of Female Products\t\t\t£{priceofProductsF}\n\n')
if costofEssentialsvar.get() != '£ 0':
textReport.insert(END, f'Cost Of Essentials\t\t\t£{priceofEssentials}\n\n')
else:
messagebox.showerror('Error', 'No products selected, please select some products and click "Total"
first!')
# This function allows for the calculation of all of the details that will be mentioned in the report text
field.
def totalnums():
global priceofProductsM, priceofProductsF, priceofEssentials, subtotalofItems, Numofproduct, priceofSale,
salesubtotalofItems
# This states that if any of the products have been selected then the following calculations must be
performed as "!=" means not equal to,
# if else then the error message below must be displayed
if var1.get() != 0 or var2.get() != 0 or var3.get() != 0 or var4.get() != 0 or var5.get() != 0 or \
var6.get() != 0 or var7.get() != 0 or var8.get() != 0 or var9.get() != 0 or var10.get() != 0 or \
var11.get() != 0 or var12.get() != 0 or var13.get() != 0 or var14.get() != 0 or var15.get() != 0 or
\
var16.get() != 0 or var17.get() != 0 or var18.get() != 0 or var19.get() != 0 or var20.get() != 0 or
\
var21.get() != 0 or var22.get() != 0 or var23.get() != 0 or var24.get() != 0 or var25.get() != 0 or
\
var26.get() != 0 or var27.get() != 0:
# This defines all of the item entries as intgers, therefore other characters will not be accepted and
if any other characters are dicovered
# the system will issue a specific error message for the entry fields with invalid characters and not
perform any calculations until all inputs are valid.
try:
item1 = int(e_Hgel.get())
except ValueError:
tkinter.messagebox.showerror("Error",
"Invalid entry in the hair gel entry box, please check if there are any
non-integer characters!")
return
try:
item2 = int(e_Gspray.get())
except ValueError:
tkinter.messagebox.showerror("Error",
"Invalid entry in the grooming spray entry box, please check if there
are any non-integer characters!")
return
try:
item3 = int(e_pomade.get())
except ValueError:
tkinter.messagebox.showerror("Error",
"Invalid entry in the hair wax entry box, please check if there are any
non-integer characters!")
return
try:
item4 = int(e_Wax.get())
except ValueError:
tkinter.messagebox.showerror("Error",
"Invalid entry in the hair wax entry box, please check if there are any
non-integer characters!")
return
try:
item5 = int(e_clay.get())
except ValueError:
tkinter.messagebox.showerror("Error",
"Invalid entry in the hair clay entry box, please check if there are
any non-integer characters!")
return
try:
item6 = int(e_Hlotion.get())
except ValueError:
tkinter.messagebox.showerror("Error",
"Invalid entry in the hair lotion entry box, please check if there are
any non-integer characters!")
return
try:
item7 = int(e_Beard_oil.get())
except ValueError:
tkinter.messagebox.showerror("Error",
"Invalid entry in the beard oil entry box, please check if there are
any non-integer characters!")
return
try:
item8 = int(e_Hregrowth.get())
except ValueError:
tkinter.messagebox.showerror("Error",
"Invalid entry in the hair regrowth oil entry box, please check if
there are any non-integer characters!")
return
try:
item9 = int(e_Beard_and_skin.get())
except ValueError:
tkinter.messagebox.showerror("Error",
"Invalid entry in the beard & skin oil entry box, please check if there
are any non-integer characters!")
return
try:
item10 = int(e_Reconstructing_Shampoo.get())
except ValueError:
tkinter.messagebox.showerror("Error",
"Invalid entry in the beard & skin oil entry box, please check if there
are any non-integer characters!")
return
try:
item11 = int(e_Htreatment.get())
except ValueError:
tkinter.messagebox.showerror("Error",
"Invalid entry in the hair treatment entry box, please check if there
are any non-integer characters!")
return
try:
item12 = int(e_conditioner.get())
except ValueError:
tkinter.messagebox.showerror("Error",
"Invalid entry in the conditioner entry box, please check if there are
any non-integer characters!")
return
try:
item13 = int(e_WhippedCream.get())
except ValueError:
tkinter.messagebox.showerror("Error",
"Invalid entry in the Whipped cream entry box, please check if there
are any non-integer characters!")
return
try:
item14 = int(e_HydratingCream.get())
except ValueError:
tkinter.messagebox.showerror("Error",
"Invalid entry in the hydrating cream entry box, please check if there
are any non-integer characters!")
return
try:
item15 = int(e_moisturiser.get())
except ValueError:
tkinter.messagebox.showerror("Error",
"Invalid entry in the moisturiser entry box, please check if there are
any non-integer characters!")
return
try:
item16 = int(e_moisture_shampoo.get())
except ValueError:
tkinter.messagebox.showerror("Error",
"Invalid entry in the moisturiser shampoo entry box, please check if
there are any non-integer characters!")
return
try:
item17 = int(e_Moroccanoil.get())
except ValueError:
tkinter.messagebox.showerror("Error",
"Invalid entry in the moroccan oil entry box, please check if there are
any non-integer characters!")
return
try:
item18 = int(e_HairDye.get())
except ValueError:
tkinter.messagebox.showerror("Error",
"Invalid entry in the hair dye entry box, please check if there are any
non-integer characters!")
return
try:
item19 = int(e_argan_oil.get())
except ValueError:
tkinter.messagebox.showerror("Error",
"Invalid entry in the argan oil entry box, please check if there are
any non-integer characters!")
return
try:
item20 = int(e_curl_cream.get())
except ValueError:
tkinter.messagebox.showerror("Error",
"Invalid entry in the curl cream entry box, please check if there are
any non-integer characters!")
return
try:
item21 = int(e_Hair_food.get())
except ValueError:
tkinter.messagebox.showerror("Error",
"Invalid entry in the hair food entry box, please check if there are
any non-integer characters!")
return
try:
item22 = int(e_comb.get())
except ValueError:
tkinter.messagebox.showerror("Error",
"Invalid entry in the comb entry box, please check if there are any
non-integer characters!")
return
try:
item23 = int(e_tesxturising_cream.get())
except ValueError:
tkinter.messagebox.showerror("Error",
"Invalid entry in the texturising cream entry box, please check if
there are any non-integer characters!")
return
try:
item24 = int(e_Essential_oil.get())
except ValueError:
tkinter.messagebox.showerror("Error",
"Invalid entry in the essential oil entry box, please check if there
are any non-integer characters!")
return
try:
item25 = int(e_anti_hairloss.get())
except ValueError:
tkinter.messagebox.showerror("Error",
"Invalid entry in anti-hairloss oil entry box, please check if there
are any non-integer characters!")
return
try:
item26 = int(e_grooming_cream.get())
except ValueError:
tkinter.messagebox.showerror("Error",
"Invalid entry in the grooming cream entry box, please check if there
are any non-integer characters!")
return
try:
item27 = int(e_Care_bundle.get())
except ValueError:
tkinter.messagebox.showerror("Error",
"Invalid entry in the care bundle entry box, please check if there are
any non-integer characters!")
return
# This calculates the total purchase price of the products in the male section.
priceofProductsM = (item1 * 3.50) + (item2 * 6.95) + (item3 * 23.50) + (item4 * 10.95) + (item5 * 11.10)
+ (
item6 * 6.30) + (item7 * 13.30) \
+ (item8 * 16) + (item9 * 12.85)
# This calculates the total purchase price of the products in the female section.
priceofProductsF = (item10 * 8.90) + (item11 * 5) + (item12 * 15) + (item13 * 12) + (item14 * 24.50) + (
item15 * 5.50) \
+ (item16 * 6) + (item17 * 11.25) + (item18 * 5.50)
# This calculates the total purchase price of the products in the essentials section.
priceofEssentials = (item19 * 9) + (item20 * 4) + (item21 * 3.50) + (item22 * 9) + (item23 * 5.50) + (
item24 * 4) \
+ (item25 * 20) + (item26 * 10.50) + (item27 * 29)
# This calculates the total selling price of all of the selected products.
priceofSale = (item1 * 6.50) + (item2 * 12) + (item3 * 30) + (item4 * 16) + (item5 * 17) + (
item6 * 9.50) + (item7 * 20) \
+ (item8 * 24) + (item9 * 19) + (item10 * 13.40) + (item11 * 8) + (item12 * 22.5) + (
item13 * 18) + (item14 * 37) + (item15 * 8) \
+ (item16 * 9) + (item17 * 17) + (item18 * 8) + (item19 * 14) + (item20 * 7) + (
item21 * 6.50) + (item22 * 13.50) + (item23 * 8) + (item24 * 7) \
+ (item25 * 31) + (item26 * 15.25) + (item27 * 40)
# This assigns the variables which are assigned to the calculations above to string variables which are
# assigned to the vital information display screens and are displayed in the report if any of the
products from their section have been selected to be in stock.
costofMaleproductsvar.set('£ ' + str(priceofProductsM))
costofFemaleproductsvar.set('£ ' + str(priceofProductsF))
costofEssentialsvar.set('£ ' + str(priceofEssentials))
salesubtotalofItems = priceofSale
salesubtotalvar.set('£ ' + str(salesubtotalofItems))
Numofproduct = Totalnumber
Numofproductsvar.set(str(Numofproduct))
# If no products have been selected and the user clicks total, this error message will be shown to them.
else:
messagebox.showerror('No products are selected', 'Please select some products first!')
# If the user clicks any of the checkboxes which are represented with the "var" variables then their assigned
entry field will be activated for use.
def Hgel():
if var1.get() == 1: # If there is a click on this checkbox then the user will be able to make inputs into
it's entry field.
textHgel.config(state=NORMAL)
textHgel.delete(0, END)
textHgel.focus()
elif var1.get() == 0: # If there is no click from the user on this certain checkbox then it will remain set
to "0".
textHgel.config(state=DISABLED)
e_Hgel.set('0')
def Gspray():
if var2.get() == 1: # If there is a click on this checkbox then the user will be able to make inputs into
it's entry field.
textGspray.config(state=NORMAL)
textGspray.delete(0, END)
textGspray.focus()
elif var2.get() == 0: # If there is no click from the user on this certain checkbox then it will remain set
to "0".
textGspray.config(state=DISABLED)
e_Gspray.set('0')
def pomade():
if var3.get() == 1: # If there is a click on this checkbox then the user will be able to make inputs into
it's entry field.
textpomade.config(state=NORMAL)
textpomade.delete(0, END)
textpomade.focus()
elif var3.get() == 0: # If there is no click from the user on this certain checkbox then it will remain set
to "0".
textpomade.config(state=DISABLED)
e_pomade.set('0')
def Wax():
if var4.get() == 1: # If there is a click on this checkbox then the user will be able to make inputs into
it's entry field.
textWax.config(state=NORMAL)
textWax.focus()
textWax.delete(0, END)
elif var4.get() == 0: # If there is no click from the user on this certain checkbox then it will remain set
to "0".
textWax.config(state=DISABLED)
e_Wax.set('0')
def clay():
if var5.get() == 1: # If there is a click on this checkbox then the user will be able to make inputs into
it's entry field.
textclay.config(state=NORMAL)
textclay.focus()
textclay.delete(0, END)
elif var5.get() == 0: # If there is no click from the user on this certain checkbox then it will remain set
to "0".
textclay.config(state=DISABLED)
e_clay.set('0')
def Hlotion():
if var6.get() == 1: # If there is a click on this checkbox then the user will be able to make inputs into
it's entry field.
textHlotion.config(state=NORMAL)
textHlotion.focus()
textHlotion.delete(0, END)
elif var6.get() == 0: # If there is no click from the user on this certain checkbox then it will remain set
to "0".
textHlotion.config(state=DISABLED)
e_Hlotion.set('0')
def Beard_oil():
if var7.get() == 1: # If there is a click on this checkbox then the user will be able to make inputs into
it's entry field.
textBeard_oil.config(state=NORMAL)
textBeard_oil.focus()
textBeard_oil.delete(0, END)
elif var7.get() == 0:
textBeard_oil.config(state=DISABLED)
e_Beard_oil.set('0')
def Hregrowth():
if var8.get() == 1: # If there is a click on this checkbox then the user will be able to make inputs into
it's entry field.
textHregrowth.config(state=NORMAL)
textHregrowth.focus()
textHregrowth.delete(0, END)
elif var8.get() == 0: # If there is no click from the user on this certain checkbox then it will remain set
to "0".
textHregrowth.config(state=DISABLED)
e_Hregrowth.set('0')
def Beard_and_skin():
if var9.get() == 1: # If there is a click on this checkbox then the user will be able to make inputs into
it's entry field.
textBeard_and_skin.config(state=NORMAL)
textBeard_and_skin.focus()
textBeard_and_skin.delete(0, END)
elif var9.get() == 0: # If there is no click from the user on this certain checkbox then it will remain set
to "0".
textBeard_and_skin.config(state=DISABLED)
e_Beard_and_skin.set('0')
def Reconstructing_Shampoo():
if var10.get() == 1: # If there is a click on this checkbox then the user will be able to make inputs into
it's entry field.
textReconstructing_Shampoo.config(state=NORMAL)
textReconstructing_Shampoo.focus()
textReconstructing_Shampoo.delete(0, END)
elif var10.get() == 0: # If there is no click from the user on this certain checkbox then it will remain
set to "0".
textReconstructing_Shampoo.config(state=DISABLED)
e_Reconstructing_Shampoo.set('0')
def Htreatment():
if var11.get() == 1: # If there is a click on this checkbox then the user will be able to make inputs into
it's entry field.
textHtreatment.config(state=NORMAL)
textHtreatment.focus()
textHtreatment.delete(0, END)
elif var11.get() == 0: # If there is no click from the user on this certain checkbox then it will remain
set to "0".
textHtreatment.config(state=DISABLED)
e_Htreatment.set('0')
def conditioner():
if var12.get() == 1: # If there is a click on this checkbox then the user will be able to make inputs into
it's entry field.
textconditioner.config(state=NORMAL)
textconditioner.focus()
textconditioner.delete(0, END)
elif var12.get() == 0: # If there is no click from the user on this certain checkbox then it will remain
set to "0".
textconditioner.config(state=DISABLED)
e_conditioner.set('0')
def WhippedCream():
if var13.get() == 1: # If there is a click on this checkbox then the user will be able to make inputs into
it's entry field.
textWhippedCream.config(state=NORMAL)
textWhippedCream.focus()
textWhippedCream.delete(0, END)
elif var13.get() == 0: # If there is no click from the user on this certain checkbox then it will remain
set to "0".
textWhippedCream.config(state=DISABLED)
e_WhippedCream.set('0')
def HydratingCream():
if var14.get() == 1: # If there is a click on this checkbox then the user will be able to make inputs into
it's entry field.
textHydratingCream.config(state=NORMAL)
textHydratingCream.focus()
textHydratingCream.delete(0, END)
elif var14.get() == 0: # If there is no click from the user on this certain checkbox then it will remain
set to "0".
textHydratingCream.config(state=DISABLED)
e_HydratingCream.set('0')
def moisturiser():
if var15.get() == 1: # If there is a click on this checkbox then the user will be able to make inputs into
it's entry field.
textmoisturiser.config(state=NORMAL)
textmoisturiser.focus()
textmoisturiser.delete(0, END)
elif var15.get() == 0: # If there is no click from the user on this certain checkbox then it will remain
set to "0".
textmoisturiser.config(state=DISABLED)
e_moisturiser.set('0')
def moisture_shampoo():
if var16.get() == 1: # If there is a click on this checkbox then the user will be able to make inputs into
it's entry field.
textmoisture_shampoo.config(state=NORMAL)
textmoisture_shampoo.focus()
textmoisture_shampoo.delete(0, END)
elif var16.get() == 0: # If there is no click from the user on this certain checkbox then it will remain
set to "0".
textmoisture_shampoo.config(state=DISABLED)
e_moisture_shampoo.set('0')
def Moroccanoil():
if var17.get() == 1: # If there is a click on this checkbox then the user will be able to make inputs into
it's entry field.
textMoroccanoil.config(state=NORMAL)
textMoroccanoil.focus()
textMoroccanoil.delete(0, END)
elif var17.get() == 0: # If there is no click from the user on this certain checkbox then it will remain
set to "0".
textMoroccanoil.config(state=DISABLED)
e_Moroccanoil.set('0')
def HairDye():
if var18.get() == 1: # If there is a click on this checkbox then the user will be able to make inputs into
it's entry field.
textHairDye.config(state=NORMAL)
textHairDye.focus()
textHairDye.delete(0, END)
elif var18.get() == 0: # If there is no click from the user on this certain checkbox then it will remain
set to "0".
textHairDye.config(state=DISABLED)
e_HairDye.set('0')
def argan_oil():
if var19.get() == 1: # If there is a click on this checkbox then the user will be able to make inputs into
it's entry field.
textargan_oil.config(state=NORMAL)
textargan_oil.focus()
textargan_oil.delete(0, END)
elif var19.get() == 0: # If there is no click from the user on this certain checkbox then it will remain
set to "0".
textargan_oil.config(state=DISABLED)
e_argan_oil.set('0')
def curl_cream():
if var20.get() == 1: # If there is a click on this checkbox then the user will be able to make inputs into
it's entry field.
textcurl_cream.config(state=NORMAL)
textcurl_cream.focus()
textcurl_cream.delete(0, END)
elif var20.get() == 0: # If there is no click from the user on this certain checkbox then it will remain
set to "0".
textcurl_cream.config(state=DISABLED)
e_curl_cream.set('0')
def Hair_food():
if var21.get() == 1: # If there is a click on this checkbox then the user will be able to make inputs into
it's entry field.
textHair_food.config(state=NORMAL)
textHair_food.focus()
textHair_food.delete(0, END)
elif var21.get() == 0: # If there is no click from the user on this certain checkbox then it will remain
set to "0".
textHair_food.config(state=DISABLED)
e_Hair_food.set('0')
def comb():
if var22.get() == 1: # If there is a click on this checkbox then the user will be able to make inputs into
it's entry field.
textcomb.config(state=NORMAL)
textcomb.focus()
textcomb.delete(0, END)
elif var22.get() == 0: # If there is no click from the user on this certain checkbox then it will remain
set to "0".
textcomb.config(state=DISABLED)
e_comb.set('0')
def tesxturising_cream():
if var23.get() == 1: # If there is a click on this checkbox then the user will be able to make inputs into
it's entry field.
texttesxturising_cream.config(state=NORMAL)
texttesxturising_cream.focus()
texttesxturising_cream.delete(0, END)
elif var23.get() == 0: # If there is no click from the user on this certain checkbox then it will remain
set to "0".
texttesxturising_cream.config(state=DISABLED)
e_tesxturising_cream.set('0')
def Essential_oil():
if var24.get() == 1: # If there is a click on this checkbox then the user will be able to make inputs into
it's entry field.
textEssential_oil.config(state=NORMAL)
textEssential_oil.focus()
textEssential_oil.delete(0, END)
elif var24.get() == 0: # If there is no click from the user on this certain checkbox then it will remain
set to "0".
textEssential_oil.config(state=DISABLED)
e_Essential_oil.set('0')
def anti_hairloss():
if var25.get() == 1: # If there is a click on this checkbox then the user will be able to make inputs into
it's entry field.
textanti_hairloss.config(state=NORMAL)
textanti_hairloss.focus()
textanti_hairloss.delete(0, END)
elif var25.get() == 0: # If there is no click from the user on this certain checkbox then it will remain
set to "0".
textanti_hairloss.config(state=DISABLED)
e_anti_hairloss.set('0')
def grooming_cream():
if var26.get() == 1: # If there is a click on this checkbox then the user will be able to make inputs into
it's entry field.
textgrooming_cream.config(state=NORMAL)
textgrooming_cream.focus()
textgrooming_cream.delete(0, END)
elif var26.get() == 0: # If there is no click from the user on this certain checkbox then it will remain
set to "0".
textgrooming_cream.config(state=DISABLED)
e_grooming_cream.set('0')
def Care_bundle():
if var27.get() == 1: # If there is a click on this checkbox then the user will be able to make inputs into
it's entry field.
textCare_bundle.config(state=NORMAL)
textCare_bundle.focus()
textCare_bundle.delete(0, END)
elif var27.get() == 0: # If there is no click from the user on this certain checkbox then it will remain
set to "0".
textCare_bundle.config(state=DISABLED)
e_Care_bundle.set('0')
window.geometry('1765x690+0+0')
window.resizable(0, 0)
window.config()
labelTitle = Label(topFrame, text='Stock management system', font=('arial', 30, 'bold'), fg='white', bd=9,
bg='black', width=71)
labelTitle.grid(row=0, column=0)
# =============================Frames of the stock management window=================================
# This code creates the frames in which the checkboxes, information entry boxes and the report will be placed
into.
productFrame = Frame(window, bd=10, relief=RIDGE)
productFrame.pack(side=LEFT)
var1 = IntVar()
var2 = IntVar()
var3 = IntVar()
var4 = IntVar()
var5 = IntVar()
var6 = IntVar()
var7 = IntVar()
var8 = IntVar()
var9 = IntVar()
var10 = IntVar()
var11 = IntVar()
var12 = IntVar()
var13 = IntVar()
var14 = IntVar()
var15 = IntVar()
var16 = IntVar()
var17 = IntVar()
var18 = IntVar()
var19 = IntVar()
var20 = IntVar()
var21 = IntVar()
var22 = IntVar()
var23 = IntVar()
var24 = IntVar()
var25 = IntVar()
var26 = IntVar()
var27 = IntVar()
# Those variables are defined as string variables because they will be used for the entry fields of the stock
selection frames, for each product.
# As they will also need to be referenced so that their contents may be used by the report in order to calculate
numbers and prices of stock.
e_Hgel = StringVar()
e_Gspray = StringVar()
e_Wax = StringVar()
e_Hlotion = StringVar()
e_pomade = StringVar()
e_Beard_oil = StringVar()
e_clay = StringVar()
e_Beard_and_skin = StringVar()
e_Hregrowth = StringVar()
e_Reconstructing_Shampoo = StringVar()
e_Htreatment = StringVar()
e_conditioner = StringVar()
e_WhippedCream = StringVar()
e_moisturiser = StringVar()
e_HydratingCream = StringVar()
e_moisture_shampoo = StringVar()
e_Moroccanoil = StringVar()
e_HairDye = StringVar()
e_Hair_food = StringVar()
e_argan_oil = StringVar()
e_curl_cream = StringVar()
e_comb = StringVar()
e_tesxturising_cream = StringVar()
e_Essential_oil = StringVar()
e_anti_hairloss = StringVar()
e_grooming_cream = StringVar()
e_Care_bundle = StringVar()
# Those variables will be used for the primary display entry boxes which will just show the user some basic
information which will be put in the stock record as well.
costofMaleproductsvar = StringVar()
costofFemaleproductsvar = StringVar()
costofEssentialsvar = StringVar()
subtotalvar = StringVar()
salesubtotalvar = StringVar()
Numofproductsvar = StringVar()
e_Reconstructing_Shampoo.set('0')
e_Htreatment.set('0')
e_conditioner.set('0')
e_moisturiser.set('0')
e_WhippedCream.set('0')
e_HydratingCream.set('0')
e_moisture_shampoo.set('0')
e_Moroccanoil.set('0')
e_HairDye.set('0')
e_Hair_food.set('0')
e_argan_oil.set('0')
e_curl_cream.set('0')
e_comb.set('0')
e_tesxturising_cream.set('0')
e_Essential_oil.set('0')
e_anti_hairloss.set('0')
e_grooming_cream.set('0')
e_Care_bundle.set('0')
Wax = Checkbutton(maleFrame, text='Hair wax', font=('arial', 18, 'bold'), onvalue=1, offvalue=0, variable=var4
, command=Wax)
Wax.grid(row=3, column=0, sticky=W)
clay = Checkbutton(maleFrame, text='Hair clay', font=('arial', 18, 'bold'), onvalue=1, offvalue=0, variable=var5
, command=clay)
clay.grid(row=4, column=0, sticky=W)
Hregrowth = Checkbutton(maleFrame, text='Hair regrowth oil', font=('arial', 18, 'bold'), onvalue=1, offvalue=0,
variable=var8
, command=Hregrowth)
Hregrowth.grid(row=7, column=0, sticky=W)
Beard_and_skin = Checkbutton(maleFrame, text='Beard & skin oil', font=('arial', 18, 'bold'), onvalue=1,
offvalue=0,
variable=var9
, command=Beard_and_skin)
Beard_and_skin.grid(row=8, column=0, sticky=W)
labelSubTotal = Label(costFrame, text='Total stock purchase price:', font=('arial', 16, 'bold'), fg='black')
labelSubTotal.grid(row=0, column=2)
# ===================================================Stock management
buttons=========================================================================
# This code creates the for buttons responsible for the calculation of properties, report generation, saving the
report and clearing the window.
# This button calculates the properties of the inputted stock when clicked.
btn_Total = Button(buttonFrame, text='Total', font=('arial', 14, 'bold'), fg='white', bg='firebrick', bd=3,
padx=5,
command=totalnums)
btn_Total.grid(row=0, column=0)
# This button clears the window of all of the user's inputs on click.
btn_Clear = Button(buttonFrame, text='Clear', font=('arial', 14, 'bold'), fg='white', bg='firebrick', bd=3,
padx=5,
command=clear)
btn_Clear.grid(row=0, column=4)
# =================================Report textarea===========================================
def mainmenu_wind():
window.destroy()
admin_menu()
def makeappointment_wind():
window.destroy()
Admin_appointment_wind()
def income_calculator_wind():
window.destroy()
income_calculator()
def appointment_details_wind():
window.destroy()
Admin_customer()
# This creates the frame in which the navigation bar buttons will be placed.
btnframe = Frame(window, bd=4, relief=RIDGE, pady=10)
btnframe.place(x=1200, y=625)
# This creates the navigation buttons themselves.
btn_menu = tk.Button(btnframe, text='Main menu', command=mainmenu_wind, cursor='hand2')
btn_menu.grid(row=0, column=0)
window.mainloop()
# This defines the variables to be later used in this window as String and Integer variables.
content = StringVar()
Date = StringVar()
Profit = StringVar()
GrossIncome = StringVar()
Description = StringVar()
Result = StringVar()
NI_tax = IntVar()
Income_tax = IntVar()
RecordID = IntVar()
Tax = IntVar()
# This function is to be used to clear the profit entry window and the tax entry windows as well on demand
when it's button is clicked.
def btnClearDisplay2():
Profit.set("")
NI_tax.set("")
Income_tax.set("")
# This performs the calculations on the inputted numbers in the gross income entry field
def btnEqualInput(): # Defines the function as "btnEqualInput".
if Result.get() == "":
tkinter.messagebox.showerror("Error", "Please make an entry before performing the calculation.")
else:
try:
sumup = str(eval(Result.get()))
Result.set(sumup) # Puts what is stored in the variable "sumup" in the "Result" text box.
except ZeroDivisionError:
tkinter.messagebox.showerror("Division by zero error",
"You cannot divide by zero, plaese check the calculation.")
except SyntaxError:
tkinter.messagebox.showerror("Syntax Error", "Syntax error, please rectify your entry.")
# This function allows for the lambda command to be assigned to the buttons that will be part of the profit
calculator and give each button a respective value.
def btnClickProfit(numbers):
current_text = Profit.get()
numbers = str(numbers)
Profit.set(current_text + numbers)
# This performs the calculations on the inputted numbers in the profit income entry field
def btnEqualProfit():
if Profit.get() == "":
tkinter.messagebox.showerror("Error", "Please make an entry before performing the calculation.")
else:
try:
sumup = str(eval(Profit.get()))
Profit.set(sumup) # Puts what is stored in the variable "sumup" in the "Result" text box.
except ZeroDivisionError:
tkinter.messagebox.showerror("Division by zero error",
"You cannot divide by zero, plaese check the calculation.")
except SyntaxError:
tkinter.messagebox.showerror("Syntax Error", "Syntax error, please rectify your entry.")
# This function allows for the user to clear the data from the database related entry boxes
def clearData_Calculator():
calc_ID.delete(0, tkinter.END)
date_entryC.delete(0, tkinter.END)
# This creates the frame in which the validation messages will be displayed.
CalculatorValidationFrame = LabelFrame(window, bd=5)
CalculatorValidationFrame.place(x=1000, y=580, width=600)
def AddInfo_Calc():
# This states that if there are any ematy required entry fields an error message stating this will be
displayed
# and nothing will be added to the database.
if date_entryC.get() == '' or gross_income.get() == '' or profit_income.get() == '':
tkinter.messagebox.showerror("Error", "Please fill in all of the entry fields!")
# If all of the entry fields have been filled in the validation process may begin.
else:
try:
int(float(
gross_income.get())) # This just states that the gross income must be an integer and it can
also have a decimal.
answer1.config(text="That is a valid gross income!")
# if the profit entry is not an integer the following message is displayed.
except ValueError:
answer1.config(
text="That is not a valid gross income because it contains other characters that are not
integers!")
return
try:
int(float(
profit_income.get())) # This just states that the profit income must be an integer and it can
also have a decimal.
answer2.config(text="That is a valid profit!")
# if the profit entry is not an integer the following message is displayed.
except ValueError:
answer2.config(
text="That is not a valid profit because it contains other characters that are not integers!")
return
# If the user's inputs passes the validation checks then the data will be added to the database with a
success message.
tkinter.messagebox.showinfo("Data Saved", "Data saved successfully!")
calcDatabase_Backend.addCalcRec(date_entryC.get(), gross_income.get(), profit_income.get())
Calculatorlist.delete(0, END)
Calculatorlist.insert(END, (date_entryC.get(), gross_income.get(), profit_income.get()))
# This function allows you to delete a record by typing in the id number of the record into the id entry
box and click delete.
def DeleteCalcData():
if calc_ID.get():
calcDatabase_Backend.deleteCalcRec(calc_ID.get())
clearData_Calculator()
DisplayInfo_Calc()
# This function allows for the lambda command to be assigned to the buttons that will be part of the gross
income calculator
# and give each button a respective value.
def btnClick(numbers):
current_text = Result.get()
numbers = str(numbers)
Result.set(current_text + numbers)
# This function calculates the income tax and national per annum for any input as well as the profit.
def btnincometax_and_NI():
if Profit.get() == "":
tkinter.messagebox.showerror("Profit entry box is empty",
"Please fill in the profit entry box before performing a calculation!")
else:
try:
# I have called the input in the Profit income window as "percent".
percent = Profit.get()
percent = int(float(
percent)) # Had to add the float() for the calculator to be able to perform the arithmetic
function on decimal numbers too.
# This calculates the taxes for any input under £6515
# The percent-1000 signifies that before the inputted number has it's profit calculated based on the
type of taxation selcted £1000 are subtracted first
# as this sum of money from any income does not get taxed as law. E.g: £7500 would still be tax free
because 7500-1000=6500 & 6500<6515.
if 0 < (percent - 1000) < 6515:
tkinter.messagebox.showinfo("No tax", "There is no tax on this income.")
Profit.set(percent)
# This sets the tax dispay entry fields to 0 regardless as no taxes are applied to any yearly
income under £6515.
try:
NI_Entry.insert(END, "0.0")
Income_tax_entry.insert(END, "0.0")
except IOError:
pass
# This calculates the income and national insurance taxes for any input between £6515 and £9567
according to the UK government guidelines
# and it gives the user some insight in what their tax brackets they are in.
if 6516 < (percent - 1000) < 9567:
tkinter.messagebox.showinfo("No Income Tax & Class 2 National insurance", \
"There is no tax on this income as any income under £12570 does not
get taxed but there is class 2 national insurance that must be paid.")
Profit.set(percent - 159)
try:
NI_Entry.insert(END, "159")
Income_tax_entry.insert(END, "0.0")
except IOError:
pass
# This calculates the income and national insurance taxes for any input between £9568 and £12570
according to the UK government guidelines
# and it gives the user some insight in what their tax brackets they are in.
if 9568 < (percent - 1000) < 12570:
tkinter.messagebox.showinfo("Income Tax & Class 4 and Class 2", \
"There is no income tax on this income as any income under £12570
does not get income tax but there is class 2 and class 4 national insurance that must be paid.")
Profit.set(percent - ((percent - 1000 - 9568) / 100 * 9) - (159))
# This calculates the taxes to be paid if the entry is between £9568 and £12570.
try:
NI_Entry.insert(END, round(((percent - 1000 - 9568) / 100 * 9) + (159), 2))
Income_tax_entry.insert(END, "0.0")
except IOError:
pass
# This calculates the income and national insurance taxes for any input over £150001 according to
the UK government guidelines
# and it gives the user some insight in what their tax brackets they are in.
if (percent - 1000) > 150001:
tkinter.messagebox.showinfo("Income tax & Class 4 and Class 2 national insurance", \
"You must pay Class 4 and Class 2 national insurance & Income tax
for this financial year")
Profit.set(percent - ((percent - 1000 - 150000) / 100 * 45) - (112300 / 10 * 4) - (37700 / 5) -
(
(percent - 1000 - 50270) / 100 * 2) - (40702 / 100 * 9) - (159))
# This calculates the taxes to be paid if the entry is over £150001.
try:
NI_Entry.insert(END, round(((percent - 1000 - 50270) / 100 * 2) + (40702 / 100 * 9) + (159),
2))
Income_tax_entry.insert(END,
round(((percent - 150000) / 100 * 45) + (112300 / 10 * 4) + (37700 /
5),
2))
except IOError:
pass
# This calculates the income and national insurance taxes for any input between £12571 and £50270
according to the UK government guidelines
# and it gives the user some insight in what their tax brackets they are in.
if 12571 < (percent - 1000) < 50270:
tkinter.messagebox.showinfo("Income tax & Class 4 and Class 2 national insurance", \
"You must pay Class 4 and Class 2 national insurance & Income tax
for this financial year")
Profit.set(
percent - ((percent - 1000 - 12570) / 10 * 2) - ((percent - 1000 - 9568) / 100 * 9) - (159))
# This calculates the taxes to be paid if the entry is between £9568 and £12570.
try:
NI_Entry.insert(END, round(((percent - 1000 - 9568) / 100 * 9) + (159), 2))
Income_tax_entry.insert(END, round(((percent - 1000 - 12570) / 10 * 2), 2))
except IOError:
pass
# This calculates the income and national insurance taxes for any input between £50271 and £150000
according to the UK government guidelines
# and it gives the user some insight in what their tax brackets they are in.
if 50271 < (percent - 1000) < 150000:
tkinter.messagebox.showinfo("Income tax & Class 4 and Class 2 national insurance", \
"You must pay Class 4 and Class 2 national insurance & Income tax
for this financial year")
Profit.set(percent - ((percent - 1000 - 50270) / 10 * 4) - (37700 / 5) - (
(percent - 1000 - 50270) / 100 * 2) - (40702 / 100 * 9) - (159))
# This calculates the taxes to be paid if the entry is between £50271 and £150000.
try:
NI_Entry.insert(END, round(((percent - 1000 - 50270) / 100 * 2) + (40702 / 100 * 9) + (159),
2))
Income_tax_entry.insert(END, round(((percent - 1000 - 50270) / 10 * 4) + (37700 / 5), 2))
except IOError:
pass
except ValueError:
tkinter.messagebox.showerror("Error",
"There is an invalid syntax in your calculation please rectify it.") #
This shows the error message in case of a value error.
# This function calculates the weekly national insurance for any input and gives the profit for the input.
def btnnationalinsurance():
if Profit.get() == "":
tkinter.messagebox.showerror("Profit entry box is empty",
"Please fill in the profit entry box before performing a calculation!")
else:
try:
# In the if percent>xxx statemnts various mathematical calculations are carried out to calculate the
National insurance based on the input from the user.
# The mathematical equations were derived from government taxation rates.
percent = Profit.get()
percent = int(float(
percent)) # Had to add the float() for the calculator to be able to perform the arithmetic
function on decimal numbers too.
if 0 < percent < 184:
tkinter.messagebox.showinfo("No National Insurance Tax",
"There is no national insurance tax for any profit under £184.")
Profit.set(percent)
try:
NI_Entry.insert(END, "0.0")
Income_tax_entry.insert(END, "N/A")
except IOError:
pass
except IOError:
pass
# This except statement shows this error message if a value error is detected.
except ValueError:
tkinter.messagebox.showerror("Error", "There is an error in your calculation please rectify it.")
# This function calculates the yearly national insurance for any input.
def yearly_national_insurance():
if Profit.get() == "":
tkinter.messagebox.showerror("Profit entry box is empty",
"Please fill in the profit entry box before performing a calculation!")
else:
try:
percent = Profit.get()
percent = int(float(
percent)) # Had to add the float() for the calculator to be able to perform the arithmetic
function on decimal numbers too.
except IOError:
pass
except IOError:
pass
except IOError:
pass
except IOError:
pass
except IOError:
pass
except ValueError:
tkinter.messagebox.showerror("Error",
"There is an invalid syntax in your calculation please rectify it.") #
This shows the error message in case of a value error.
# This function calculates the income tax per annum for any input.
def btnincometax():
if Profit.get() == "":
tkinter.messagebox.showerror("Profit entry box is empty",
"Please fill in the profit entry box before performing a calculation!")
else:
try:
percent = Profit.get()
percent = int(float(
percent)) # Had to add the float() for the calculator to be able to perform the arithmetic
function on decimal numbers too.
except IOError:
pass
except IOError:
pass
except IOError:
pass
except IOError:
pass
except ValueError:
tkinter.messagebox.showerror("Error",
"There is an invalid syntax in your calculation please rectify it.") #
This shows the error message in case of a value error.
# This places the validation messages generated during the addition of data to the database inside of the
validation frame.
answer1 = Label(CalculatorValidationFrame, text="")
answer1.pack()
answer2 = Label(CalculatorValidationFrame, text="")
answer2.pack()
# This creates the profit income label and entry field, which will display the calculations of the user for the
profit income.
label = Label(window, text="Profit Income", font=('arial', 18, 'bold'))
label.place(x=780, y=40)
# This creates the entry field for the record ID to be typed in for the deletion of a record.
ID_label = Label(window, text="ID entry (deletion):", font=('arial', 13, 'bold'))
ID_label.place(x=200, y=450)
calc_ID = ttk.Entry(window, justify='center', textvariable=RecordID)
calc_ID.place(x=200, y=480)
calc_ID.focus()
# This code creates the function buttons for the profit calculator
Addition_btn = Button(window, font=('arial', 15, 'bold'),
text="+", command=lambda: btnClickProfit("+"), cursor="hand2").place(x=950, y=200)
# This creates the various buttons which will be used by the "Profit Income" entry field to calculate Income
tax, National Insurance (NI), and profit.
# ================Income calculator navigation bar======================================================
# Those are the functions which are to be assigned to the navigation bar's buttons
def iExit():
iExit = tkinter.messagebox.askyesno("Log-out", "Are you sure that you want to log-out?")
if iExit > 0:
window.destroy()
return
def mainmenu_wind():
window.destroy()
admin_menu()
def makeappointment_wind():
window.destroy()
Admin_appointment_wind()
def appointment_details_wind():
window.destroy()
Admin_customer()
def stock_management_wind():
window.destroy()
stock_management()
# ==========================List box===========================================================
# This code creates the scroll bar and the list box in which the saved data will be displayed.
DataFrame2 = LabelFrame(window, bd=1, width=500, height=250, padx=20, pady=3, relief=RIDGE, bg="Ghost White",
font=('arial', 15, 'bold'), text="Customer Appointment Details\n")
DataFrame2.place(x=450, y=50)
Customerlist = Listbox(DataFrame2, width=56, height=16, font=('arial', 15, 'bold'))
Customerlist.grid(row=0, column=0, padx=8)
# Allows the user to exit the window if the button that it is assigned to is clicked.
def iExit():
iExit = tkinter.messagebox.askyesno("Customer Registration Form", "Confirm that you want to exit")
if iExit > 0:
window.destroy()
return
# This function just resets the entry fields to blank when the button that it is assigned to is clicked.
def clearData():
ID_entry.delete(0, tkinter.END)
name_entry.delete(0, tkinter.END)
DOB.delete(0, tkinter.END)
phone_nr.delete(0, tkinter.END)
email_ad.delete(0, tkinter.END)
cus_gender.delete(0, tkinter.END)
# This funcion lists all of the records in the database on demand.
def DisplayInfo():
Customerlist.delete(0, END)
for row in cusDatabase_BackEnd.viewData():
Customerlist.insert(END, "%d, |%s| |%s| |%s| |%s| |%s| |%s|" % row)
# This function allows you to cancel an appointment by typing in the number of the record of the appointment you
want to delete into the Customer Id box
# and pressing "Delete Record".
def DeleteData():
if ID_entry.get():
cusDatabase_BackEnd.deleteRec(ID_entry.get())
clearData()
DisplayInfo()
# This performs a search on the ID entry and name_entry, which means that if either one is entered into
# their respective entry fields and the search button is clicked then the search will be performed.
def search():
Customerlist.delete(0, END)
for rows in cusDatabase_BackEnd.searchData(ID_entry.get(), name_entry.get()):
rows = list(rows)
for i in range(len(rows)):
rows[i] = str(rows[i]).strip()
Customerlist.insert(END, rows, str(""))
# This creates the frame in which the on screen validation will be displayed.
CustomerValidationFrame = LabelFrame(window, bd=5)
CustomerValidationFrame.place(x=1150, y=260, width=600)
# As this will respect the 11 digit UK phone number format there need to be if statements in place to
ensure that this is the case.
# This states that if the phone number is greater than 10 digits it will be too long as the first 0
doesn't count.
try:
# The phone number is defined as an integer because it only consists of numbers, this also allows
the program to check it's data type.
i = int(phone_nr.get())
if i > 9999999999:
answer3.config(text="This phone number is too long!")
return
# This states that if the phone number is smaller than 10 digits it will be too short.
# Because the phone number may be 11 digits long but they always start in 0s which do not count in
this method.
if i < 1000000000:
answer3.config(text="This phone number is too short!")
return
# This states that if the phone number is exactly 10 digits long then it is validas the 0 in which
all phone numbers start is not considered a digit.
if 1000000000 < i < 9999999999:
answer3.config(text="This is a valid phone number!")
# This states that if the phone number is not an integer then it is invalid due to it's data type.
except ValueError:
answer3.config(
text="That is not a phone number because it contains other characters that are not integers!")
return
# The gender entry must repect this regex entry style in order for it to be considered valid.
# All this does is that it defines the allowed way in which any gender can be written in order to be
considered valid.
try:
gender_style = r"^[a-zA-Z]+(([',. -][a-zA-Z ])?[a-zA-Z]*)*$"
if re.search(gender_style, cus_gender.get()):
answer4.config(text="The gender entry is valid!")
# If the input does not respect the validation requirements then a negative message which will let
the user know what has gone wrong
# will be displayed on screen and a return statement will be used so that the program will not get
to the add data part and as such
# not add any of the data to the database if all of the entries have not been found to be valid yet.
else:
answer4.config(text="The gender entry is not valid because it may contain integer characters!")
return
except ValueError:
pass
# The email entry must repect this regex email style in order for it to be considered valid and for the
entry of the data to occur.
try:
email_style = r"^([\w\.]+)\@([\w\.]+)(\.\w+)$"
if re.search(email_style, email_ad.get()):
answer5.config(text="That is a valid email record format!")
# If the format is not respected then this negative validation message will be displayed and the
return statement will take action
# hence not adding any of the data to the database.
else:
answer5.config(text="That is not a valid email address due to it's format!")
return
except ValueError:
pass
# The DOB entry must repect the dd/mm/yyyy format in order for it to be considered valid and for the
entry of the data to occur.
i = str(DOB.get())
try:
date_start = datetime.strptime(i,
'%d/%m/%Y') # DOB entry must conform to this format otherwise it
willbe considered invalid.
answer6.config(text="That is a valid date of birth format!")
# If the entry does not respect the date format then the except statement is called along the return
statement
# and the record will not be added to the database.
except ValueError:
answer6.config(text="Incorrect date of birth data format, it should be DD/MM/YYYY instead!")
return
try:
# The ID entry is defined as an integer so that the validation routine can check for length and data
type
# with the help of the try/except statement.
i = int(ID_entry.get())
if i < 100000:
answer2.config(
text="The ID is too short!") # If the ID is less than 6 digits long then this message will
be shown on screen.
return
if i > 999999:
answer2.config(
text="The ID is too long!") # If the ID is more than 6 digits long then this message will
be shown on screen.
return
if 99999 < i < 1000000:
answer2.config(
text="That is valid ID!") # If the ID is 6 digits long then this message will be shown on
screen.
# Only if all of the entry fields have been entered and all of the entries have been validated
respectively will the data be added to the database.
# And only then will the newlyadded record be displayed on the main screen.
tkinter.messagebox.showinfo("Customer information added", "Customer information succesfully added!")
cusDatabase_BackEnd.addCustomerRec(ID_entry.get(), DOB.get(), name_entry.get(), phone_nr.get(),
email_ad.get(), cus_gender.get())
Customerlist.delete(0, END)
Customerlist.insert(END, (
ID_entry.get(), DOB.get(), name_entry.get(), phone_nr.get(), email_ad.get(), cus_gender.get()))
# This takes the email address supplied and writes it to a text file in order for it to be entered into
the email window.
email = str(email_ad.get())
email_ad.set = (email)
lastEmail = open("lastEmail.txt", "w")
lastEmail.write(str(email))
lastEmail.close()
# This places the validation answers into the Validation label frame.
answer = Label(CustomerValidationFrame, text="")
answer.pack()
# This creates the "Customer's E-mail" of the customer entry box and label
Email_label = Label(window, text="Customer E-mail:", font=('arial', 13, 'bold'))
Email_label.place(x=30, y=220)
email_ad = ttk.Entry(window, textvariable=email)
email_ad.place(x=210, y=220)
email_ad.focus()
# This creates the "Phone number" of the customer entry box and label
Phone_label = Label(window, text="Phone number:", font=('arial', 13, 'bold'))
Phone_label.place(x=30, y=260)
phone_nr = ttk.Entry(window, textvariable=phone)
phone_nr.place(x=210, y=260)
phone_nr.focus()
# Creates the validation frame where the validation messages will be displayed for the entries.
ServiceValidationFrame = LabelFrame(window, bd=5)
ServiceValidationFrame.place(x=1150, y=750, width=600)
# This function validates the data entries in the service section and adds the data if the data is found to be
valid.
def AddInfo_Service():
# This ensures that presence checks will take place on all of the entries.
if service_ID.get() == '' or product_name.get() == '' or service_name.get() == '' or service_price.get() ==
'' or date_entry.get() == '' \
or Time_entry.get() == '':
tkinter.messagebox.showerror("Error", "Please make sure to complete all of the entry fields!")
else:
try:
# This ensures that the Service_ID will be an integer.
i = int(service_ID.get())
# If the ID is less than 6 digits long then this message will be shown on screen the program will
stop because of the "return".
if i < 100000:
answer12.config(text="The ID is too short!")
return
# If the ID is more than 6 digits long then this message will be shown on screen & the program will
stop because of the "return".
if i > 999999:
answer12.config(text="The ID is too long!")
return
if 99999 < i < 1000000:
answer12.config(
text="That is valid ID!") # If the ID is 6 digits long then this message will be shown on
screen.
# If the entry is not an integer then this negative message will be displayed.
except ValueError:
answer12.config(
text="That is not a valid ID entry because it contains other characters that are not integers!")
return
try:
service_name_style = r"^[a-zA-Z]+(([',. -][a-zA-Z ])?[a-zA-Z]*)*$" # This just sets the format that
the service name must respect.
if re.search(service_name_style, service_name.get()):
answer7.config(text="That is a valid service name!")
else:
# If the entry doesn't respect this format then this negative message will be displayed.
answer7.config(text="That is not a valid service name because it may contain integers!")
return
except ValueError:
pass
try:
int(service_price.get()) # This just states that the service price must be an integer.
answer8.config(text="That is a valid service price!")
# If the entry is not an integer then this negative message will be displayed.
except ValueError:
answer8.config(
text="That is not a valid service price because it contains other characters that are not
integers!")
return
timeformat = "%H:%M" # This sets out the format that the appointment time must follow for it to be
considered valid.
i = str(Time_entry.get())
try:
validtime = datetime.strptime(i, timeformat)
answer10.config(
text="That is a valid time entry!") # This message will be displayed if the HH:MM format is
respected.
# If the entry doesn't respect this time format then this negative message will be displayed.
except ValueError:
answer10.config(
text="The time entry is not valid because it is not in the correct format!") # This message
will be displayed if the HH:MM format is not respected.
return
try:
product_name_style = r"^[a-zA-Z]+(([',. -][a-zA-Z ])?[a-zA-Z]*)*$" # This just sets the style that
the product name must respect.
if re.search(product_name_style, product_name.get()):
answer11.config(
text="That is a valid product name!") # If the style is respected then this message will be
displayed.
else:
# If the entry doesn't respect this format then this negative message will be displayed.
answer11.config(text="That is not a valid product name because it may contain integers!")
return
except ValueError:
pass
i = str(date_entry.get())
try:
date_start = datetime.strptime(i,
'%d/%m/%Y') # This is states the format that the appointment date
must respect in order for validation to be granted.
answer9.config(text="That is a valid date format!")
# If the entry doesn't respect this date format then this negative message will be displayed.
except ValueError:
answer9.config(text="Incorrect date format, it should be DD/MM/YYYY instead!")
return
# This allows for information to be added to the database and be displayed after it is successfully
validated.
tkinter.messagebox.showinfo("Appointment Made", "Appointment Successfully Made!")
serDatabase_BackEnd.addServiceRec(service_ID.get(), product_name.get(), service_name.get(),
service_price.get(), date_entry.get(), Time_entry.get())
Servicelist.delete(0, END)
Servicelist.insert(END, (
service_ID.get(), product_name.get(), service_name.get(), service_price.get(), date_entry.get(),
Time_entry.get()))
# This creates a text file that writes various appointment related details which will be used
# to create a confirmation message for the user in a mannered & semi-automatic way.
email_details = open('emaildetails.txt', 'w')
email_details.write(
name_entry.get() + ',' + service_name.get() + ',' + service_price.get() + ',' + date_entry.get() +
',' + Time_entry.get())
email_details.close()
# This lists all of the records in the database on demand by retrieving them from the database.
# and inserting them into the Servicelist listbox.
def DisplayInfo_Service():
Servicelist.delete(0, END)
for row in serDatabase_BackEnd.viewServiceData():
Servicelist.insert(END, "%d,|%s| |%s| |%s| |£%s| |%s| |%s|" % row)
# This allows for any data item in the database to be searched for as long as it is inputted in it's determined
entry field.
# It does this by fetching the record/records with the matching searched item and displayong the entire record
into the listbox.
def searchService():
Servicelist.delete(0, END)
for rows in serDatabase_BackEnd.searchDataService(Time_entry.get(), service_ID.get(), product_name.get(),
service_name.get(), service_price.get(),
date_entry.get()):
rows = list(rows)
for i in range(len(rows)):
rows[i] = str(rows[i]).strip()
Servicelist.insert(END, rows, str(""))
# This function allows you to cancel an appointment by typing in the record ID of the appointment you want to
delete into the Service Id box
# and pressing "Delete Record".
def DeleteServiceData():
if service_ID.get():
serDatabase_BackEnd.deleteServiceRec(service_ID.get())
clearData_Service()
DisplayInfo_Service()
# This places the validation answers into the Validation label frame.
# This ensures that if there are any empty required fields an error message will appear even though the finish
appointment button must be clicked after
# the Customer snd Service information has been validated and added to the databases.
def Finish_Appointment():
if service_ID.get() == '' or product_name.get() == '' or service_name.get() == '' or service_price.get() ==
'' or date_entry.get() == '' \
or Time_entry.get() == '' or name_entry.get() == '':
tkinter.messagebox.showerror("Error", "Error, please complete all entry fields!")
# This function adds the key appointment details into the Principal database which can be used to view and
search for appointment detais in an easy and convenient way.
else:
tkinter.messagebox.showinfo("Appointment Made", "Process complete!")
priDatabase_BackEnd.addMainRec(date_entry.get(), Time_entry.get(), service_ID.get(), name_entry.get(),
product_name.get(), service_name.get(), service_price.get())
Principallist.insert(END, (
date_entry.get(), Time_entry.get(), service_ID.get(), name_entry.get(), product_name.get(),
service_name.get(), service_price.get()))
# This button allows the user to delete a record based on the record id that they enter.
btnDeleteS = tk.Button(window, text="Delete Record", command=DeleteServiceData, cursor='hand2')
btnDeleteS.place(x=1300, y=530, width=175, height=50)
# This button is used to display all of the records held in the database.
btnDisplayS = tk.Button(window, text="Display", command=DisplayInfo_Service, cursor='hand2')
btnDisplayS.place(x=1150, y=530, width=125, height=50)
# This allows for the user to clear the entry fields on click.
btnClearS = tk.Button(window, text="Clear", command=clearData_Service, cursor='hand2')
btnClearS.place(x=1150, y=580, width=125, height=50)
# This allows for the search function being called for the user to use to search for a record.
btnSearchS = tk.Button(window, text="Search record", command=searchService, cursor='hand2')
btnSearchS.place(x=1150, y=630, width=125, height=50)
# This button is used to finnish the appointment which means that the important data is entered into the main
appointment details table.
btnFinishS = tk.Button(window, text="Finish appointment", command=Finish_Appointment, cursor='hand2')
btnFinishS.place(x=1300, y=630, width=175, height=50)
# This creates the ID Generator button which redirects the user to the ID generator when clicked.
btnGenerate = tk.Button(window, text="Generate ID", command=generate_ID, cursor='hand2')
btnGenerate.place(x=1150, y=680, width=125, height=50)
# This creates the "Confirmation Eamil button which opens the email window when clicked.
btnemail_send = tk.Button(window, text="Confirmation Email", command=email_wind, cursor='hand2')
btnemail_send.place(x=1300, y=680, width=175, height=50)
# =====================================Navigation bar==================================================
# Those are the functions which are to be assigned to the navigation bar's buttons
def LExit():
iExit = tkinter.messagebox.askyesno("Log-out", "Are you sure that you want to log-out?")
if iExit > 0:
window.destroy()
return
def mainmenu_wind():
window.destroy()
admin_menu()
def appointment_details_wind():
window.destroy()
Admin_customer()
def income_calc_wind():
window.destroy()
income_calculator()
def stock_management_wind():
window.destroy()
stock_management()
def appointmentdetails_wind():
window.destroy()
Staff_customer()
def make_appointment_wind():
window.destroy()
Staff_appointment_wind()
window.mainloop()
def appointment_details_wind():
window.destroy()
Admin_customer()
def make_appointment_wind():
window.destroy()
Admin_appointment_wind()
def income_calc_wind():
window.destroy()
income_calculator()
def stock_management_wind():
window.destroy()
stock_management()
# This creates the buttons showcased in the window with the commands of each of the windows that they open.
btn_main_appoint = tk.Button(window, text='Appointment details', command=appointment_details_wind,
cursor='hand2')
btn_main_appoint.place(x=225, y=100, width=150, height=50)
window.mainloop()
# ===============================================================================================================
# This creates the Staff button which when clicked will redirect the user to the staff login page.
btn_staff = tk.Button(window, text='Staff', command=staff_login, cursor='hand2')
btn_staff.place(x=100, y=100, width=100, height=50)
# This creates the Admin button which when clicked will redirect the user to the staff login page.
btn_admin = tk.Button(window, text='Admin', command=admin_login, cursor='hand2')
btn_admin.place(x=200, y=100, width=100, height=50)
window.mainloop()
Customer information database annotated listing:
import sqlite3 # This imports the sqlite3 module to be used in the creation of the database.
# This creates the Customer database's columns in sqlite which will be used to store the inputted customer
information.
def CustomerData():
conn = sqlite3.connect("Customer.db")
cur = conn.cursor()
cur.execute("CREATE TABLE IF NOT EXISTS customer(id INTEGER PRIMARY KEY, CustomerID text, CustomerDOB text,
CustomerName text, phone text, email text, gender text)")
conn.commit()
conn.close()
# This creates the function in the back end of the program which will be called in the front-end to add customer
details to their allocated sqlite columns
# this function will be linked to a function in the front end and that function will be assigned to an "Add Details"
button.
def addCustomerRec(CustomerID, CustomerName, CustomerDOB, email, gender, phone):
conn = sqlite3.connect("Customer.db")
cur = conn.cursor()
cur.execute("INSERT INTO Customer VALUES (NULL, ?,?,?,?,?,?)",
(CustomerID, CustomerName, CustomerDOB, email, gender, phone))
conn.commit()
conn.close()
# This creates the function in the back end of the program which will be called in the front-end to display all of
the records that the database holds.
def viewData():
conn = sqlite3.connect("Customer.db")
cur = conn.cursor()
cur.execute("SELECT * FROM Customer")
rows = cur.fetchall()
conn.close()
return rows
# This creates the function in the back end of the program which will be called in the front-end to delete a
selected record.
def deleteRec(id):
conn = sqlite3.connect("Customer.db")
cur = conn.cursor()
cur.execute("DELETE FROM Customer WHERE id=?", (id,))
conn.commit()
conn.close()
# This creates the function in the back end of the program which will be called in the front-end to search for
certain records that the data holds.
# In this case the search will be based on the "CustomerID" and the "CustomerName" and when those data items will be
entered into their specific entry fields.
# The database function below will execute the query by fetching all of the records with the matching data items.
def searchData(CustomerID="", CustomerName=""):
conn = sqlite3.connect("Customer.db")
cur = conn.cursor()
cur.execute("SELECT * FROM Customer WHERE CustomerID=? OR CustomerName=?", (CustomerID, CustomerName,))
rows = cur.fetchall()
conn.close()
return rows
CustomerData()
# This creates the Customer database's columns in sqlite which will be used to store the inputted customer
information.
def ServiceData():
conn = sqlite3.connect("Service.db")
cur = conn.cursor()
cur.execute("CREATE TABLE IF NOT EXISTS Service(id INTEGER PRIMARY KEY, ServiceID text, ServiceName text,
ServicePrice text, Date text, Time text, ExtraProducts text)")
conn.commit()
conn.close()
# This creates the function in the back end of the program which will be called in the front-end to add customer
details to their allocated sqlite columns
# this function will be linked to a function in the front end and that function will be assigned to an "Add Details"
button.
def addServiceRec(ServiceID, ServiceName, ServicePrice, Date, Time, ExtraProducts):
conn = sqlite3.connect("Service.db")
cur = conn.cursor()
cur.execute("INSERT INTO Service VALUES (NULL, ?,?,?,?,?,?)",
(ServiceID, ServiceName, ServicePrice, Date, Time, ExtraProducts))
conn.commit()
conn.close()
# This creates the function in the back end of the program which will be called in the front-end to display all of
the records that the database holds.
def viewServiceData():
conn = sqlite3.connect("Service.db")
cur = conn.cursor()
cur.execute("SELECT * FROM Service")
rows = cur.fetchall()
conn.close()
return rows
# This creates the function in the back end of the program which will be called in the front-end to delete a
selected record.
def deleteServiceRec(id):
conn = sqlite3.connect("Service.db")
cur = conn.cursor()
cur.execute("DELETE FROM Service WHERE id=?", (id,))
conn.commit()
conn.close()
# This creates the function in the back end of the program which will be called in the front-end to search for
certain records that the data holds.
# In this case the search will be based on any data entry that the user desires.
# The database function below will execute the query by fetching all of the records with the matching data items
provided by the user.
def searchDataService(ServiceID="", ServiceName="", ServicePrice="", Date="", Time="", ExtraProducts=""):
conn = sqlite3.connect("Service.db")
cur = conn.cursor()
cur.execute(
"SELECT * FROM Service WHERE ServiceID=? OR ServiceName=? OR ServicePrice=? OR Date=? OR Time=? OR
ExtraProducts=?",
(ServiceID, ServiceName, ServicePrice, Date, Time, ExtraProducts,))
rows = cur.fetchall()
conn.close()
return rows
ServiceData()
#This creates the Principal database's columns in sqlite which will be used to store the inputted appointment
information.
def PrincipalData():
conn=sqlite3.connect("Principal.db")
cur=conn.cursor()
cur.execute("CREATE TABLE IF NOT EXISTS principal(id INTEGER PRIMARY KEY, CustomerID text, ServiceName text,
ServicePrice text, 'Date' DATE, 'Time' TIME, ExtraProducts text, CustomerName text)")
conn.commit()
conn.close()
#This creates the function in the back end of the program which will be called in the front-end to add appointment
details to their allocated sqlite columns
#this function will be linked to a function in the front end and that function will be assigned to the "Finish
appointment" button.
def addMainRec(Date, Time, CustomerID, CustomerName, ExtraProducts, ServiceName, ServicePrice):
conn=sqlite3.connect("Principal.db")
cur=conn.cursor()
cur.execute("INSERT INTO Principal VALUES (NULL, ?,?,?,?,?,?,?)", (Date, Time, CustomerID, CustomerName,
ExtraProducts, ServiceName, ServicePrice))
conn.commit()
conn.close()
#This creates the function in the back end of the program which will be called in the front-end to display all of
the records that the database holds.
def viewMainData():
conn=sqlite3.connect("Principal.db")
cur=conn.cursor()
cur.execute("SELECT * FROM principal")
rows = cur.fetchall()
conn.close()
return rows
#This creates the function in the back end of the program which will be called in the front-end to delete a selected
record.
def deleteMainRec(id):
conn=sqlite3.connect("Principal.db")
cur=conn.cursor()
cur.execute("DELETE FROM Principal WHERE id=?", (id,))
conn.commit()
conn.close()
#This creates the function in the back end of the program which will be called in the front-end to search for
certain records that the data holds.
#The database function below will execute the query by fetching all of the records with the matching data items.
def searchDataMain(CustomerID="", ExtraProducts="", ServicePrice="", ):
conn=sqlite3.connect("Principal.db")
cur=conn.cursor()
cur.execute("SELECT * FROM Principal WHERE CustomerID=? or ExtraProducts=? or ServicePrice=? ", (CustomerID,
ExtraProducts, ServicePrice,))
rows=cur.fetchall()
conn.close()
return rows
PrincipalData()
# This creates the Calculator database's columns in sqlite which will be used to store the inputted customer
information.
def CalculatorData():
conn = sqlite3.connect("Calculator.db")
cur = conn.cursor()
cur.execute("CREATE TABLE IF NOT EXISTS calculator(id INTEGER PRIMARY KEY, Date text, Profit text, GrossIncome
text)")
conn.commit()
conn.close()
# This creates the function in the back end of the program which will be called in the front-end to add customer
details to their allocated sqlite columns
# this function will be linked to a function in the front end and that function will be assigned to an "Save Data"
button.
def addCalcRec(Date, Profit, GrossIncome):
conn = sqlite3.connect("Calculator.db")
cur = conn.cursor()
cur.execute("INSERT INTO Calculator VALUES (NULL, ?,?,?)", (Date, Profit, GrossIncome))
conn.commit()
conn.close()
# This creates the function in the back end of the program which will be called in the front-end to display all of
the records that the database holds.
def viewCalcData():
conn = sqlite3.connect("Calculator.db")
cur = conn.cursor()
cur.execute("SELECT * FROM Calculator")
rows = cur.fetchall()
conn.close()
return rows
# This creates the function in the back end of the program which will be called in the front-end to delete a
selected record.
def deleteCalcRec(id):
conn = sqlite3.connect("Calculator.db")
cur = conn.cursor()
cur.execute("DELETE FROM Calculator WHERE id=?", (id,))
conn.commit()
conn.close()
CalculatorData()