0% found this document useful (0 votes)
19 views120 pages

Annotated Listing of The System

The document outlines a Python program that utilizes the tkinter library to create a user interface for managing appointments and sending confirmation emails. It includes features for user credential management, email composition with attachments, and validation of input fields. The program integrates with various backend databases and uses SMTP for email functionality, ensuring a user-friendly experience for managing customer appointments and communications.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
19 views120 pages

Annotated Listing of The System

The document outlines a Python program that utilizes the tkinter library to create a user interface for managing appointments and sending confirmation emails. It includes features for user credential management, email composition with attachments, and validation of input fields. The program integrates with various backend databases and uses SMTP for email functionality, ensuring a user-friendly experience for managing customer appointments and communications.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 120

Main program annotated listing:

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.

User selection window:


# This creates the selection window itself.
window = Tk()
window.title("User selection window")
window.geometry('400x400+0+0')
window.resizable(0, 0)
titlelabel = Label(window, text="User selection window", font=('arial', 20, 'bold'), bg="black", fg="white")
titlelabel.pack(side=TOP, fill=X)
# This displays a cosmetic only image of the business logo to be used on the user selection window.
HairCraftImage = PhotoImage(file='Hair_Craft_logo.png') # Imports image into the program.
HairCraft_logo = Label(window, image=HairCraftImage, fg='black', bg='white').place(x=125, y=200)

# 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()

Confirmation E-mail window:


# =====================================================Email window================================================
# Creating the confirmation email window and it's subwindow.
def email_wind():
# =================================Confirmation email functions================================================
def attachment():
global filename, filetype, filepath, check
check = True

# 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.

# This uses the smtp gmail servers to send the emails.


smtp_connection = smtplib.SMTP('smtp.gmail.com', 587)
smtp_connection.starttls() # Encrypts the email message
# This takes the account information that has to be provided in the email sender and logs the user in to the
smtp gmail servers to send the email.
smtp_connection.login(credentials[0], credentials[1])
smtp_connection.send_message(
message) # This actually sends the message after everything has been compiled together.
smtp_confirmation = smtp_connection.ehlo() # This identifies the email when connecting to the smtp servers
in order for the email to be sent.
if smtp_confirmation[
0] == 250: # 250 is a SMTP server response which indicates that everything went well and I have used it
for my success/error messages.
return 'sent'
else:
return 'failed'

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)

Confirmatio E-mail settings window:


# ======================================User credential settings====================================
# This creates the credential settings window itself with it's title, geometry and background.
window1 = Toplevel()
window1.title('Credential Settings')
window1.geometry('500x340+0+0')
window1.resizable(0, 0) # This is so that the window cannot be expanded to full screen.
window1.config(bg='dodger blue2')

# 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)

# The clear button clears all entry fields when clicked.


clearImage = PhotoImage(file='clear.png') # Imports the "Save" image into the program.
btn_Clear = Button(window1, image=clearImage, bd=0, bg='dodger blue2', cursor='hand2',
activebackground='dodger blue2'
, command=clear1).place(x=280, 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 function clears the entry fields on click


def clear():
RecipientEntryField.delete(0, END)
subjectEntryField.delete(0, END)
textarea.delete(1.0, END)

# 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)

# This imports the "settings" image


settingImage = PhotoImage(file='setting.png')

# 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 creates the recipient entry field.


RecipientLabelFrame = LabelFrame(window, text='Recipient (Email Address)', font=('times new roman', 16, 'bold'),
bd=5, fg='white', bg='dodger blue2')
RecipientLabelFrame.grid(row=2, column=0, padx=100)

# 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 attachment button


attachmentImage = PhotoImage(file='attachments.png') # Imports attachment image into the program.
btn_Attach = Button(emailLabelFrame, text=' Attachment', image=attachmentImage, compound=LEFT,
font=('arial', 12, 'bold'),
cursor='hand2', bd=0, bg='dodger blue2', activebackground='dodger blue2',
command=attachment).grid(row=0, column=1)

# 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()

Staff appointment details window:


# This creates the Appointment details window for the staff member.
def Staff_customer():
# This code creates the title, size and style of the window as well as the window itself.
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)
black_bar_label = Label(window, text="", font=('arial', 15, 'bold'), bg="black", fg="white")
black_bar_label.pack(side=BOTTOM, fill=X)

# ===========================Listbox 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)

# ==============Functions for the appointment details window====================================================


# This function allows the user to exit the program
def Exit():
Exit = tkinter.messagebox.askyesno("Customer Registration Form", "Confirm that you want to exit")
if Exit > 0:
window.destroy()
return

# 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(""))

# =================================Entry fields & labels=======================================================


# This creates the entry fields and labels corresponding to them for the staff appointment details window.
date_label = Label(window, text="Date search:", font=('arial', 13, 'bold'))
date_label.place(x=110, y=180)
date_entry2 = ttk.Entry(window, textvariable=Date)
date_entry2.place(x=240, y=180)
date_entry2.focus()

service_label = Label(window, text="Service search:", font=('arial', 13, 'bold'))


service_label.place(x=110, y=260)
ID_entry2 = ttk.Entry(window, textvariable=CustomerName)
ID_entry2.place(x=240, y=260)
ID_entry2.focus()
ID_label = Label(window, text="ID search:", font=('arial', 13, 'bold'))
ID_label.place(x=110, y=340)
service_name2 = ttk.Entry(window, textvariable=ServiceName)
service_name2.place(x=240, y=340)
service_name2.focus()

Record_ID_label = Label(window, text="Record ID:", font=('arial', 13, 'bold'))


Record_ID_label.place(x=110, y=100)
principal_ID = ttk.Entry(window, textvariable=PrincipalID)
principal_ID.place(x=240, y=100)
principal_ID.focus()

deletion_label = Label(window, text="(deletion)", font=('arial', 13, 'bold')).place(x=110, y=120)

# ==========================Buttons for data manipulation=====================================================


# This creates the buttons for the staff appointment details window.
btnDelete = tk.Button(window, text="Delete", command=DeletePrincipalData, cursor='hand2')
btnDelete.place(x=1350, y=60, width=125, height=50)

btnExit = tk.Button(window, text="Exit", command=Exit, cursor='hand2')


btnExit.place(x=1350, y=110, width=125, height=50)

btnDisplay = tk.Button(window, text="Display", command=DisplayInfo_Main, cursor='hand2')


btnDisplay.place(x=1200, y=110, width=125, height=50)

btnSearch = tk.Button(window, text="Search", command=searchPrincipal, cursor='hand2')


btnSearch.place(x=1200, y=60, width=125, height=50)

btnClearM = tk.Button(window, text="Clear", command=clearData_Main, cursor='hand2')


btnClearM.place(x=1200, y=160, width=125, height=50)

# ======================================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()

# This creates the navigation bar itself and it's buttons.


btnframe = Frame(window, bd=4, relief=RIDGE, pady=10)
btnframe.place(x=1200, y=550)

btn_menu = tk.Button(btnframe, text='Main menu', command=mainmenu_wind, cursor='hand2')


btn_menu.grid(row=0, column=0)

btn_appoint = tk.Button(btnframe, text='Make An Appointment', command=makeappointment_wind, cursor='hand2')


btn_appoint.grid(row=0, column=1)

btn_logout = tk.Button(btnframe, text='Log-out', command=Exit, cursor='hand2')


btn_logout.grid(row=0, column=2)

Staf main appointment window:


# ================================Customer & Service insertion section========================================
# This code creates the title, size and style of the window.
def Staff_appointment_wind():
window = Tk()
window.title("Appointment form")
window.geometry('1200x700')
titlelabel = Label(window, text="Staff Appointment Window", 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)
# ======================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)

# =========================Functions for Customer details database======================================


# This function allows for the Customer ID to be automatically generated by the system as a 6 digit number.
# And if the user clciks it more than once it clears the previous ID number and replaces it.
def generate_ID():
ID_entry.delete(0, tkinter.END)
service_ID.delete(0, tkinter.END)
ID_entry.delete(0, END)
x = random.randint(100000, 999999)
randomnum = str(x)
ID_entry.insert(END, randomnum)
service_ID.insert(END, randomnum)

# 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 displays all of the records in the database on demand.


# It displays the records in the format shown in the format hown on the "Customerlist.insert" line.
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(""))

# =================================Adding and validating Customer records===================================

# 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)

# This function validates and adds the data to the database.


def AddInfo():
# This is a presence check which ensures that nothing will even be validated unless all of the entry fields
are filled in.
# This is displayed through an error message.
if ID_entry.get() == "" or DOB.get() == "" or name_entry.get() == "" or phone_nr.get() == "" or
email_ad.get() == "" or cus_gender.get() == '':
tkinter.messagebox.showerror("Error", "Please make sure to fill in all of the entry fields!")
# If all of the entry fields have been completed then validation can occur, my validation technique utilises
return statements which ensure that
# if any entry fields are not valid then none of the data will be added to the database, this is done with
the following try/execute statements.
else:
name_style = r"^[a-zA-Z]+(([',. -][a-zA-Z ])?[a-zA-Z]*)*$"
if re.search(name_style, name_entry.get()):
answer.config(text="That is a valid name!")
else:
answer.config(text="That is not a valid name because it may contain integer characters!")
return
# 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.

# If the ID is not fully composed of integers this message will be displayed.


# and the return statemnt will be put in action.
except ValueError:
answer2.config(
text="That is not a valid ID entry because it contains other characters that are not integers!")
return

# 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()

answer2 = Label(CustomerValidationFrame, text="")


answer2.pack()

