MS Visual C++ MFC Examples:
Payroll Calculation

Introduction

In this application, we will calculate the overtime worked by an employee of a company. To do this, we will consider each week day with 8 regular hours. If an employee works more than 8 hours in one day, any time over that is considered overtime. In our payroll simulation, we will cover two weeks but each week has its separate calculation. After collecting the time for both weeks and performing the calculation, we will display the number of regular hours, the number of hours worked overtime, the amount pay for the regular hours, and the amount pay for overtime, if any. At the end, we will display the total net pay.

To assist the user with selecting the start and end dates of the payroll, we will use two date picker controls.

 

Practical Learning: Implement the Payroll Application

  1. Start a new MFC Application named Payroll2
  2. Create it as Dialog Based
  3. Design the dialog box as follows: 
     
    Control ID Caption Other Characteristics
    Group Box   Employee Identification  
    Static Text   Employee Name:  
    Edit Control IDC_EMPLNAME     
    Static Text   Hourly Salary  
    Edit Control IDC_HOURLYSALARY     
    Group Box   Time Range  
    Static Text   Start Date:  
    Date Time Picker IDC_STARTDATE   Format: Long Date
    Static Text   End Date:  
    Date Time Picker IDC_ENDDATE    Format: Long Date
    Group Box   Time Sheet  
    Static Text   Monday  
    Static Text   Tuesday  
    Static Text   Wednesday  
    Static Text   Thursday  
    Static Text   Friday  
    Static Text   Saturday  
    Static Text   Sunday  
    Static Text   First Week:  
    Edit Control IDC_MONDAY1   Align Text: Right
    Edit Control IDC_TUESDAY1   Align Text: Right
    Edit Control IDC_WEDNESDAY1   Align Text: Right
    Edit Control IDC_THURSDAY1   Align Text: Right
    Edit Control IDC_FRIDAY1   Align Text: Right
    Edit Control IDC_SATURDAY1   Align Text: Right
    Edit Control IDC_SUNDAY1   Align Text: Right
    Static Text   Second Week:  
    Edit Control IDC_MONDAY2   Align Text: Right
    Edit Control IDC_ TUESDAY2   Align Text: Right
    Edit Control IDC_WEDNESDAY2   Align Text: Right
    Edit Control IDC_ THURSDAY2   Align Text: Right
    Edit Control IDC_FRIDAY2   Align Text: Right
    Edit Control IDC_SATURDAY2   Align Text: Right
    Edit Control IDC_SUNDAY2   Align Text: Right
    Group Box   Payroll Processing  
    Button IDC_PROCESS_BTN Process It  
    Static Text   Hours  
    Static Text   Amount  
    Static Text   Regular:  
    Edit Control IDC_REGHOURS   Align Text: Right
    Edit Control IDC_REGAMOUNT   Align Text: Right
    Static Text   Net Pay:  
    Edit Control IDC_NETPAY   Align Text: Right
    Static Text   Overtime:  
    Edit Control IDC_OVTHOURS   Align Text: Right
    Edit Control IDC_OVTAMOUNT   Align Text: Right
  4. Save all
  5. Right-click each control on the dialog and click Add Variable
  6. Create the variables as follows:
     
    ID Control Variable Value Variable
    IDC_EMPLNAME   m_EmployeeName
    IDC_HOURLYSALARY   m_HourlySalary
    IDC_STARTDATE m_StartDate  
    IDC_ENDDATE m_EndDate  
    IDC_MONDAY1   m_Monday1
    IDC_TUESDAY1   m_Tuesday1
    IDC_WEDNESDAY1   m_Wednesday1
    IDC_THURSDAY1   m_Thursday1
    IDC_FRIDAY1   m_Friday1
    IDC_SATURDAY1   m_Saturday1
    IDC_SUNDAY1   m_Sunday1
    IDC_MONDAY2   m_Monday2
    IDC_TUESDAY2   m_Tuesday2
    IDC_WEDNESDAY2   m_Wednesday2
    IDC_THURSDAY2   m_Thursday2
    IDC_FRIDAY2   m_Friday2
    IDC_SATURDAY2   m_Saturday2
    IDC_SUNDAY2   m_Sunday2
    IDC_REGHOURS   m_RegHours
    IDC_REGAMOUNT   m_RegAmount
    IDC_NETPAY   m_NetPay
    IDC_OVTHOURS   m_OvtHours
    IDC_OVTAMOUNT   m_OvtAmount
  7. Access the OnInitDialog() event of the CtimeSheet1Dlg class and change it as follows:
     
    BOOL CPayroll2Dlg::OnInitDialog()
    {
    	CDialog::OnInitDialog();
    
    	// Set the icon for this dialog. The framework does this automatically
    	// when the application's main window is not a dialog
    	SetIcon(m_hIcon, TRUE); // Set big icon
    	SetIcon(m_hIcon, FALSE); // Set small icon
    
    	// TODO: Add extra initialization here
    	CTime today = CTime::GetCurrentTime();
    	CTimeSpan tmeSpan(14, 0, 0, 0); 
    	CTime twoWeeksAgo = today - tmeSpan;
    
    	this->m_StartDate.SetTime(&twoWeeksAgo);
    
    	return TRUE; // return TRUE unless you set the focus to a control
    }
  8. Return to the dialog box and click the top DateTimePicker control
  9. In the Properties window, click the Events button and double-click the DTN_CLOSEUP field
  10. Implement the event as follows:
     
    void CPayroll2Dlg::OnDtnCloseupStartdate(NMHDR *pNMHDR, LRESULT *pResult)
    {
    	// TODO: Add your control notification handler code here
    	*pResult = 0;
    
    	// Get the starting date
    	CTime dteStart;
    
    	this->m_StartDate.GetTime(dteStart);
    
    	// Find out if the user selected a day that is not Monday
    	if( dteStart.GetDayOfWeek() != 2 )
    	{
    		AfxMessageBox("The date you selected in invalid\n"
    		"The time period should start on a Monday");
    		this->m_StartDate.SetFocus();
    	}
    }
  11. Return to the dialog box and click the other DateTimePicker control
  12. In the Properties window, click the Events button and double-click the DTN_CLOSEUP field
  13. Implement the event as follows:
     
    void CPayroll2Dlg::OnDtnCloseupEnddate(NMHDR *pNMHDR, LRESULT *pResult)
    {
    	// TODO: Add your control notification handler code here
    	*pResult = 0;
    
    	CTime dteStart, dteEnd;
    
    	// Get the starting date
    	this->m_StartDate.GetTime(dteStart);
    	// Get the ending date
    	this->m_EndDate.GetTime(dteEnd);
    
    	// Make sure the first day of the period is Monday
    	if( dteStart.GetDayOfWeek() != 2 )
    	{
    		AfxMessageBox("The starting date you selected in invalid\n"
    			"The time period should start on a Monday");
    
    		this->m_StartDate.SetFocus();
    	}
    
    	// Find the number of days that separates the start and end
    	CTimeSpan timeDifference = dteEnd - dteStart; 
    	LONGLONG fourteenDaysLater = timeDifference.GetDays();
    
    	if( (dteEnd.GetDayOfWeek() != 1) || (fourteenDaysLater != 13) )
    	{
    		AfxMessageBox("The ending date you selected in invalid\n"
    			"The time period should span 2 weeks and end on a Sunday");
    
    		this->m_EndDate.SetFocus();
    	}
    }
  14. Return to the dialog box. Double-click the Process It button and implement its Click event as follows:
     
    // Payroll2Dlg.cpp : implementation file
    //
    
    #include "stdafx.h"
    #include "Payroll2.h"
    #include "Payroll2Dlg.h"
    #include ".\payroll2dlg.h"
    
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #endif
    
    
    // CPayroll2Dlg dialog
    
    
    
    CPayroll2Dlg::CPayroll2Dlg(CWnd* pParent /*=NULL*/)
    	: CDialog(CPayroll2Dlg::IDD, pParent)
    	, m_EmployeeName(_T(""))
    	, m_HourlySalary(_T("10.00"))
    	, m_Monday1(_T("0.00"))
    	, m_Tuesday1(_T("0.00"))
    	, m_Wednesday1(_T("0.00"))
    	, m_Thursday1(_T("0.00"))
    	, m_Friday1(_T("0.00"))
    	, m_Saturday1(_T("0.00"))
    	, m_Sunday1(_T("0.00"))
    	, m_Monday2(_T("0.00"))
    	, m_Tuesday2(_T("0.00"))
    	, m_Wednesday2(_T("0.00"))
    	, m_Thursday2(_T("0.00"))
    	, m_Friday2(_T("0.00"))
    	, m_Saturday2(_T("0.00"))
    	, m_Sunday2(_T("0.00"))
    	, m_RegHours(_T("0.00"))
    	, m_RegAmount(_T("0.00"))
    	, m_NetPay(_T("0.00"))
    	, m_OvtHours(_T("0.00"))
    	, m_OvtAmount(_T("0.00"))
    {
    	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
    }
    
    void CPayroll2Dlg::DoDataExchange(CDataExchange* pDX)
    {
    	CDialog::DoDataExchange(pDX);
    	DDX_Text(pDX, IDC_EMPLNAME, m_EmployeeName);
    	DDX_Text(pDX, IDC_HOURLYSALARY, m_HourlySalary);
    	DDX_Control(pDX, IDC_STARTDATE, m_StartDate);
    	DDX_Control(pDX, IDC_ENDDATE, m_EndDate);
    	DDX_Text(pDX, IDC_MONDAY1, m_Monday1);
    	DDX_Text(pDX, IDC_TUESDAY1, m_Tuesday1);
    	DDX_Text(pDX, IDC_WEDNESDAY1, m_Wednesday1);
    	DDX_Text(pDX, IDC_THURSDAY1, m_Thursday1);
    	DDX_Text(pDX, IDC_FRIDAY1, m_Friday1);
    	DDX_Text(pDX, IDC_SATURDAY1, m_Saturday1);
    	DDX_Text(pDX, IDC_SUNDAY1, m_Sunday1);
    	DDX_Text(pDX, IDC_MONDAY2, m_Monday2);
    	DDX_Text(pDX, IDC_TUESDAY2, m_Tuesday2);
    	DDX_Text(pDX, IDC_WEDNESDAY2, m_Wednesday2);
    	DDX_Text(pDX, IDC_THURSDAY2, m_Thursday2);
    	DDX_Text(pDX, IDC_FRIDAY2, m_Friday2);
    	DDX_Text(pDX, IDC_SATURDAY2, m_Saturday2);
    	DDX_Text(pDX, IDC_SUNDAY2, m_Sunday2);
    	DDX_Text(pDX, IDC_REGHOURS, m_RegHours);
    	DDX_Text(pDX, IDC_REGAMOUNT, m_RegAmount);
    	DDX_Text(pDX, IDC_NETPAY, m_NetPay);
    	DDX_Text(pDX, IDC_OVTHOURS, m_OvtHours);
    	DDX_Text(pDX, IDC_OVTAMOUNT, m_OvtAmount);
    	DDX_Control(pDX, IDC_HOURLYSALARY, m_WndHourlySalay);
    	DDX_Control(pDX, IDC_MONDAY1, m_WndMonday1);
    	DDX_Control(pDX, IDC_TUESDAY1, m_WndTuesday1);
    	DDX_Control(pDX, IDC_WEDNESDAY1, m_WndWednesday1);
    	DDX_Control(pDX, IDC_THURSDAY1, m_WndThursday1);
    	DDX_Control(pDX, IDC_FRIDAY1, m_WndFriday1);
    	DDX_Control(pDX, IDC_SATURDAY1, m_WndSaturday1);
    	DDX_Control(pDX, IDC_SUNDAY1, m_WndSunday1);
    	DDX_Control(pDX, IDC_MONDAY2, m_WndMonday2);
    	DDX_Control(pDX, IDC_TUESDAY2, m_WndTuesday2);
    	DDX_Control(pDX, IDC_WEDNESDAY2, m_WndWednesday2);
    	DDX_Control(pDX, IDC_THURSDAY2, m_WndThursday2);
    	DDX_Control(pDX, IDC_FRIDAY2, m_WndFriday2);
    	DDX_Control(pDX, IDC_SATURDAY2, m_WndSaturday2);
    	DDX_Control(pDX, IDC_SUNDAY2, m_WndSunday2);
    }
    
    BEGIN_MESSAGE_MAP(CPayroll2Dlg, CDialog)
    	ON_WM_PAINT()
    	ON_WM_QUERYDRAGICON()
    	//}}AFX_MSG_MAP
    	ON_NOTIFY(DTN_CLOSEUP, IDC_STARTDATE, OnDtnCloseupStartdate)
    	ON_NOTIFY(DTN_CLOSEUP, IDC_ENDDATE, OnDtnCloseupEnddate)
    	ON_BN_CLICKED(IDC_PROCESS_BTN, OnBnClickedProcessBtn)
    END_MESSAGE_MAP()
    
    
    // CPayroll2Dlg message handlers
    
    BOOL CPayroll2Dlg::OnInitDialog()
    {
    	CDialog::OnInitDialog();
    
    	// Set the icon for this dialog.  The framework does this automatically
    	//  when the application's main window is not a dialog
    	SetIcon(m_hIcon, TRUE);			// Set big icon
    	SetIcon(m_hIcon, FALSE);		// Set small icon
    
    	// TODO: Add extra initialization here
    	CTime today = CTime::GetCurrentTime();
    	CTimeSpan tmeSpan(14, 0, 0, 0); 
    	CTime twoWeeksAgo = today - tmeSpan;
    
    	this->m_StartDate.SetTime(&twoWeeksAgo);
    
    	return TRUE;  // return TRUE  unless you set the focus to a control
    }
    
    // If you add a minimize button to your dialog, you will need the code below
    //  to draw the icon.  For MFC applications using the document/view model,
    //  this is automatically done for you by the framework.
    
    void CPayroll2Dlg::OnPaint() 
    {
    	if (IsIconic())
    	{
    		CPaintDC dc(this); // device context for painting
    
    		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
    
    		// Center icon in client rectangle
    		int cxIcon = GetSystemMetrics(SM_CXICON);
    		int cyIcon = GetSystemMetrics(SM_CYICON);
    		CRect rect;
    		GetClientRect(&rect);
    		int x = (rect.Width() - cxIcon + 1) / 2;
    		int y = (rect.Height() - cyIcon + 1) / 2;
    
    		// Draw the icon
    		dc.DrawIcon(x, y, m_hIcon);
    	}
    	else
    	{
    		CDialog::OnPaint();
    	}
    }
    
    // The system calls this function to obtain the cursor to display while the user drags
    //  the minimized window.
    HCURSOR CPayroll2Dlg::OnQueryDragIcon()
    {
    	return static_cast<HCURSOR>(m_hIcon);
    }
    
    void CPayroll2Dlg::OnDtnCloseupStartdate(NMHDR *pNMHDR, LRESULT *pResult)
    {
    	// TODO: Add your control notification handler code here
    	*pResult = 0;
    	
    	// Get the starting date
    	CTime dteStart;
    	
    	this->m_StartDate.GetTime(dteStart);
    
    	// Find out if the user selected a day that is not Monday
    	if( dteStart.GetDayOfWeek() != 2 )
    	{
    		AfxMessageBox("The date you selected in invalid\n"
    			          "The time period should start on a Monday");
    		this->m_StartDate.SetFocus();
    	}
    }
    
    void CPayroll2Dlg::OnDtnCloseupEnddate(NMHDR *pNMHDR, LRESULT *pResult)
    {
    	// TODO: Add your control notification handler code here
    	*pResult = 0;
    
    	CTime dteStart, dteEnd;
    	
    	// Get the starting date
    	this->m_StartDate.GetTime(dteStart);
    	// Get the ending date
    	this->m_EndDate.GetTime(dteEnd);
    
    	// Make sure the first day of the period is Monday
    	if( dteStart.GetDayOfWeek() != 2 )
    	{
    		AfxMessageBox("The starting date you selected in invalid\n"
    			          "The time period should start on a Monday");
    
    		this->m_StartDate.SetFocus();
    	}
    
    	// Find the number of days that separates the start and end
    	CTimeSpan timeDifference = dteEnd - dteStart; 
    	LONGLONG fourteenDaysLater = timeDifference.GetDays();
    
    	if( (dteEnd.GetDayOfWeek() != 1) || (fourteenDaysLater != 13) )
    	{
    		AfxMessageBox("The ending date you selected in invalid\n"
    			          "The time period should span 2 weeks and end on a Sunday");
    
    		this->m_EndDate.SetFocus();
    	}
    }
    
    void CPayroll2Dlg::OnBnClickedProcessBtn()
    {
    	// TODO: Add your control notification handler code here
    	UpdateData();
    
    	double monday1 = 0.00, tuesday1 = 0.00, wednesday1 = 0.00,
    		   thursday1 = 0.00, friday1 = 0.00, saturday1 = 0.00,
    		   sunday1 = 0.00, monday2 = 0.00, tuesday2 = 0.00,
    		   wednesday2 = 0.00, thursday2 = 0.00, friday2 = 0.00,
    		   saturday2 = 0.00, sunday2 = 0.00;
    	double totalHoursWeek1, totalHoursWeek2;
    
    	double regHours1 = 0.00, regHours2 = 0.00, ovtHours1 = 0.00, ovtHours2 = 0.00;
    	double regAmount1 = 0.00, regAmount2 = 0.00, ovtAmount1 = 0.00, ovtAmount2 = 0.00;
    	double regularHours, overtimeHours;
    	double regularAmount, overtimeAmount, totalEarnings;
    	
    	double hourlySalary = 0.00;
    
    	// Retrieve the hourly salary
    	hourlySalary = atof(this->m_HourlySalary);
    	
    	monday1 = atof(this->m_Monday1);
    	tuesday1 = atof(this->m_Tuesday1);
    	wednesday1 = atof(this->m_Wednesday1);
    	thursday1 = atof(this->m_Thursday1);						  
    	friday1 = atof(this->m_Friday1);
    	saturday1 = atof(this->m_Saturday1);
    	sunday1 = atof(this->m_Sunday1);
    
    	monday2 = atof(this->m_Monday2);
    	tuesday2 = atof(this->m_Tuesday2);
    	wednesday2 = atof(this->m_Wednesday2);
    	thursday2 = atof(this->m_Thursday2);
    	friday2 = atof(this->m_Friday2);
    	saturday2 = atof(this->m_Saturday2);
    	sunday2 = atof(this->m_Sunday2);
    	
    	// Calculate the total number of hours for each week
    	totalHoursWeek1 = monday1 + tuesday1 + wednesday1 + thursday1 +
    		friday1 + saturday1 + sunday1;
    	totalHoursWeek2 = monday2 + tuesday2 + wednesday2 + thursday2 + 
    		friday2 + saturday2 + sunday2;
    
    	// The overtime is paid time and half
    	double ovtSalary = hourlySalary * 1.5;
    
    	// If the employee worked under 40 hours, there is no overtime
    	if( totalHoursWeek1 < 40 )
    	{
    		regHours1  = totalHoursWeek1;
    		regAmount1 = hourlySalary * regHours1;
    		ovtHours1  = 0.00;
    		ovtAmount1 = 0.00;
    	} // If the employee worked over 40 hours, calculate the overtime
    	else if( totalHoursWeek1 >= 40 )
    	{
    		regHours1  = 40;
    		regAmount1 = hourlySalary * 40;
    		ovtHours1  = totalHoursWeek1 - 40;
    		ovtAmount1 = ovtHours1 * ovtSalary;
    	}
    
    	if( totalHoursWeek2 < 40 )
    	{
    		regHours2  = totalHoursWeek2;
    		regAmount2 = hourlySalary * regHours2;
    		ovtHours2  = 0.00;
    		ovtAmount2 = 0.00;
    	}
    	else if( totalHoursWeek2 >= 40 )
    	{
    		regHours2  = 40;
    		regAmount2 = hourlySalary * 40;
    		ovtHours2  = totalHoursWeek2 - 40;
    		ovtAmount2 = ovtHours2 * ovtSalary;
    	}
    
    	regularHours   = regHours1  + regHours2;
    	overtimeHours  = ovtHours1  + ovtHours2;
    	regularAmount  = regAmount1 + regAmount2;
    	overtimeAmount = ovtAmount1 + ovtAmount2;
    	totalEarnings  = regularAmount + overtimeAmount;
    	
    	this->m_RegHours.Format("%.2f", regularHours);
    	this->m_OvtHours.Format("%.2f", overtimeHours);
    	this->m_RegAmount.Format("%.2f", regularAmount);
    	this->m_OvtAmount.Format("%.2f", overtimeAmount);
    
    	this->m_NetPay.Format("%.2f", totalEarnings);
    	UpdateData(FALSE);
    }
  15. Execute the application to test it
  16. After using it, close the dialog box and return to your programming environment
 

Copyright © 2004-2005 FunctionX, Inc.