Database Applications: |
|
|
An auto repair application is one that allows an employee to enter information about the company's work orders of fixing cars. In most cases, there are parts used when fixing the cars. Besides these parts, there are some jobs performed on the car. It should be a good idea to list all parts that were used for the repair and all jobs that were performed on the car. In this example, we will create a database application that allows a car repair shop to register customers orders. If you haven't done so, install MSDE or Microsoft SQL Server.
|
The Data Source |
Before starting the application design, you should create an ODBC Data Source to connect to a SQL Server or MSDE database. Microsoft Windows (all versions) comes with an engine or wizard used to create this data source. Although there are various types of data sources you can use, one of the options provided by Microsoft is called ODBC, which stands for Open Database Connectivity. It is simply a driver or program you use to "translate" a database in a format that the needing programming environments can use. |
Practical Learning: Creating an ODBC Data Source |
|
Application Design |
The design of this application is classic: no complication, only a lot of work. We will create a control for each column we added in the SQL statement. There are a few concerns you would have to address. We create one column for an order date and another column for an order time. These can easily be handled by text boxes but data entry can be made user friendly is you use a date picker and a time picker controls. Remember that the lesser typing your users have to perform, the less worries you have. We also created two text boxes that would need multiple lines. You would just have to decide how much room each needs. The first text box allows an employee to describe the problem that the car was brought for. The other text box allows a user or the perform who fixed the car to provide some recommendations to the customer concerning the repair. |
Practical Learning: Creating the Application |
} #error Security Issue: The connection string may contain a password // The connection string below may contain plain text passwords and/or // other sensitive information. Please remove the #error after reviewing // the connection string for any security related issues. You may want to // store the password in some other form or use a different user authentication. |
Read that line and then comment it
Access the source file of the main frame and change it as follows:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) || !m_wndToolBar.LoadToolBar(IDR_MAINFRAME)) { TRACE0("Failed to create toolbar\n"); return -1; // fail to create } this->m_wndToolBar.SetWindowText(_T("Standard Toolbar")); if (!m_wndStatusBar.Create(this) || !m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT))) { TRACE0("Failed to create status bar\n"); return -1; // fail to create } // TODO: Delete these three lines if you don't want the toolbar to be dockable m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY); EnableDocking(CBRS_ALIGN_ANY); DockControlBar(&m_wndToolBar); this->CenterWindow(); this->SetWindowText(_T("College Park Auto-Shop - Repair Orders")); return 0; } BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) { if( !CFrameWnd::PreCreateWindow(cs) ) return FALSE; // TODO: Modify the Window class or styles here by modifying // the CREATESTRUCT cs cs.cx = 640; cs.cy = 600; cs.style &= ~FWS_ADDTOTITLE; return TRUE; } |
|
Data Controls Binding |
After designing the application and its controls, you can bind each control to the table's column that corresponds to its data. This is done differently in MSVC 6 and MSVC .NET |
Practical Learning: Binding Controls to Data: MSVC 6 |
Based on this, bind the controls as follows:
Identifier | Member Variable Name | Variable Type |
IDC_ADDRESS | m_Address | CString |
IDC_CITY | m_City | CString |
IDC_CUSTNAME | m_CustomerName | CString |
IDC_JOBPERFORMED1 | m_JobPerformed1 | CString |
IDC_JOBPERFORMED2 | m_JobPerformed2 | CString |
IDC_JOBPERFORMED3 | m_JobPerformed3 | CString |
IDC_JOBPERFORMED4 | m_JobPerformed4 | CString |
IDC_JOBPRICE1 | m_JobPrice1 | CString |
IDC_JOBPRICE2 | m_JobPrice2 | CString |
IDC_JOBPRICE3 | m_JobPrice3 | CString |
IDC_JOBPRICE4 | m_JobPrice4 | CString |
IDC_MAKE | m_Make | CString |
IDC_MODEL | m_Model | CString |
IDC_ORDERDATME | m_OrderDate | CTime |
IDC_ORDERTIME | m_OrderTime | CTime |
IDC_ORDERTOTAL | m_TotalOrder | CString |
IDC_PARTNAME1 | m_PartName1 | CString |
IDC_PARTNAME2 | m_PartName2 | CString |
IDC_PARTNAME3 | m_PartName3 | CString |
IDC_PARTNAME4 | m_PartName4 | CString |
IDC_PROBDESC | m_ProbDesc | CString |
IDC_QUANTITY1 | m_Quantity1 | CString |
IDC_QUANTITY2 | m_Quantity2 | CString |
IDC_QUANTITY3 | m_Quantity3 | CString |
IDC_QUANTITY4 | m_Quantity4 | CString |
IDC_RECOMMENDATIONS | m_Recommendations | CString |
IDC_STATE | m_State | CString |
IDC_SUBTOTAL1 | m_SubTotal1 | CString |
IDC_SUBTOTAL2 | m_SubTotal2 | CString |
IDC_SUBTOTAL3 | m_SubTotal3 | CString |
IDC_SUBTOTAL4 | m_SubTotal4 | CString |
IDC_TAXAMOUNT | m_TaxAmount | CString |
IDC_TAXRATE | m_TaxRate | CString |
IDC_TOTALLABOR | m_TotalLabor | CString |
IDC_TOTALPARTS | m_TotalParts | CString |
IDC_UNITPRICE1 | m_UnitPrice1 | CString |
IDC_UNITPRICE2 | m_UnitPrice2 | CString |
IDC_UNITPRICE3 | m_UnitPrice3 | CString |
IDC_UNITPRICE4 | m_UnitPrice4 | CString |
IDC_YEAR | CString | m_CarYear |
IDC_ZIPCODE | CString | m_ZIPCode |
// CPASView.cpp : implementation of the CCPASView class // #include "stdafx.h" #include "CPAS.h" #include "CPASSet.h" #include "CPASDoc.h" #include "CPASView.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CCPASView IMPLEMENT_DYNCREATE(CCPASView, CRecordView) BEGIN_MESSAGE_MAP(CCPASView, CRecordView) //{{AFX_MSG_MAP(CCPASView) //}}AFX_MSG_MAP // Standard printing commands ON_COMMAND(ID_FILE_PRINT, CRecordView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_DIRECT, CRecordView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_PREVIEW, CRecordView::OnFilePrintPreview) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CCPASView construction/destruction CCPASView::CCPASView() : CRecordView(CCPASView::IDD) { //{{AFX_DATA_INIT(CCPASView) m_pSet = NULL; //}}AFX_DATA_INIT } CCPASView::~CCPASView() { } void CCPASView::DoDataExchange(CDataExchange* pDX) { CRecordView::DoDataExchange(pDX); //{{AFX_DATA_MAP(CCPASView) DDX_FieldText(pDX, IDC_ADDRESS, m_pSet->m_Address, m_pSet); DDX_FieldText(pDX, IDC_CITY, m_pSet->m_City, m_pSet); DDX_FieldText(pDX, IDC_CUSTNAME, m_pSet->m_CustomerName, m_pSet); DDX_FieldText(pDX, IDC_JOBPERFORMED1, m_pSet->m_JobPerformed1, m_pSet); DDX_FieldText(pDX, IDC_JOBPERFORMED2, m_pSet->m_JobPerformed2, m_pSet); DDX_FieldText(pDX, IDC_JOBPERFORMED3, m_pSet->m_JobPerformed3, m_pSet); DDX_FieldText(pDX, IDC_JOBPERFORMED4, m_pSet->m_JobPerformed4, m_pSet); DDX_FieldText(pDX, IDC_JOBPRICE1, m_pSet->m_JobPrice1, m_pSet); DDX_FieldText(pDX, IDC_JOBPRICE2, m_pSet->m_JobPrice2, m_pSet); DDX_FieldText(pDX, IDC_JOBPRICE3, m_pSet->m_JobPrice3, m_pSet); DDX_FieldText(pDX, IDC_JOBPRICE4, m_pSet->m_JobPrice4, m_pSet); DDX_FieldText(pDX, IDC_MAKE, m_pSet->m_Make, m_pSet); DDX_FieldText(pDX, IDC_MODEL, m_pSet->m_Model, m_pSet); DDX_DateTimeCtrl(pDX, IDC_ORDERDATE, m_pSet->m_OrderDate); DDX_DateTimeCtrl(pDX, IDC_ORDERTIME, m_pSet->m_OrderTime); DDX_FieldText(pDX, IDC_ORDERTOTAL, m_pSet->m_TotalOrder, m_pSet); DDX_FieldText(pDX, IDC_PARTNAME1, m_pSet->m_PartName1, m_pSet); DDX_FieldText(pDX, IDC_PARTNAME2, m_pSet->m_PartName2, m_pSet); DDX_FieldText(pDX, IDC_PARTNAME3, m_pSet->m_PartName3, m_pSet); DDX_FieldText(pDX, IDC_PARTNAME4, m_pSet->m_PartName4, m_pSet); DDX_FieldText(pDX, IDC_PROBDESC, m_pSet->m_ProbDesc, m_pSet); DDX_FieldText(pDX, IDC_QUANTITY1, m_pSet->m_Quantity1, m_pSet); DDX_FieldText(pDX, IDC_QUANTITY2, m_pSet->m_Quantity2, m_pSet); DDX_FieldText(pDX, IDC_QUANTITY3, m_pSet->m_Quantity3, m_pSet); DDX_FieldText(pDX, IDC_QUANTITY4, m_pSet->m_Quantity4, m_pSet); DDX_FieldText(pDX, IDC_RECOMMENDATIONS, m_pSet->m_Recommendations, m_pSet); DDX_FieldText(pDX, IDC_STATE, m_pSet->m_State, m_pSet); DDX_FieldText(pDX, IDC_SUBTOTAL1, m_pSet->m_SubTotal1, m_pSet); DDX_FieldText(pDX, IDC_SUBTOTAL2, m_pSet->m_SubTotal2, m_pSet); DDX_FieldText(pDX, IDC_SUBTOTAL3, m_pSet->m_SubTotal3, m_pSet); DDX_FieldText(pDX, IDC_SUBTOTAL4, m_pSet->m_SubTotal4, m_pSet); DDX_FieldText(pDX, IDC_TAXAMOUNT, m_pSet->m_TaxAmount, m_pSet); DDX_FieldText(pDX, IDC_TAXRATE, m_pSet->m_TaxRate, m_pSet); DDX_FieldText(pDX, IDC_TOTALLABOR, m_pSet->m_TotalLabor, m_pSet); DDX_FieldText(pDX, IDC_TOTALPARTS, m_pSet->m_TotalParts, m_pSet); DDX_FieldText(pDX, IDC_UNITPRICE1, m_pSet->m_UnitPrice1, m_pSet); DDX_FieldText(pDX, IDC_UNITPRICE2, m_pSet->m_UnitPrice2, m_pSet); DDX_FieldText(pDX, IDC_UNITPRICE3, m_pSet->m_UnitPrice3, m_pSet); DDX_FieldText(pDX, IDC_UNITPRICE4, m_pSet->m_UnitPrice4, m_pSet); DDX_FieldText(pDX, IDC_YEAR, m_pSet->m_CarYear, m_pSet); DDX_FieldText(pDX, IDC_ZIPCODE, m_pSet->m_ZIPCode, m_pSet); //}}AFX_DATA_MAP } BOOL CCPASView::PreCreateWindow(CREATESTRUCT& cs) { return CRecordView::PreCreateWindow(cs); } void CCPASView::OnInitialUpdate() { m_pSet = &GetDocument()->m_cPASSet; CRecordView::OnInitialUpdate(); GetParentFrame()->RecalcLayout(); ResizeParentToFit(); } ///////////////////////////////////////////////////////////////////////////// // CCPASView printing BOOL CCPASView::OnPreparePrinting(CPrintInfo* pInfo) { // default preparation return DoPreparePrinting(pInfo); } void CCPASView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) { } void CCPASView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) { } ///////////////////////////////////////////////////////////////////////////// // CCPASView diagnostics #ifdef _DEBUG void CCPASView::AssertValid() const { CRecordView::AssertValid(); } void CCPASView::Dump(CDumpContext& dc) const { CRecordView::Dump(dc); } CCPASDoc* CCPASView::GetDocument() // non-debug version is inline { ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CCPASDoc))); return (CCPASDoc*)m_pDocument; } #endif //_DEBUG ///////////////////////////////////////////////////////////////////////////// // CCPASView database support CRecordset* CCPASView::OnGetRecordset() { return m_pSet; } ///////////////////////////////////////////////////////////////////////////// // CCPASView message handlers |
Practical Learning: Binding Controls to Data: MSVC .NET |
void CCPASView::DoDataExchange(CDataExchange* pDX) { CRecordView::DoDataExchange(pDX); // you can insert DDX_Field* functions here to 'connect' your controls to the database fields, ex. // DDX_FieldText(pDX, IDC_MYEDITBOX, m_pSet->m_szColumn1, m_pSet); // DDX_FieldCheck(pDX, IDC_MYCHECKBOX, m_pSet->m_bColumn2, m_pSet); // See MSDN and ODBC samples for more information DDX_DateTimeCtrl(pDX, IDC_ORDERDATE, m_pSet->m_OrderDate); DDX_DateTimeCtrl(pDX, IDC_ORDERTIME, m_pSet->m_OrderTime); DDX_FieldText(pDX, IDC_CUSTNAME, m_pSet->m_CustomerName, m_pSet); DDX_FieldText(pDX, IDC_ADDRESS, m_pSet->m_Address, m_pSet); DDX_FieldText(pDX, IDC_CITY, m_pSet->m_City, m_pSet); DDX_FieldText(pDX, IDC_STATE, m_pSet->m_State, m_pSet); DDX_FieldText(pDX, IDC_ZIPCODE, m_pSet->m_ZIPCode, m_pSet); DDX_FieldText(pDX, IDC_MAKE, m_pSet->m_Make, m_pSet); DDX_FieldText(pDX, IDC_MODEL, m_pSet->m_Model, m_pSet); DDX_FieldText(pDX, IDC_YEAR, m_pSet->m_CarYear, m_pSet); DDX_FieldText(pDX, IDC_PROBDESC, m_pSet->m_ProbDesc, m_pSet); DDX_FieldText(pDX, IDC_PARTNAME1, m_pSet->m_PartName1, m_pSet); DDX_FieldText(pDX, IDC_UNITPRICE1, m_pSet->m_UnitPrice1, m_pSet); DDX_FieldText(pDX, IDC_QUANTITY1, m_pSet->m_Quantity1, m_pSet); DDX_FieldText(pDX, IDC_SUBTOTAL1, m_pSet->m_SubTotal1, m_pSet); DDX_FieldText(pDX, IDC_PARTNAME2, m_pSet->m_PartName2, m_pSet); DDX_FieldText(pDX, IDC_UNITPRICE2, m_pSet->m_UnitPrice2, m_pSet); DDX_FieldText(pDX, IDC_QUANTITY2, m_pSet->m_Quantity2, m_pSet); DDX_FieldText(pDX, IDC_SUBTOTAL2, m_pSet->m_SubTotal2, m_pSet); DDX_FieldText(pDX, IDC_PARTNAME3, m_pSet->m_PartName3, m_pSet); DDX_FieldText(pDX, IDC_UNITPRICE3, m_pSet->m_UnitPrice3, m_pSet); DDX_FieldText(pDX, IDC_QUANTITY3, m_pSet->m_Quantity3, m_pSet); DDX_FieldText(pDX, IDC_SUBTOTAL3, m_pSet->m_SubTotal3, m_pSet); DDX_FieldText(pDX, IDC_PARTNAME4, m_pSet->m_PartName4, m_pSet); DDX_FieldText(pDX, IDC_UNITPRICE4, m_pSet->m_UnitPrice4, m_pSet); DDX_FieldText(pDX, IDC_QUANTITY4, m_pSet->m_Quantity4, m_pSet); DDX_FieldText(pDX, IDC_SUBTOTAL4, m_pSet->m_SubTotal4, m_pSet); DDX_FieldText(pDX, IDC_JOBPERFORMED1, m_pSet->m_JobPerformed1, m_pSet); DDX_FieldText(pDX, IDC_JOBPRICE1, m_pSet->m_JobPrice1, m_pSet); DDX_FieldText(pDX, IDC_JOBPERFORMED2, m_pSet->m_JobPerformed2, m_pSet); DDX_FieldText(pDX, IDC_JOBPRICE2, m_pSet->m_JobPrice2, m_pSet); DDX_FieldText(pDX, IDC_JOBPERFORMED3, m_pSet->m_JobPerformed3, m_pSet); DDX_FieldText(pDX, IDC_JOBPRICE3, m_pSet->m_JobPrice3, m_pSet); DDX_FieldText(pDX, IDC_JOBPERFORMED4, m_pSet->m_JobPerformed4, m_pSet); DDX_FieldText(pDX, IDC_JOBPRICE4, m_pSet->m_JobPrice4, m_pSet); DDX_FieldText(pDX, IDC_TOTALPARTS, m_pSet->m_TotalParts, m_pSet); DDX_FieldText(pDX, IDC_TOTALLABOR, m_pSet->m_TotalLabor, m_pSet); DDX_FieldText(pDX, IDC_TAXRATE, m_pSet->m_TaxRate, m_pSet); DDX_FieldText(pDX, IDC_TAXAMOUNT, m_pSet->m_TaxAmount, m_pSet); DDX_FieldText(pDX, IDC_ORDERTOTAL, m_pSet->m_TotalOrder, m_pSet); DDX_FieldText(pDX, IDC_RECOMMENDATIONS, m_pSet->m_Recommendations, m_pSet); } |
Order Related Calculations |
In this application, we would like the employee to enter the list of parts used, the prices of those parts, and the list of the jobs performed when fixing the car. All these results in values that we don't have while designing the application. Because of this, we created a button that can be used to perform the necessary calculations. When writing the SQL statement of the database, we included fields that in fact should be calculated. Of course this is a matter of opinion: some people would not make these columns part of the database, probably because they are calculated and therefore should remain external. |
Practical Learning: Performing Order-Related Calculations |
void CCPASView::OnBnClickedCalculate() { // TODO: Add your control notification handler code here double unitPrice1, unitPrice2, unitPrice3, unitPrice4; int quantity1, quantity2, quantity3, quantity4; double subTotal1, subTotal2, subTotal3, subTotal4; double jobPrice1, jobPrice2, jobPrice3, jobPrice4; double totalParts, totalLabor, taxRate, taxAmount, orderTotal; CString strUnitPrice1, strUnitPrice2, strUnitPrice3, strUnitPrice4; CString strQuantity1, strQuantity2, strQuantity3, strQuantity4; CString strJobPrice1, strJobPrice2, strJobPrice3, strJobPrice4; CString strTaxRate; // Retrieve the value entered in each unit price controls // of the Parts Used section GetDlgItemText(IDC_UNITPRICE1, strUnitPrice1); unitPrice1 = atof(strUnitPrice1); GetDlgItemText(IDC_UNITPRICE2, strUnitPrice2); unitPrice2 = atof(strUnitPrice2); GetDlgItemText(IDC_UNITPRICE3, strUnitPrice3); unitPrice3 = atof(strUnitPrice3); GetDlgItemText(IDC_UNITPRICE4, strUnitPrice4); unitPrice4 = atof(strUnitPrice4); // Retrieve the quantity entered for each part GetDlgItemText(IDC_QUANTITY1, strQuantity1); quantity1 = atoi(strQuantity1); GetDlgItemText(IDC_QUANTITY2, strQuantity2); quantity2 = atoi(strQuantity2); GetDlgItemText(IDC_QUANTITY3, strQuantity3); quantity3 = atoi(strQuantity3); GetDlgItemText(IDC_QUANTITY4, strQuantity4); quantity4 = atoi(strQuantity4); // Retrieve the price for each job GetDlgItemText(IDC_JOBPRICE1, strJobPrice1); jobPrice1 = atof(strJobPrice1); GetDlgItemText(IDC_JOBPRICE2, strJobPrice2); jobPrice2 = atof(strJobPrice2); GetDlgItemText(IDC_JOBPRICE3, strJobPrice3); jobPrice3 = atof(strJobPrice3); GetDlgItemText(IDC_JOBPRICE4, strJobPrice4); jobPrice4 = atof(strJobPrice4); // Get the tax rate of the current order GetDlgItemText(IDC_TAXRATE, strTaxRate); taxRate = atof(strTaxRate); // Calculate the sub-total of each part used // based on its unit price and quantitty subTotal1 = unitPrice1 * quantity1; subTotal2 = unitPrice2 * quantity2; subTotal3 = unitPrice3 * quantity3; subTotal4 = unitPrice4 * quantity4; // Caculate the total price of the parts used totalParts = subTotal1 + subTotal2 + subTotal3 + subTotal4; // Calculate the total amount of the jobs performed totalLabor = jobPrice1 + jobPrice2 + jobPrice3 + jobPrice4; // Add the total of parts and the total of labor // then calculate the amount of tax for this order taxAmount = (totalParts + totalLabor) * taxRate / 100; // Calculate the total amount of this repair job orderTotal = totalParts + totalLabor + taxAmount; CString strSubTotal1, strSubTotal2, strSubTotal3, strSubTotal4; // Before displaying the values, format each to display two decimals strSubTotal1.Format("%.2f", subTotal1); strSubTotal2.Format("%.2f", subTotal2); strSubTotal3.Format("%.2f", subTotal3); strSubTotal4.Format("%.2f", subTotal4); // Display the sub-totals in the Parts Used section SetDlgItemText(IDC_SUBTOTAL1, strSubTotal1); SetDlgItemText(IDC_SUBTOTAL2, strSubTotal2); SetDlgItemText(IDC_SUBTOTAL3, strSubTotal3); SetDlgItemText(IDC_SUBTOTAL4, strSubTotal4); CString strTotalParts, strTotalLabor, strTaxAmount, strOrderTotal; strTotalParts.Format("%.2f", totalParts); strTotalLabor.Format("%.2f", totalLabor); strTaxAmount.Format("%.2f", taxAmount); strOrderTotal.Format("%.2f", orderTotal); // Display the values of the Order Summary section SetDlgItemText(IDC_TOTALPARTS, strTotalParts); SetDlgItemText(IDC_TOTALLABOR, strTotalLabor); SetDlgItemText(IDC_TAXAMOUNT, strTaxAmount); SetDlgItemText(IDC_ORDERTOTAL, strOrderTotal); } |
void CCPASView::OnLostFocusQuantity1() { // TODO: Add your control notification handler code here OnBnClickedCalculate(); } |
void CCPASView::OnLostFocusQuantity2() { // TODO: Add your control notification handler code here OnBnClickedCalculate(); } |
void CCPASView::OnLostFocusQuantity3() { // TODO: Add your control notification handler code here OnBnClickedCalculate(); } |
void CCPASView::OnLostFocusQuantity4() { // TODO: Add your control notification handler code here OnBnClickedCalculate(); } |
void CCPASView::OnLostFocusJobPrice1() { // TODO: Add your control notification handler code here OnBnClickedCalculate(); } |
void CCPASView::OnLostFocusJobPrice2() { // TODO: Add your control notification handler code here OnBnClickedCalculate(); } |
void CCPASView::OnLostFocusJobPrice3() { // TODO: Add your control notification handler code here OnBnClickedCalculate(); } |
void CCPASView::OnLostFocusJobPrice4() { // TODO: Add your control notification handler code here OnBnClickedCalculate(); } |
Adding a Repair Order |
|
Deleting a Record |
|
|
||
Home | Copyright © 2005-2012, FunctionX, Inc. | |
|