answer3 = Label(CustomerValidationFrame, text="")


answer3.pack()

answer4 = Label(CustomerValidationFrame, text="")


answer4.pack()

answer5 = Label(CustomerValidationFrame, text="")


answer5.pack()

answer6 = Label(CustomerValidationFrame, text="")


answer6.pack()

# =================Entry boxes and Labels of Staff Appointment database===========================


# This creates the "Customer's DOB" entry box and label.
DOB_label = Label(window, text="Customer DOB:", font=('arial', 13, 'bold'))
DOB_label.place(x=30, y=180)
DOB = ttk.Entry(window, textvariable=CustomerDOB)
DOB.place(x=210, y=180)
DOB.focus()

# This creates the "Customer's name" entry box and label.


Customer_Name_label = Label(window, text="Customer's Name:", font=('arial', 13, 'bold'))
Customer_Name_label.place(x=30, y=60)
name_entry = ttk.Entry(window, textvariable=CustomerName)
name_entry.place(x=210, y=60)
name_entry.focus()

# This creates the "Customer's ID" entry box and label.


CustomerID_label = Label(window, text="Customer's ID:", font=('arial', 13, 'bold'))
CustomerID_label.place(x=30, y=100)
ID_entry = ttk.Entry(window, textvariable=CustomerID)
ID_entry.place(x=210, y=100)
ID_entry.focus()

# This creates the "Customer's gender" entry box and label


Gender_label = Label(window, text="Customer Gender:", font=('arial', 13, 'bold'))
Gender_label.place(x=30, y=140)
cus_gender = ttk.Entry(window, textvariable=gender)
cus_gender.place(x=210, y=140)
cus_gender.focus()

# 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()

# ======================Database Buttons for Customer section===========================================


# Those lines of code create the buttons used for the Customer table.
btnAddData2 = tk.Button(window, text="Add Details", command=AddInfo, cursor='hand2')
btnAddData2.place(x=1300, y=110, width=175, height=50)

btnDelete2 = tk.Button(window, text="Delete Record", command=DeleteData, cursor='hand2')


btnDelete2.place(x=1300, y=60, width=175, height=50)

btnExit2 = tk.Button(window, text="Exit", command=iExit, cursor='hand2')


btnExit2.place(x=1150, y=110, width=125, height=50)

btnDisplay2 = tk.Button(window, text="Display", command=DisplayInfo, cursor='hand2')


btnDisplay2.place(x=1150, y=60, width=125, height=50)
btnClear2 = tk.Button(window, text="Clear", command=clearData, cursor='hand2')
btnClear2.place(x=1150, y=160, width=125, height=50)

btnSearch2 = tk.Button(window, text="Search record", command=search, cursor='hand2')


btnSearch2.place(x=1300, y=160, width=175, height=50)

# ===========================Service details List box================================================


# This code creates the list box in which the Service saved data will be displayed.
DataFrame3 = LabelFrame(window, bd=1, width=00, height=250, padx=20, pady=3, relief=RIDGE, bg="Ghost White",
font=('arial', 15, 'bold'), text="Service Appointment Details\n")
DataFrame3.place(x=450, y=580)
Servicelist = Listbox(DataFrame3, width=56, height=12, font=('arial', 15, 'bold'))
Servicelist.grid(row=0, column=0, padx=8)

# ========================================Functions for service table==================================

# Function to clear the entry fields when clicked.


def clearData_Service():
service_ID.delete(0, tkinter.END)
service_name.delete(0, tkinter.END)
Time_entry.delete(0, tkinter.END)
date_entry.delete(0, tkinter.END)
product_name.delete(0, tkinter.END)
service_price.delete(0, tkinter.END)

# 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()

answer7 = Label(ServiceValidationFrame, text="")


answer7.pack()

answer8 = Label(ServiceValidationFrame, text="")


answer8.pack()

answer9 = Label(ServiceValidationFrame, text="")


answer9.pack()

answer10 = Label(ServiceValidationFrame, text="")


answer10.pack()

answer11 = Label(ServiceValidationFrame, text="")


answer11.pack()

# ====================================================Finnish appointment function


==========================================
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=5000, y=2000)
Principallist = Listbox(DataFrame, width=58, height=16, font=('arial', 15, 'bold'))
Principallist.grid(row=0, column=0, padx=8)

# 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()))

# ==============================Service entry fields and labels=================================


# This creates the Customer's Id entry box and label in the service table
CustomerID_label = Label(window, text=" Customer's ID :", font=('arial', 13, 'bold'))
CustomerID_label.place(x=30, y=580)
service_ID = ttk.Entry(window, textvariable=ServiceID)
service_ID.place(x=210, y=580)
service_ID.focus()

# This creates the Service's name entry box and label.


Service_Name_label = Label(window, text="Service Name:", font=('arial', 13, 'bold'))
Service_Name_label.place(x=30, y=620)
service_name = ttk.Entry(window, textvariable=ServiceName)
service_name.place(x=210, y=620)
service_name.focus()

# This creates the service price entry box and label.


Price_label = Label(window, text="Service Price:", font=('arial', 13, 'bold'))
Price_label.place(x=30, y=660)
service_price = ttk.Entry(window, textvariable=ServicePrice)
service_price.place(x=210, y=660)
service_price.focus()

# This creates the Date of appointment entry box and label.


Appointment_date_label = Label(window, text="Date of appointment:", font=('arial', 13, 'bold'))
Appointment_date_label.place(x=30, y=700)
date_entry = ttk.Entry(window, textvariable=Date)
date_entry.place(x=210, y=700)
date_entry.focus()

# This creates the Date of appointment entry box and label.


Time_label = Label(window, text="Time of appointment:", font=('arial', 13, 'bold'))
Time_label.place(x=30, y=740)
Time_entry = ttk.Entry(window, textvariable=Time)
Time_entry.place(x=210, y=740)
Time_entry.focus()

# This creates the Extra products entry box and label.


Products_label = Label(window, text="Extra Products:", font=('arial', 13, 'bold'))
Products_label.place(x=30, y=780)
product_name = ttk.Entry(window, textvariable=ExtraProducts)
product_name.place(x=210, y=780)
product_name.focus()

# =============================Database Buttons for Appointment window============================


# This button is used by the user to add the details to the services database, when this button is clicked
validation also takes place.
btnAddDataS = tk.Button(window, text="Add Details", command=AddInfo_Service, cursor='hand2')
btnAddDataS.place(x=1300, y=580, width=175, height=50)

# 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()

# This creates the navigation bar itself and it's buttons.


btnframe = Frame(window, bd=4, relief=RIDGE, pady=10)
btnframe.place(x=1150, y=925)

btn_menu = tk.Button(btnframe, text='Main menu', command=mainmenu_wind, cursor='hand2')


btn_menu.grid(row=0, column=0)

btn_appoint = tk.Button(btnframe, text='Appointment details', command=appointment_details_wind, cursor='hand2')


btn_appoint.grid(row=0, column=1)

btn_logout = tk.Button(btnframe, text='Log-out', command=LExit, cursor='hand2')


btn_logout.grid(row=0, column=2)

window.mainloop()

Staff login window:


# ================================================Staff login window ============================================
def staff_login():
window = Tk()
window.title("Staff password window")
window.geometry('400x400')
titlelabel = Label(window, text="Log-in page for staff", font=('arial', 20, 'bold'), bg="black", fg="white")
titlelabel.pack(side=TOP, fill=X)

label = Label(window, text="", font=('arial', 15, 'bold'), bg="black", fg="white")

# 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)

# This creates the username and password entry boxes


password_entry = Entry(window, show="*") # this makes the password be entered in asterix.
password_entry.place(x=200, y=130)

user_name_entry = Entry(window)
user_name_entry.place(x=200, y=90)

Admin login window:


# ================================================Admin login window==============================================
# This function creates the admin login window.
def admin_login():
# This creates the window itself.
window = Tk()
window.title("Admin password window")
window.geometry('400x400')
titlelabel = Label(window, text="Log-in page for admin", font=('arial', 20, 'bold'), bg="black", fg="white")
titlelabel.pack(side=TOP, fill=X)

# 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.

CheckPass = Button(window, text="Check credentials", command=login_admin, cursor='hand2').place(x=150, y=200)


password_entry = Entry(window,
show="*") # This creates the password entry field and it allows the password be entered
in asterix.
password_entry.place(x=200, y=130)

user_name_entry = Entry(window) # This creates the name entry field.


user_name_entry.place(x=200, y=90)

# ========================================================================================
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)

# =======================Functions for the appointment details window=====================================


# This function allows the user to exit the program
def Exit():
Exit = tkinter.messagebox.askyesno("Customer Registration Form", "Confirm that you want to exit")
if Exit > 0:
window.destroy()
return

# 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(""))

# =======================Entry fields & labels=======================================================


# This creates the entry fields of the window and the labels which indicate what the user should enter in each
entry field.
date_label = Label(window, text="Date search:", font=('arial', 13, 'bold'))
date_label.place(x=110, y=180)
date_entry2 = ttk.Entry(window, textvariable=Date)
date_entry2.place(x=240, y=180)
date_entry2.focus()

service_label = Label(window, text="Service search:", font=('arial', 13, 'bold'))


service_label.place(x=110, y=260)
ID_entry2 = ttk.Entry(window, textvariable=CustomerID)
ID_entry2.place(x=240, y=260)
ID_entry2.focus()

ID_label = Label(window, text="ID search:", font=('arial', 13, 'bold'))


ID_label.place(x=110, y=340)
service_name2 = ttk.Entry(window, textvariable=CustomerName)
service_name2.place(x=240, y=340)
service_name2.focus()

Record_ID_label = Label(window, text="Record ID:", font=('arial', 13, 'bold'))


Record_ID_label.place(x=110, y=100)
principal_ID = ttk.Entry(window, textvariable=PrincipalID)
principal_ID.place(x=240, y=100)
principal_ID.focus()

deletion_label = Label(window, text="(deletion)", font=('arial', 13, 'bold')).place(x=110, y=120)

# ========================Buttons for data manipulation================================================


# This creates the buttons which the user can use to manipulate the principal database's contents.
btnDelete = tk.Button(window, text="Delete", command=DeletePrincipalData, cursor='hand2')
btnDelete.place(x=1350, y=60, width=125, height=50)

btnExit = tk.Button(window, text="Exit", command=Exit, cursor='hand2')


btnExit.place(x=1350, y=110, width=125, height=50)

btnDisplay = tk.Button(window, text="Display", command=DisplayInfo_Main, cursor='hand2')


btnDisplay.place(x=1200, y=110, width=125, height=50)

btnSearch = tk.Button(window, text="Search", command=searchPrincipal, cursor='hand2')


btnSearch.place(x=1200, y=60, width=125, height=50)

btnClearM = tk.Button(window, text="Clear", command=clearData_Main, cursor='hand2')


btnClearM.place(x=1200, y=160, width=125, height=50)

# ===============================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()

# This creates the navigation bar itself and it's buttons.


btnframe = Frame(window, bd=4, relief=RIDGE, pady=10)
btnframe.place(x=1200, y=550)

btn_menu = tk.Button(btnframe, text='Main menu', command=mainmenu_wind, cursor='hand2')


btn_menu.grid(row=0, column=0)

btn_appoint = tk.Button(btnframe, text='Make An Appointment', command=makeappointment_wind, cursor='hand2')


btn_appoint.grid(row=0, column=1)

btn_calculator = tk.Button(btnframe, text='Income calculator', command=income_calc_wind, cursor='hand2')


btn_calculator.grid(row=0, column=2)

btn_stock = tk.Button(btnframe, text='Stock management', command=stock_management_wind, cursor='hand2')


btn_stock.grid(row=0, column=3)

btn_logout = tk.Button(btnframe, text='Log-out', command=Exit, cursor='hand2')


btn_logout.grid(row=0, column=4)

Stock management window:


# ======================================Stock management window============================================
def stock_management():
# Those are the stock management functions which will perform actions on the stock table's report and on the
window itself:
# This function allows the reset button to clear the page and bring all of the selected entry boxes back to "0"
on click.
def clear():
textReport.delete(1.0, END)
e_Gspray.set('0')
e_Hgel.set('0')
e_Wax.set('0')
e_pomade.set('0')
e_clay.set('0')
e_Hlotion.set('0')
e_Beard_oil.set('0')
e_Hregrowth.set('0')
e_Beard_and_skin.set('0')

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)

# This defines variables to be later assigned to each checkbox as their variable.


var1.set(0)
var2.set(0)
var3.set(0)
var4.set(0)
var5.set(0)
var6.set(0)
var7.set(0)
var8.set(0)
var9.set(0)
var10.set(0)
var11.set(0)
var12.set(0)
var13.set(0)
var14.set(0)
var15.set(0)
var16.set(0)
var17.set(0)
var18.set(0)
var19.set(0)
var20.set(0)
var21.set(0)
var22.set(0)
var23.set(0)
var24.set(0)
var25.set(0)
var26.set(0)
var27.set(0)

costofFemaleproductsvar.set('')
costofMaleproductsvar.set('')
costofEssentialsvar.set('')
subtotalvar.set('')
salesubtotalvar.set('')
Numofproductsvar.set('')

# This function allows the report to be saved as a text file on click.


def save():
if textReport.get(1.0, END) == '\n':
pass
else:
url = filedialog.asksaveasfile(mode='w',
defaultextension='.txt') # This automatically saves the report as a text
file.
if url == None:
pass
else:
report_data = textReport.get(1.0, END)
url.write(report_data)
url.close()
messagebox.showinfo('Success', 'Your report has been successfully saved!')

# 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')

textReport.insert(END, f'Total Purchase Price\t\t\t£{subtotalofItems}\n\n')


textReport.insert(END, f'No. of products in stock\t\t\t{Numofproduct}\n\n')
textReport.insert(END, f'Total Selling Price\t\t\t£{salesubtotalofItems}\n\n')
textReport.insert(END, '=========================================\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 calculates the total number of products in stock.


Totalnumber = (item1 * 1) + (item2 * 1) + (item3 * 1) + (item4 * 1) + (item5 * 1) + (item6 * 1) + (
item7 * 1) \
+ (item8 * 1) + (item9 * 1) + (item10 * 1) + (item11 * 1) + (item12 * 1) + (item13 * 1) +
(
item14 * 1) + (item15 * 1) \
+ (item16 * 1) + (item17 * 1) + (item18 * 1) + (item19 * 1) + (item20 * 1) + (item21 * 1)
+ (
item22 * 1) + (item23 * 1) + (item24 * 1) \
+ (item25 * 1) + (item26 * 1) + (item27 * 1)

# 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))

subtotalofItems = priceofProductsM + priceofProductsF + priceofEssentials


subtotalvar.set('£ ' + str(subtotalofItems))

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!')

# ==========================Functions for the selection of stock products====================================


# The stock management window works by having all of the products disabled and then the user can select which
products are in stock
# by clicking on them and entering a number of products, this is what those functions are for, to determine
whether or not the user
# has or has not selected a certain product by keeping it "DISABLED" unless clicked on.

# 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')

# ==============================Stock management window creation=======================================


# This creates the stock management window itself, as well as it's size, title and title label of the window
window = Toplevel()

window.geometry('1765x690+0+0')

window.resizable(0, 0)

window.title('Stock management system')

window.config()

topFrame = Frame(window, bd=10, relief=RIDGE)


topFrame.pack(side=TOP)

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)

costFrame = Frame(productFrame, bd=4, relief=RIDGE, pady=10)


costFrame.pack(side=BOTTOM)

maleFrame = LabelFrame(productFrame, text='Male products', font=('arial', 19, 'bold'), bd=10, relief=RIDGE,


fg='firebrick', )
maleFrame.pack(side=LEFT)

femaleFrame = LabelFrame(productFrame, text='Female products', font=('arial', 19, 'bold'), bd=10, relief=RIDGE,


fg='firebrick')
femaleFrame.pack(side=LEFT)

essentialsFrame = LabelFrame(productFrame, text='Essential products', font=('arial', 19, 'bold'), bd=10,


relief=RIDGE, fg='firebrick')
essentialsFrame.pack(side=LEFT)

rightFrame = Frame(window, bd=15, relief=RIDGE, bg='firebrick')


rightFrame.place(x=1200, y=90)

reportFrame = Frame(rightFrame, bd=4, relief=RIDGE, bg='firebrick')


reportFrame.pack()

buttonFrame = Frame(rightFrame, bd=3, relief=RIDGE, bg='firebrick')


buttonFrame.pack()

# ===========================Variables defined as integer variables for later use===========================


# Each variable represents a product that exists in the stock system and they are defined as Integer variables
# so that the system may know when they are selected

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()

# =============================Pre-set values at 0 for the stock management window==========================


# This code just sets all of the entry boxes of the system to "0" by default when the window is opened.
e_Hgel.set('0')
e_Gspray.set('0')
e_Wax.set('0')
e_pomade.set('0')
e_clay.set('0')
e_Hlotion.set('0')
e_Beard_oil.set('0')
e_Beard_and_skin.set('0')
e_Hregrowth.set('0')

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')

# ====================Male product check-buttons placement===============================================


# This code creates the male section product check buttons which are all assigned to their own entry field.
Hgel = Checkbutton(maleFrame, text='Hair gel', font=('arial', 18, 'bold'), onvalue=1, offvalue=0, variable=var1
, command=Hgel)
Hgel.grid(row=0, column=0, sticky=W)

Gspray = Checkbutton(maleFrame, text='Grooming spray', font=('arial', 18, 'bold'), onvalue=1, offvalue=0,


variable=var2
, command=Gspray)
Gspray.grid(row=1, column=0, sticky=W)

pomade = Checkbutton(maleFrame, text='Pomade', font=('arial', 18, 'bold'), onvalue=1, offvalue=0, variable=var3


, command=pomade)
pomade.grid(row=2, column=0, sticky=W)

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)

Hlotion = Checkbutton(maleFrame, text='Hair lotion', font=('arial', 18, 'bold'), onvalue=1, offvalue=0,


variable=var6
, command=Hlotion)
Hlotion.grid(row=5, column=0, sticky=W)

Beard_oil = Checkbutton(maleFrame, text='Beard oil', font=('arial', 18, 'bold'), onvalue=1, offvalue=0,


variable=var7,
command=Beard_oil)
Beard_oil.grid(row=6, 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)

# ====================Entry fields for male products================================================


# This creates the entry fields in which the number of products will be inputted after their checkbox has been
clicked
textHgel = Entry(maleFrame, font=('arial', 18, 'bold'), bd=7, width=6, state=DISABLED, textvariable=e_Hgel)
textHgel.grid(row=0, column=1)

textGspray = Entry(maleFrame, font=('arial', 18, 'bold'), bd=7, width=6, state=DISABLED, textvariable=e_Gspray)


textGspray.grid(row=1, column=1)

textpomade = Entry(maleFrame, font=('arial', 18, 'bold'), bd=7, width=6, state=DISABLED, textvariable=e_pomade)


textpomade.grid(row=2, column=1)

textWax = Entry(maleFrame, font=('arial', 18, 'bold'), bd=7, width=6, state=DISABLED, textvariable=e_Wax)


textWax.grid(row=3, column=1)
textclay = Entry(maleFrame, font=('arial', 18, 'bold'), bd=7, width=6, state=DISABLED, textvariable=e_clay)
textclay.grid(row=4, column=1)

textHlotion = Entry(maleFrame, font=('arial', 18, 'bold'), bd=7, width=6, state=DISABLED,


textvariable=e_Hlotion)
textHlotion.grid(row=5, column=1)

textBeard_oil = Entry(maleFrame, font=('arial', 18, 'bold'), bd=7, width=6, state=DISABLED,


textvariable=e_Beard_oil)
textBeard_oil.grid(row=6, column=1)

textHregrowth = Entry(maleFrame, font=('arial', 18, 'bold'), bd=7, width=6, state=DISABLED,


textvariable=e_Hregrowth)
textHregrowth.grid(row=7, column=1)

textBeard_and_skin = Entry(maleFrame, font=('arial', 18, 'bold'), bd=7, width=6, state=DISABLED,


textvariable=e_Beard_and_skin)
textBeard_and_skin.grid(row=8, column=1)

# ==============================Female products check boxes======================================


# This code creates the female section product checkboxes which are all assigned to their own entry field.
Reconstructing_Shampoo = Checkbutton(femaleFrame, text='Reconstructing shampoo', font=('arial', 18, 'bold'),
onvalue=1, offvalue=0, variable=var10
, command=Reconstructing_Shampoo)
Reconstructing_Shampoo.grid(row=0, column=0, sticky=W)

Htreatment = Checkbutton(femaleFrame, text='Hair treatment', font=('arial', 18, 'bold'), onvalue=1, offvalue=0,


variable=var11
, command=Htreatment)
Htreatment.grid(row=1, column=0, sticky=W)

conditioner = Checkbutton(femaleFrame, text='Conditioner', font=('arial', 18, 'bold'), onvalue=1, offvalue=0,


variable=var12
, command=conditioner)
conditioner.grid(row=2, column=0, sticky=W)

WhippedCream = Checkbutton(femaleFrame, text='Whipped cream', font=('arial', 18, 'bold'), onvalue=1, offvalue=0,


variable=var13
, command=WhippedCream)
WhippedCream.grid(row=3, column=0, sticky=W)

HydratingCream = Checkbutton(femaleFrame, text='Hydrating cream', font=('arial', 18, 'bold'), onvalue=1,


offvalue=0,
variable=var14
, command=HydratingCream)
HydratingCream.grid(row=4, column=0, sticky=W)

moisturiser = Checkbutton(femaleFrame, text='Moisturiser', font=('arial', 18, 'bold'), onvalue=1, offvalue=0,


variable=var15
, command=moisturiser)
moisturiser.grid(row=5, column=0, sticky=W)

moisture_shampoo = Checkbutton(femaleFrame, text='Moisturiser Shampoo', font=('arial', 18, 'bold'), onvalue=1,


offvalue=0, variable=var16
, command=moisture_shampoo)
moisture_shampoo.grid(row=6, column=0, sticky=W)

Moroccanoil = Checkbutton(femaleFrame, text='Moroccan oil', font=('arial', 18, 'bold'), onvalue=1, offvalue=0,


variable=var17
, command=Moroccanoil)
Moroccanoil.grid(row=7, column=0, sticky=W)

HairDye = Checkbutton(femaleFrame, text='Hair Dye', font=('arial', 18, 'bold'), onvalue=1, offvalue=0,


variable=var18
, command=HairDye)
HairDye.grid(row=8, column=0, sticky=W)

# =======================Entry fields for female products==============================================


# This creates the entry fields in which the number of products will be inputted after their checkbox has been
clicked
textReconstructing_Shampoo = Entry(femaleFrame, font=('arial', 18, 'bold'), bd=7, width=6, state=DISABLED,
textvariable=e_Reconstructing_Shampoo)
textReconstructing_Shampoo.grid(row=0, column=1)

textHtreatment = Entry(femaleFrame, font=('arial', 18, 'bold'), bd=7, width=6, state=DISABLED,


textvariable=e_Htreatment)
textHtreatment.grid(row=1, column=1)
textconditioner = Entry(femaleFrame, font=('arial', 18, 'bold'), bd=7, width=6, state=DISABLED,
textvariable=e_conditioner)
textconditioner.grid(row=2, column=1)

textWhippedCream = Entry(femaleFrame, font=('arial', 18, 'bold'), bd=7, width=6, state=DISABLED,


textvariable=e_WhippedCream)
textWhippedCream.grid(row=3, column=1)

textHydratingCream = Entry(femaleFrame, font=('arial', 18, 'bold'), bd=7, width=6, state=DISABLED,


textvariable=e_HydratingCream)
textHydratingCream.grid(row=4, column=1)

textmoisturiser = Entry(femaleFrame, font=('arial', 18, 'bold'), bd=7, width=6, state=DISABLED,


textvariable=e_moisturiser)
textmoisturiser.grid(row=5, column=1)

textmoisture_shampoo = Entry(femaleFrame, font=('arial', 18, 'bold'), bd=7, width=6, state=DISABLED,


textvariable=e_moisture_shampoo)
textmoisture_shampoo.grid(row=6, column=1)

textMoroccanoil = Entry(femaleFrame, font=('arial', 18, 'bold'), bd=7, width=6, state=DISABLED,


textvariable=e_Moroccanoil)
textMoroccanoil.grid(row=7, column=1)

textHairDye = Entry(femaleFrame, font=('arial', 18, 'bold'), bd=7, width=6, state=DISABLED,


textvariable=e_HairDye)
textHairDye.grid(row=8, column=1)

# ===============================Essential products checkboxes=========================================


# This code creates the essential section product checkboxes which are all assigned to their own entry field.
argan_oil = Checkbutton(essentialsFrame, text='Argan oil', font=('arial', 18, 'bold'), onvalue=1, offvalue=0,
variable=var19
, command=argan_oil)
argan_oil.grid(row=0, column=0, sticky=W)

curl_cream = Checkbutton(essentialsFrame, text='Curl cream', font=('arial', 18, 'bold'), onvalue=1, offvalue=0,


variable=var20
, command=curl_cream)
curl_cream.grid(row=1, column=0, sticky=W)
Hair_food = Checkbutton(essentialsFrame, text='Hair food', font=('arial', 18, 'bold'), onvalue=1, offvalue=0,
variable=var21
, command=Hair_food)
Hair_food.grid(row=2, column=0, sticky=W)

comb = Checkbutton(essentialsFrame, text='Comb', font=('arial', 18, 'bold'), onvalue=1, offvalue=0,


variable=var22
, command=comb)
comb.grid(row=3, column=0, sticky=W)

tesxturising_cream = Checkbutton(essentialsFrame, text='Tesxturising cream', font=('arial', 18, 'bold'),


onvalue=1,
offvalue=0, variable=var23
, command=tesxturising_cream)
tesxturising_cream.grid(row=4, column=0, sticky=W)

Essential_oil = Checkbutton(essentialsFrame, text='Essential oil', font=('arial', 18, 'bold'), onvalue=1,


offvalue=0, variable=var24
, command=Essential_oil)
Essential_oil.grid(row=5, column=0, sticky=W)

anti_hairloss = Checkbutton(essentialsFrame, text='Anti-hairloss oil', font=('arial', 18, 'bold'), onvalue=1,


offvalue=0, variable=var25
, command=anti_hairloss)
anti_hairloss.grid(row=6, column=0, sticky=W)

grooming_cream = Checkbutton(essentialsFrame, text='Grooming cream', font=('arial', 18, 'bold'), onvalue=1,


offvalue=0, variable=var26
, command=grooming_cream)
grooming_cream.grid(row=7, column=0, sticky=W)

Care_bundle = Checkbutton(essentialsFrame, text='Hair-Care bundle', font=('arial', 18, 'bold'), onvalue=1,


offvalue=0, variable=var27
, command=Care_bundle)
Care_bundle.grid(row=8, column=0, sticky=W)

# ============================Entry fields for essentials=======================================================


# This creates the entry fields in which the number of products will be inputted after their checkbox has been
clicked
textargan_oil = Entry(essentialsFrame, font=('arial', 18, 'bold'), bd=7, width=6, state=DISABLED,
textvariable=e_argan_oil)
textargan_oil.grid(row=0, column=1)

textcurl_cream = Entry(essentialsFrame, font=('arial', 18, 'bold'), bd=7, width=6, state=DISABLED,


textvariable=e_curl_cream)
textcurl_cream.grid(row=1, column=1)

textHair_food = Entry(essentialsFrame, font=('arial', 18, 'bold'), bd=7, width=6, state=DISABLED,


textvariable=e_Hair_food)
textHair_food.grid(row=2, column=1)

textcomb = Entry(essentialsFrame, font=('arial', 18, 'bold'), bd=7, width=6, state=DISABLED,


textvariable=e_comb)
textcomb.grid(row=3, column=1)

texttesxturising_cream = Entry(essentialsFrame, font=('arial', 18, 'bold'), bd=7, width=6, state=DISABLED,


textvariable=e_tesxturising_cream)
texttesxturising_cream.grid(row=4, column=1)

textEssential_oil = Entry(essentialsFrame, font=('arial', 18, 'bold'), bd=7, width=6, state=DISABLED,


textvariable=e_Essential_oil)
textEssential_oil.grid(row=5, column=1)

textanti_hairloss = Entry(essentialsFrame, font=('arial', 18, 'bold'), bd=7, width=6, state=DISABLED,


textvariable=e_anti_hairloss)
textanti_hairloss.grid(row=6, column=1)

textgrooming_cream = Entry(essentialsFrame, font=('arial', 18, 'bold'), bd=7, width=6, state=DISABLED,


textvariable=e_grooming_cream)
textgrooming_cream.grid(row=7, column=1)

textCare_bundle = Entry(essentialsFrame, font=('arial', 18, 'bold'), bd=7, width=6, state=DISABLED,


textvariable=e_Care_bundle)
textCare_bundle.grid(row=8, column=1)

# ===============================Costlabels & entry fields of stock management window======================


# This code creates the labels and entry fields of the entry fields which display some of the vital information
which will go in the report when the user clicks
# the "total" button.
labelCostofProductsM = Label(costFrame, text='Purchase cost of male products:', font=('arial', 16, 'bold'),
fg='black')
labelCostofProductsM.grid(row=0, column=0)

textCostofProductsM = Entry(costFrame, font=('arial', 16, 'bold'), bd=6, width=14, state='readonly',


textvariable=costofMaleproductsvar)
textCostofProductsM.grid(row=0, column=1, padx=41)

labelCostofProductsF = Label(costFrame, text='Purchase cost of female products:', font=('arial', 16, 'bold'),


fg='black')
labelCostofProductsF.grid(row=1, column=0)

textCostofProductsF = Entry(costFrame, font=('arial', 16, 'bold'), bd=6, width=14, state='readonly',


textvariable=costofFemaleproductsvar)
textCostofProductsF.grid(row=1, column=1, padx=41)

labelCostofEssentials = Label(costFrame, text='Purchase cost of essentials:', font=('arial', 16, 'bold'),


fg='black')
labelCostofEssentials.grid(row=2, column=0)

textCostofEssentials = Entry(costFrame, font=('arial', 16, 'bold'), bd=6, width=14, state='readonly',


textvariable=costofEssentialsvar)
textCostofEssentials.grid(row=2, column=1, padx=41)

labelSubTotal = Label(costFrame, text='Total stock purchase price:', font=('arial', 16, 'bold'), fg='black')
labelSubTotal.grid(row=0, column=2)

textSubTotal = Entry(costFrame, font=('arial', 16, 'bold'), bd=6, width=14, state='readonly',


textvariable=subtotalvar)
textSubTotal.grid(row=0, column=3, padx=41)

labelNumofproducts = Label(costFrame, text='No. of products in stock', font=('arial', 16, 'bold'), fg='black')


labelNumofproducts.grid(row=1, column=2)

textNumofproducts = Entry(costFrame, font=('arial', 16, 'bold'), bd=6, width=14, state='readonly',


textvariable=Numofproductsvar)
textNumofproducts.grid(row=1, column=3, padx=41)
labelTotalSale = Label(costFrame, text='Total selling price:', font=('arial', 16, 'bold'), fg='black')
labelTotalSale.grid(row=2, column=2)

textTotalSale = Entry(costFrame, font=('arial', 16, 'bold'), bd=6, width=14, state='readonly',


textvariable=salesubtotalvar)
textTotalSale.grid(row=2, column=3, padx=41)

# ===================================================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 saves the report as a text file.


btn_Save = Button(buttonFrame, text='Save', font=('arial', 14, 'bold'), fg='white', bg='firebrick', bd=3, padx=5
, command=save)
btn_Save.grid(row=0, column=2)

# This button generates the report in the report text box.


btn_Display = Button(buttonFrame, text='Display', font=('arial', 14, 'bold'), fg='white', bg='firebrick', bd=3,
padx=5
, command=report)
btn_Display.grid(row=0, column=1)

# 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===========================================

textReport = Text(reportFrame, font=('arial', 12, 'bold'), bd=3, width=48, height=23)


textReport.grid(row=0, column=0)
# ====================================Stock management navigation bar=================================
# those function enable the navigation bar buttons to perform their role of closing the current window and
opening each of their respective windows.
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 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)

btn_appoint = tk.Button(btnframe, text='Make An Appointment', command=makeappointment_wind, cursor='hand2')


btn_appoint.grid(row=0, column=1)

btn_appointment_details = tk.Button(btnframe, text='Appointment details', command=appointment_details_wind,


cursor='hand2')
btn_appointment_details.grid(row=0, column=2)
btn_income_calculator = tk.Button(btnframe, text='Income calculator', command=income_calculator_wind,
cursor='hand2')
btn_income_calculator.grid(row=0, column=3)

btn_logout = tk.Button(btnframe, text='Log-out', command=iExit, cursor='hand2')


btn_logout.grid(row=0, column=4)

window.mainloop()

Income calculator window:


# =========================Income calculator window===================================================
def income_calculator():
# This creates the actual income_calculator window.
window = Toplevel()
window.title("Calculator")
window.geometry('600x400')
titlelabel = Label(window, text="Income Calculator", 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 = Label(window, text="", font=('arial', 13, 'bold'))
bar_label.place(x=30, y=110)

# 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()

# ==========================================Calculator List box================================================


# This creates the lisbox in which the contents of the calculator's database will be displayed.
DataFrameC = LabelFrame(window, bd=1, width=500, height=250, padx=20, pady=3, relief=RIDGE, bg="Ghost White",
font=('arial', 15, 'bold'), text="Income Details\n")
DataFrameC.place(x=10, y=520)

Calculatorlist = Listbox(DataFrameC, width=55, height=12, font=('arial', 15, 'bold'))


Calculatorlist.grid(row=0, column=0, padx=8)

# ========================Tax Labels and entry fields of the calculator window============================


# This creates the labels and entry fields in which the program automatically calculates and displays
# the national insurance and income taxes.
NI_label = Label(window, text="National insurance tax:", font=('arial', 13, 'bold'))
NI_label.place(x=1300, y=40)

Income_label = Label(window, text="Income tax:", font=('arial', 13, 'bold'))


Income_label.place(x=1300, y=150)

sign_label = Label(window, text="£", font=('arial', 13, 'bold'))


sign_label.place(x=1275, y=80)

sign_label = Label(window, text="£", font=('arial', 13, 'bold'))


sign_label.place(x=1275, y=190)

NI_Entry = Entry(window, textvariable=NI_tax)


NI_Entry.place(x=1300, y=80, width=300, height=30)

Income_tax_entry = Entry(window, textvariable=Income_tax)


Income_tax_entry.place(x=1300, y=190, width=300, height=30)

# ==========================Functions for calculations and database data manipulation==========================


# This creates the function which will be used to clear the Gross income entry field on click of it's button.
def btnClearDisplay():
Result.set("")

# 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 lists all of the records in the database on demand.


def DisplayInfo_Calc():
Calculatorlist.delete(0, END)
for row in calcDatabase_Backend.viewCalcData():
Calculatorlist.insert(END, "%d, |%s| |£%s| |£%s|" % row)

# 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

if 185 < percent < 967:


tkinter.messagebox.showinfo("National insurance tax",
"There is a national insurance tax of 12% percent over any weekly
income that surpases £184.")
Profit.set(percent - ((percent - 184) / 100 * 12))
try:
NI_Entry.insert(END, round(((percent - 184) / 100 * 12), 2))
Income_tax_entry.insert(END, "N/A")

except IOError:
pass

if percent > 968:


tkinter.messagebox.showinfo("National insurance tax",
"There is a national insurance tax of 12% percent on the first £783
and then 2% on aeverything after that.")
Profit.set(percent - ((percent - 967) / 100 * 2) - (783 / 100 * 12))
try:
# round() is used to round the outputted sum to 2 decimal places so that the output values
can be accurate to the penny.
NI_Entry.insert(END, round(((percent - 967) / 100 * 2) + (783 / 100 * 12), 2))
Income_tax_entry.insert(END, "N/A")
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.

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

if 6516 < (percent - 1000) < 9568:


tkinter.messagebox.showinfo("Class 2",
"You are in the Class 2 National insurance bracket due to your
earnings and as such only £159 are required for your national insurance tax.")
Profit.set(percent - 159)
try:
NI_Entry.insert(END, "159")
Income_tax_entry.insert(END, "N/A")

except IOError:
pass

if 9569 < (percent - 1000) < 50270:


tkinter.messagebox.showinfo("Class 2 & Class 4",
"You are in the Class 4 National insurance bracket due to your
earnings and Class 2 must also be applied. And as such £159 will be or the class 2 and 9% of everything over £9567
will also be taxed.")
Profit.set((percent - ((percent - 1000 - 9568) / 100 * 9) - 159))
try:
NI_Entry.insert(END, round(((percent - 1000 - 9568) / 100 * 9) + (159), 2))
Income_tax_entry.insert(END, "N/A")

except IOError:
pass

if 50271 < (percent - 1000) < 150000:


tkinter.messagebox.showinfo("Class 2 & Class 4",
"You are in the Class 4 National insurance bracket due to your
earnings and Class 2 must also be applied. Everything over £50,270 must be taxed at 2%.")
Profit.set(percent - ((percent - 1000 - 50270) / 100 * 2) - (40702 / 100 * 9) - 159)
try:
NI_Entry.insert(END, round(((percent - 1000 - 50270) / 100 * 2) + (40702 / 100 * 9) + (159),
2))
Income_tax_entry.insert(END, "N/A")

except IOError:
pass

if (percent - 1000) > 150001:


tkinter.messagebox.showinfo("Class 2 & Class 4",
"You are in the Class 4 National insurance bracket due to your
earnings and Class 2 must also be applied. Everything over £50,270 must be taxed at 2%.")
Profit.set(percent - ((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, "N/A")

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.

if 0 < (percent - 1000) < 12570:


tkinter.messagebox.showinfo("No tax",
"There is no tax on this income as any income under £12570 does not
get taxed.")
Profit.set(percent)
try:
NI_Entry.insert(END, "N/A")
Income_tax_entry.insert(END, "0.0")

except IOError:
pass

if 12571 < (percent - 1000) < 50270:


tkinter.messagebox.showinfo("Income tax",
"You must pay Income tax for this financial year at 20% anything
over £12571.")
Profit.set(percent - ((percent - 1000 - 12570) / 10 * 2))
try:
NI_Entry.insert(END, "N/A")
Income_tax_entry.insert(END, round(((percent - 1000 - 12570) / 10 * 2), 2))

except IOError:
pass

if 50271 < (percent - 1000) < 150000:


tkinter.messagebox.showinfo("Income tax",
"You must pay Income tax for this financial year at 20% anything of
£37700 and 40% of anything over £50271")
Profit.set(percent - ((percent - 50270) / 10 * 4) - (37700 / 5))
try:
NI_Entry.insert(END, "N/A")
Income_tax_entry.insert(END, round(((percent - 50270) / 10 * 4) + (37700 / 5), 2))

except IOError:
pass

if (percent - 1000) > 150001:


tkinter.messagebox.showinfo("Income tax",
"You must pay Income tax for this financial year at 40% of £99730,
20% for £37700 and 45% for anything else.")
Profit.set(percent - ((percent - 150000) / 100 * 45) - (112300 / 10 * 4) - (37700 / 5))
try:
NI_Entry.insert(END, "N/A")
Income_tax_entry.insert(END,
round(((percent - 150000) / 100 * 45) + (112300 / 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 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()

# ======================Gross income & Profit income labels and entry fields================================


# This creates the gross income label and entry field, which will display the calculations of the user for the
gross income.
label = Label(window, text="Gross Income", font=('arial', 18, 'bold'))
label.place(x=85, y=40)
gross_income = Entry(window, font=('arial', 20, 'bold'), textvariable=Result,
bd=30, insertwidth=4, bg="white", justify='center')
gross_income.place(x=10, y=80)

# 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)

profit_income = Entry(window, font=('arial', 20, 'bold'), textvariable=Profit,


bd=30, insertwidth=4, bg="white", justify='center')
profit_income.place(x=700, y=80)

# ============================Gross income display service income buttons=====================================


label = Label(window, text="Services Income:", font=('arial', 13, 'bold'))
label.place(x=20, y=175)
# This creates the service buttons which wen clicked display a preset value into the "Gross Income" entry field.
btn_Mhaircut = Button(window, text='Men Haircut', command=lambda: btnClick('+14'), cursor="hand2")
btn_Mhaircut.place(x=30, y=200, width=110, height=25)

btn_Whaircut = Button(window, text='Woman Haircut', command=lambda: btnClick('+25'), cursor="hand2")


btn_Whaircut.place(x=30, y=225, width=110, height=25)

btn_Khaircut = Button(window, text="Kid's Haircut", command=lambda: btnClick('+9'), cursor="hand2")


btn_Khaircut.place(x=30, y=250, width=110, height=25)
btn_Hcolour = Button(window, text='Hair Colouring', command=lambda: btnClick('+44'), cursor="hand2")
btn_Hcolour.place(x=30, y=275, width=110, height=25)

btn_extension = Button(window, text='Hair Extensions', command=lambda: btnClick('+30'), cursor="hand2")


btn_extension.place(x=30, y=300, width=110, height=25)

btn_perm = Button(window, text='Perms & relaxers', command=lambda: btnClick('+50'), cursor="hand2")


btn_perm.place(x=30, y=325, width=110, height=25)

btn_Fstyling = Button(window, text='Formal Styling', command=lambda: btnClick('+55'), cursor="hand2")


btn_Fstyling.place(x=30, y=350, width=110, height=25)

btn_Htreatment = Button(window, text='Hair Treatment', command=lambda: btnClick('+35'), cursor="hand2")


btn_Htreatment.place(x=30, y=375, width=110, height=25)

btn_fade = Button(window, text='Fade', command=lambda: btnClick('+22'), cursor="hand2")


btn_fade.place(x=30, y=400, width=110, height=25)

# ========================Gross income display product income buttons========================================


# This creates the product buttons which wen clicked display a preset value into the "Gross Income" entry field.
label = Label(window, text="Product income:", font=('arial', 13, 'bold'), cursor="hand2")
label.place(x=250, y=175)

btn_Hgel = Button(window, text='Hair Gel', command=lambda: btnClick('+6.50'), cursor="hand2")


btn_Hgel.place(x=180, y=200, width=110, height=25)

btn_pomade = Button(window, text='Pomade', command=lambda: btnClick('+30'), cursor="hand2")


btn_pomade.place(x=180, y=225, width=110, height=25)

btn_Hwax = Button(window, text="Hair Wax", command=lambda: btnClick('+16'), cursor="hand2")


btn_Hwax.place(x=180, y=250, width=110, height=25)

btn_Hclay = Button(window, text='Hair Clay', command=lambda: btnClick('+5'), cursor="hand2")


btn_Hclay.place(x=180, y=275, width=110, height=25)

btn_Hlotion = Button(window, text='Hair Lotion', command=lambda: btnClick('+9.50'), cursor="hand2")


btn_Hlotion.place(x=180, y=300, width=110, height=25)

btn_Beard_oil = Button(window, text='Beard Oil', command=lambda: btnClick('+20'), cursor="hand2")


btn_Beard_oil.place(x=180, y=325, width=110, height=25)

btn_Gspray = Button(window, text='Grooming spray', command=lambda: btnClick('+12'), cursor="hand2")


btn_Gspray.place(x=180, y=350, width=110, height=25)

btn_Hregrowth = Button(window, text='Hair regrowth oil', command=lambda: btnClick('+24'), cursor="hand2")


btn_Hregrowth.place(x=180, y=375, width=110, height=25)

btn_Beard_skin = Button(window, text='Beard & skin oil', command=lambda: btnClick('+19'), cursor="hand2")


btn_Beard_skin.place(x=180, y=400, width=110, height=25)

btn_Htreatment = Button(window, text='Hair treatment', command=lambda: btnClick('+8'), cursor="hand2")


btn_Htreatment.place(x=300, y=200, width=110, height=25)

btn_conditioner = Button(window, text='Conditioner', command=lambda: btnClick('+22.5'), cursor="hand2")


btn_conditioner.place(x=300, y=225, width=110, height=25)

btn_Wcream = Button(window, text="Whipped cream", command=lambda: btnClick('+18'), cursor="hand2")


btn_Wcream.place(x=300, y=250, width=110, height=25)

btn_Hcream = Button(window, text='Hydrating cream', command=lambda: btnClick('+37'), cursor="hand2")


btn_Hcream.place(x=300, y=275, width=110, height=25)

btn_Moisturiser = Button(window, text='Moisturiser', command=lambda: btnClick('+8'), cursor="hand2")


btn_Moisturiser.place(x=300, y=300, width=110, height=25)

btn_essential_oil = Button(window, text='Essential oil', command=lambda: btnClick('+7'), cursor="hand2")


btn_essential_oil.place(x=300, y=375, width=110, height=25)

btn_Morocan_oil = Button(window, text='Moroccan oil', command=lambda: btnClick('+17'), cursor="hand2")


btn_Morocan_oil.place(x=300, y=350, width=110, height=25)

btn_Hdye = Button(window, text='Hair dye', command=lambda: btnClick('+8'), cursor="hand2")


btn_Hdye.place(x=300, y=325, width=110, height=25)

btn_Comb = Button(window, text='Comb', command=lambda: btnClick('+13.50'), cursor="hand2")


btn_Comb.place(x=300, y=400, width=110, height=25)

btn_Cbundle = Button(window, text='Care bundle', command=lambda: btnClick('+40'), cursor="hand2")


btn_Cbundle.place(x=430, y=200, width=110, height=25)

btn_Gcream = Button(window, text='Grooming cream', command=lambda: btnClick('+15.25'), cursor="hand2")


btn_Gcream.place(x=430, y=225, width=110, height=25)

btn_Aoil = Button(window, text="Argan oil", command=lambda: btnClick('+14'), cursor="hand2")


btn_Aoil.place(x=430, y=250, width=110, height=25)

btn_Ccream = Button(window, text='Curl cream', command=lambda: btnClick('+7'), cursor="hand2")


btn_Ccream.place(x=430, y=275, width=110, height=25)

btn_Hfood = Button(window, text='Hair food', command=lambda: btnClick('+6.50'), cursor="hand2")


btn_Hfood.place(x=430, y=300, width=110, height=25)

btn_Mshampoo = Button(window, text='Moisturiser shampoo', command=lambda: btnClick('+9'), cursor="hand2")


btn_Mshampoo.place(x=430, y=375, width=140, height=25)

btn_Rshampoo = Button(window, text='Reconstructing shampoo', command=lambda: btnClick('+13.40'), cursor="hand2")


btn_Rshampoo.place(x=430, y=400, width=155, height=25)

btn_anti_hairloss = Button(window, text='Anti-hairloss oil', command=lambda: btnClick('+31'), cursor="hand2")


btn_anti_hairloss.place(x=430, y=325, width=110, height=25)

btn_Tcream = Button(window, text='Texturizing cream', command=lambda: btnClick('+8'), cursor="hand2")


btn_Tcream.place(x=430, y=350, width=140, height=25)

# =======================Database related entry fields================================================


# This creates the entry field for the Date interval.
Date_Interval_label = Label(window, text="Date Interval:", font=('arial', 13, 'bold'))
Date_Interval_label.place(x=10, y=450)
date_entryC = ttk.Entry(window, justify='center', textvariable=Date)
date_entryC.place(x=10, y=480)
date_entryC.focus()

# 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()

# ========================Basic calculator functions for the gross income entry field=====================


# This code creates the function buttons for the gross income.
btnClear = Button(window, font=('arial', 15, 'bold'),
text="C", command=btnClearDisplay, cursor="hand2").place(x=470, y=80)

Addition_btn = Button(window, font=('arial', 15, 'bold'),


text="+", command=lambda: btnClick("+"), cursor="hand2").place(x=430, y=80)

Multiplication_btn = Button(window, font=('arial', 15, 'bold'),


text="X", command=lambda: btnClick("*"), cursor="hand2").place(x=430, y=130)

btnEqual = Button(window, font=('arial', 15, 'bold'),


text="=", command=btnEqualInput, cursor="hand2").place(x=470, y=130)

# =======================Calculator database buttons========================================================


# This code creates the buttons of the income calculator.
btnAddDataC = tk.Button(window, text="Save Data", command=AddInfo_Calc, cursor="hand2")
btnAddDataC.place(x=685, y=730, width=125, height=50)

btnDisplayC = tk.Button(window, text="Display", command=DisplayInfo_Calc, cursor="hand2")


btnDisplayC.place(x=685, y=630, width=125, height=50)

btnDeleteC = tk.Button(window, text="Delete", command=DeleteCalcData, cursor="hand2")


btnDeleteC.place(x=685, y=680, width=125, height=50)

btnClear_Calc_Display = tk.Button(window, text="Clear", command=clearData_Calculator, cursor="hand2")


btnClear_Calc_Display.place(x=685, y=580, width=125, height=50)
# =============================Profit income calculator buttons================================================
# This code creates the 0-9 buttons for the profit income calculator.
one_btn = Button(window, font=('arial', 15, 'bold'),
text="1", command=lambda: btnClickProfit("1"), cursor="hand2").place(x=800, y=200)

two_btn = Button(window, font=('arial', 15, 'bold'),


text="2", command=lambda: btnClickProfit("2"), cursor="hand2").place(x=850, y=200)

three_btn = Button(window, font=('arial', 15, 'bold'),


text="3", command=lambda: btnClickProfit("3"), cursor="hand2").place(x=900, y=200)
four_btn = Button(window, font=('arial', 15, 'bold'),
text="4", command=lambda: btnClickProfit("4"), cursor="hand2").place(x=800, y=250)

five_btn = Button(window, font=('arial', 15, 'bold'),


text="5", command=lambda: btnClickProfit("5"), cursor="hand2").place(x=850, y=250)

six_btn = Button(window, font=('arial', 15, 'bold'),


text="6", command=lambda: btnClickProfit("6"), cursor="hand2").place(x=900, y=250)

seven_btn = Button(window, font=('arial', 15, 'bold'),


text="7", command=lambda: btnClickProfit("7"), cursor="hand2").place(x=800, y=300)

eight_btn = Button(window, font=('arial', 15, 'bold'),


text="8", command=lambda: btnClickProfit("8"), cursor="hand2").place(x=850, y=300)

nine_btn = Button(window, font=('arial', 15, 'bold'),


text="9", command=lambda: btnClickProfit("9"), cursor="hand2").place(x=900, y=300)

zero_btn = Button(window, font=('arial', 15, 'bold'),


text="0", command=lambda: btnClickProfit("0"), cursor="hand2").place(x=850, y=350)

# 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)

Subtraction_btn = Button(window, font=('arial', 15, 'bold'),


text="-", justify='center', command=lambda: btnClickProfit("-"), cursor="hand2").place(
x=950, y=250)

Multiplication_btn = Button(window, font=('arial', 15, 'bold'),


text="x", command=lambda: btnClickProfit("*"), cursor="hand2").place(x=1000, y=200)

Division_btn = Button(window, font=('arial', 15, 'bold'),


text="÷", command=lambda: btnClickProfit("/"), cursor="hand2").place(x=1000, y=250)

btnEqual = Button(window, font=('arial', 15, 'bold'),


text="=", command=btnEqualProfit, cursor="hand2").place(x=900, y=350)
btnClear = Button(window, font=('arial', 15, 'bold'),
text="C", command=btnClearDisplay2, cursor="hand2").place(x=800, y=350)

Income_NI_btn = Button(window, font=('arial', 15, 'bold'),


text="Income tax & NI", command=btnincometax_and_NI, cursor="hand2")
Income_NI_btn.place(x=1050, y=200, width=160)

Income_btn = Button(window, font=('arial', 15, 'bold'),


text="Income tax", command=btnincometax, cursor="hand2")
Income_btn.place(x=1050, y=250, width=160)

NI_btn = Button(window, font=('arial', 15, 'bold'),


text="Weekly NI tax", command=btnnationalinsurance, cursor="hand2")
NI_btn.place(x=1050, y=350, width=160)

YNI_btn = Button(window, font=('arial', 15, 'bold'),


text="Yearly NI tax", command=yearly_national_insurance, cursor="hand2")
YNI_btn.place(x=1050, y=300, width=160)

# 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()

# This creates the navigation bar itself and it's buttons.


btnframe = Frame(window, bd=4, relief=RIDGE, pady=10)
btnframe.place(x=1150, y=925)

btn_menu = tk.Button(btnframe, text='Main menu', command=mainmenu_wind, cursor='hand2')


btn_menu.grid(row=0, column=0)

btn_appoint = tk.Button(btnframe, text='Make An Appointment', command=makeappointment_wind, cursor='hand2')


btn_appoint.grid(row=0, column=1)

btn_appointment_details = tk.Button(btnframe, text='Appointment details', command=appointment_details_wind,


cursor='hand2')
btn_appointment_details.grid(row=0, column=2)

btn_stock = tk.Button(btnframe, text='Stock management', command=stock_management_wind, cursor='hand2')


btn_stock.grid(row=0, column=3)

btn_logout = tk.Button(btnframe, text='Log-out', command=iExit, cursor='hand2')


btn_logout.grid(row=0, column=4)

Admin main appointment window:


# ==================Customer & Service insertion section========================================
def Admin_appointment_wind():
# This code creates the title, size and style of the window.
window = Tk()
window.title("Appointment form")
window.geometry('1200x700')
titlelabel = Label(window, text="Admin Appointment Window", 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)

# ==========================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)

# =========================Functions for Customer details database=====================================


# This function allows for the Customer ID to be automatically generated by the system as a 6 digit number.
# And if the user clciks it more than once it clears the previous ID number and replaces it.
def generate_ID():
ID_entry.delete(0, tkinter.END)
service_ID.delete(0, tkinter.END)
ID_entry.delete(0, END)
x = random.randint(100000, 999999) # This sets the range of the possible number to be generated
randomnum = str(x)
ID_entry.insert(END,
randomnum) # This inserts the generated Id into the "Customer ID" entry box in the customer
section
service_ID.insert(END,
randomnum) # This inserts the generated Id into the "Customer ID" entry box in the
service section

# 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(""))

# ============================Adding and validating Customer records==========================================

# 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)

# This function validates and adds the data to the database.


def AddInfo():
# This is a presence check which ensures that nothing will even be validated unless all of the entry fields
are filled in.
# This is displayed through an error message.
if ID_entry.get() == "" or DOB.get() == "" or name_entry.get() == "" or phone_nr.get() == "" or
email_ad.get() == "" or cus_gender.get() == '':
tkinter.messagebox.showerror("Error", "Please make sure to fill in all of the entry fields!")
# If all of the entry fields have been completed then validation can occur, my validation technique utilises
return statements which ensure that
# if any entry fields are not valid then none of the data will be added to the database, this is done with
the following try/execute statements.
else:
name_style = r"^[a-zA-Z]+(([',. -][a-zA-Z ])?[a-zA-Z]*)*$"
if re.search(name_style, name_entry.get()):
answer.config(text="That is a valid name!")
else:
answer.config(text="That is not a valid name because it may contain integer characters!")
return

# 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.

# If the ID is not fully composed of integers this message will be displayed.


# and the return statemnt will be put in action.
except ValueError:
answer2.config(
text="That is not a valid ID entry because it contains other characters that are not integers!")
return

# 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()

answer2 = Label(CustomerValidationFrame, text="")


answer2.pack()

answer3 = Label(CustomerValidationFrame, text="")


answer3.pack()

answer4 = Label(CustomerValidationFrame, text="")


answer4.pack()

answer5 = Label(CustomerValidationFrame, text="")


answer5.pack()

answer6 = Label(CustomerValidationFrame, text="")


answer6.pack()

# ======================Entry boxes and Labels of Staff Appointment database=============================


# This creates the "Customer's DOB" entry box and label.
DOB_label = Label(window, text="Customer DOB:", font=('arial', 13, 'bold'))
DOB_label.place(x=30, y=180)
DOB = ttk.Entry(window, textvariable=CustomerDOB)
DOB.place(x=210, y=180)
DOB.focus()

# This creates the "Customer's name" entry box and label.


Customer_Name_label = Label(window, text="Customer's Name:", font=('arial', 13, 'bold'))
Customer_Name_label.place(x=30, y=60)
name_entry = ttk.Entry(window, textvariable=CustomerName)
name_entry.place(x=210, y=60)
name_entry.focus()

# This creates the "Customer's Id" entry box and label.


CustomerID_label = Label(window, text="Customer's ID:", font=('arial', 13, 'bold'))
CustomerID_label.place(x=30, y=100)
ID_entry = ttk.Entry(window, textvariable=CustomerID)
ID_entry.place(x=210, y=100)
ID_entry.focus()

# This creates the "Customer's gender" entry box and label


Gender_label = Label(window, text="Customer Gender:", font=('arial', 13, 'bold'))
Gender_label.place(x=30, y=140)
cus_gender = ttk.Entry(window, textvariable=gender)
cus_gender.place(x=210, y=140)
cus_gender.focus()

# 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()

# =============================Database Buttons for Customer window==========================


# Those lines of code create the buttons used for the Customer table.
btnAddData2 = tk.Button(window, text="Add Details", command=AddInfo, cursor='hand2')
btnAddData2.place(x=1300, y=110, width=175, height=50)

btnDelete2 = tk.Button(window, text="Delete Record", command=DeleteData, cursor='hand2')


btnDelete2.place(x=1300, y=60, width=175, height=50)

btnExit2 = tk.Button(window, text="Exit", command=iExit, cursor='hand2')


btnExit2.place(x=1150, y=110, width=125, height=50)

btnDisplay2 = tk.Button(window, text="Display", command=DisplayInfo, cursor='hand2')


btnDisplay2.place(x=1150, y=60, width=125, height=50)

btnClear2 = tk.Button(window, text="Clear", command=clearData, cursor='hand2')


btnClear2.place(x=1150, y=160, width=125, height=50)

btnSearch2 = tk.Button(window, text="Search record", command=search, cursor='hand2')


btnSearch2.place(x=1300, y=160, width=175, height=50)

# ==========================================Service details List box=================================


# This code creates the list box in which the Service saved data will be displayed.
DataFrame3 = LabelFrame(window, bd=1, width=00, height=250, padx=20, pady=3, relief=RIDGE, bg="Ghost White",
font=('arial', 15, 'bold'), text="Service Appointment Details\n")
DataFrame3.place(x=450, y=580)
Servicelist = Listbox(DataFrame3, width=56, height=12, font=('arial', 15, 'bold'))
Servicelist.grid(row=0, column=0, padx=8)

# ==============================Functions for service table===============================================

# Function to clear the entry fields when clicked.


def clearData_Service():
service_ID.delete(0, tkinter.END)
service_name.delete(0, tkinter.END)
Time_entry.delete(0, tkinter.END)
date_entry.delete(0, tkinter.END)
product_name.delete(0, tkinter.END)
service_price.delete(0, tkinter.END)

# 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.

answer12 = Label(ServiceValidationFrame, text="")


answer12.pack()

answer7 = Label(ServiceValidationFrame, text="")


answer7.pack()

answer8 = Label(ServiceValidationFrame, text="")


answer8.pack()
answer9 = Label(ServiceValidationFrame, text="")
answer9.pack()

answer10 = Label(ServiceValidationFrame, text="")


answer10.pack()

answer11 = Label(ServiceValidationFrame, text="")


answer11.pack()

# ======================Finnish appointment function ==========================================


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=5000, y=2000)
Principallist = Listbox(DataFrame, width=58, height=16, font=('arial', 15, 'bold'))
Principallist.grid(row=0, column=0, padx=8)

# 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()))

# ==========================Service entry fields and labels====================================================


# This creates the Customer's Id entry box and label in the service table
CustomerID_label = Label(window, text=" Customer's ID :", font=('arial', 13, 'bold'))
CustomerID_label.place(x=30, y=580)
service_ID = ttk.Entry(window, textvariable=ServiceID)
service_ID.place(x=210, y=580)
service_ID.focus()

# This creates the Service's name entry box and label.


Service_Name_label = Label(window, text="Service Name:", font=('arial', 13, 'bold'))
Service_Name_label.place(x=30, y=620)
service_name = ttk.Entry(window, textvariable=ServiceName)
service_name.place(x=210, y=620)
service_name.focus()

# This creates the service price entry box and label.


Price_label = Label(window, text="Service Price:", font=('arial', 13, 'bold'))
Price_label.place(x=30, y=660)
service_price = ttk.Entry(window, textvariable=ServicePrice)
service_price.place(x=210, y=660)
service_price.focus()

# This creates the Date of appointment entry box and label.


Appointment_date_label = Label(window, text="Date of appointment:", font=('arial', 13, 'bold'))
Appointment_date_label.place(x=30, y=700)
date_entry = ttk.Entry(window, textvariable=Date)
date_entry.place(x=210, y=700)
date_entry.focus()

# This creates the Date of appointment entry box and label.


Time_label = Label(window, text="Time of appointment:", font=('arial', 13, 'bold'))
Time_label.place(x=30, y=740)
Time_entry = ttk.Entry(window, textvariable=Time)
Time_entry.place(x=210, y=740)
Time_entry.focus()

# This creates the Extra products entry box and label.


Products_label = Label(window, text="Extra Products:", font=('arial', 13, 'bold'))
Products_label.place(x=30, y=780)
product_name = ttk.Entry(window, textvariable=ExtraProducts)
product_name.place(x=210, y=780)
product_name.focus()
# ======================Database Buttons for Appointment window========================================
# This button is used by the user to add the details to the services database, when this button is clicked
validation also takes place.
btnAddDataS = tk.Button(window, text="Add Details", command=AddInfo_Service, cursor='hand2')
btnAddDataS.place(x=1300, y=580, width=175, height=50)

# 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()

# This creates the navigation bar itself and it's buttons.


btnframe = Frame(window, bd=4, relief=RIDGE, pady=10)
btnframe.place(x=1150, y=925)

btn_menu = tk.Button(btnframe, text='Main menu', command=mainmenu_wind, cursor='hand2')


btn_menu.grid(row=0, column=0)

btn_appoint = tk.Button(btnframe, text='Appointment details', command=appointment_details_wind, cursor='hand2')


btn_appoint.grid(row=0, column=1)

btn_calculator = tk.Button(btnframe, text='Income calculator', command=income_calc_wind, cursor='hand2')


btn_calculator.grid(row=0, column=2)

btn_stock = tk.Button(btnframe, text='Stock management', command=stock_management_wind, cursor='hand2')


btn_stock.grid(row=0, column=3)

btn_logout = tk.Button(btnframe, text='Log-out', command=LExit, cursor='hand2')


btn_logout.grid(row=0, column=4)
window.mainloop()

Staff main menu window:


# ========================================Staff Menu=========================================================
def staff_menu():
# This creates the staff menu window itself.
window = Tk()
window.title("Staff main menu")
window.geometry('400x400+0+0')
window.resizable(0, 0)
window.config()
titlelabel = Label(window, text="Main menu for staff", font=('arial', 20, 'bold'), bg="black", fg="white")
titlelabel.pack(side=TOP, fill=X)

# ====================================Staff menu buttons and their functions=================================


# This creates the function to be used for the buttons which close the current window and redirect the user to
their desired window.
def logout():
result = messagebox.askyesno('Notification', 'Are you sure you want to log-out?')
if result > 0:
window.destroy()
else:
pass

def appointmentdetails_wind():
window.destroy()
Staff_customer()

def make_appointment_wind():
window.destroy()
Staff_appointment_wind()

# This creates the buttons to be used in the window.


btn_main_appoint = tk.Button(window, text='Appointment details', command=appointmentdetails_wind,
cursor='hand2')
btn_main_appoint.place(x=225, y=100, width=150, height=50)
btnappointment = tk.Button(window, text="Make appointment", command=make_appointment_wind, cursor='hand2')
btnappointment.place(x=45, y=100, width=150, height=50)

btnlogout = tk.Button(window, text="Log-out", command=logout, cursor='hand2')


btnlogout.place(x=135, y=200, width=150, height=50)

window.mainloop()

Amin main menu window:


# ==================================================Admin Menu=================================================
def admin_menu():
# This creates the staff menu window itself.
window = Tk()
window.title("Admin main menu")
window.geometry('400x400+0+0')
window.resizable(0, 0)
window.config()
titlelabel = Label(window, text="Main menu for admin", font=('arial', 20, 'bold'), bg="black", fg="white")
titlelabel.pack(side=TOP, fill=X)

# =======================Admin menu buttons and their functions=====================================


# This creates the function to be used for the buttons which close the current window and redirect the user to
their desired window.
def logout():
result = messagebox.askyesno('Notification', 'Are you sure you want to log-out?')
if result > 0:
window.destroy()
else:
pass

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)

btnCalculator = tk.Button(window, text="Income calculator", command=income_calc_wind, cursor='hand2')


btnCalculator.place(x=45, y=175, width=150, height=50)

btnstock = tk.Button(window, text="Stock management", command=stock_management_wind, cursor='hand2')


btnstock.place(x=225, y=175, width=150, height=50)

btnappointment = tk.Button(window, text="Make appointment", command=make_appointment_wind, cursor='hand2')


btnappointment.place(x=45, y=100, width=150, height=50)

btnlogout = tk.Button(window, text="Log-out", command=logout, cursor='hand2')


btnlogout.place(x=135, y=250, 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()

Service details 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 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()

Principal database annotated listing:


import sqlite3 #This imports the sqlite3 module to be used in the creation of the database.

#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()

Income calculator annotated listing:


import sqlite3 # This imports the sqlite3 module to be used in the creation of the database.

# 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()

You might also like