Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 64 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
64
Dung lượng
434,7 KB
Nội dung
For each timecard, we keep track of how many hours were spent, what date they were used, and what the individual’s hourly rate is. This way, we can generate project- status information to be shown at the task and project levels. We also require that a description of the member’s work be stored in the timecard. This enables you to keep a running history of what has been accomplished by your team. Creating the Views We have two views that will help us aggregate billing and budget information for the task and project views. We do this to remove some of the more complex SQL code that would otherwise be required to get summary information, in addition to join to other tables. The first view we create sums up the hours and dollars for timecards, grouped by task number. The code for this view is shown in Listing 10.1. CREATE VIEW vw_SummarizeTimecardsByTask AS SELECT fkTaskID, SUM(HourCount) As HoursByTask, SUM(HourCount * HourlyRate) As TotalByTask FROM tblTimecards GROUP BY fkTaskID Listing 10.1 vw_SummarizeTimecardsByTask The second view we need is one step above this one and provides the total of hours and dollars by project. The code is shown in Listing 10.2. CREATE VIEW dbo.vw_SummarizeTasksByProject AS SELECT T.fkProjectID, SUM(V.HoursByTask) AS HoursByProject, SUM(V.TotalByTask) AS TotalByProject FROM dbo.tblTasks T INNER JOIN dbo.vw_SummarizeTimecardsByTask V ON T.pkTaskID = V.fkTaskID GROUP BY T.fkProjectID Listing 10.2 vw_SummarizeTasksByProject You’ll be seeing these views in a number of different places in the application as you work through the project. Teamwork Network: Project Tracking 559 Creating the Stored Procedures As usual, we are making extensive use of stored procedures for this portion of the application. The primary reason we’re doing this is to help remove some of the need to build grids manually. If we can get all the information from the database in the proper format, we can just feed it into the various Repeater controls instead of having to man- ually build all the tables; a few tables, however, will still have to be built manually. The first stored procedure you need is used in the first page, where the teams are shown along with the number of projects for each. The code is shown in Listing 10.3. CREATE PROCEDURE dbo.sp_RetrieveProjectCountByTeam @TeamID int AS SELECT COUNT(*) AS Total FROM tblProjects WHERE fkTeamID = @TeamID Listing 10.3 sp_RetrieveProjectCountByTeam We’re using the same stored procedure as that used in several other projects to determine what teams the member has joined, so we don’t have to re-create that one here. The next stored procedure we need to build returns all the projects for a particular team. We’re doing a good deal of data formatting in this stored procedure to make it easier to display in the Web page. The code for this stored procedure is shown in List- ing 10.4. CREATE PROCEDURE dbo.sp_RetrieveProjectsByTeam @TeamID int AS SELECT ‘<a href=”project_view.aspx?id=’ + convert(varchar, pkProjectID) + ‘“>’ + P.Name + ‘</a>’ As ProjectName, P.*, M.LastName + ‘, ‘ + M.FirstName As ProjectLead FROM tblProjects P, tblMembers M WHERE fkTeamID = @TeamID AND fkCreatorID = M.pkMemberID ORDER BY P.Name Listing 10.4 sp_RetrieveProjectsByTeam This joins the projects table with the members table in order to retrieve the project lead’s name. If you wanted to, you could create a link to the member viewer so that 560 Project 10 clicking the project leader’s name would bring up his or her profile. Just copy the code used here to generate a link to the Project_view.aspx file and point to Member_ view.aspx. In order to show the Project Detail page, we have to run a fairly long query in order to gather the data required for the page. The code for this stored procedure is shown in Listing 10.5. CREATE procedure sp_RetrieveProjectDetails @ProjectID int AS SELECT P.pkProjectID, P.fkTeamID, P.fkCreatorID, P.Name, M.LastName + ‘, ‘ + M.FirstName As ProjectLead, P.Description, P.StartDate, P.DueDate, P.IsCompleted, CompletedStatus = (CASE WHEN IsCompleted = 1 THEN ‘Yes’ ELSE ‘No’ END), TaskCount = (SELECT COUNT(*) FROM tblTasks WHERE fkProjectID = @ProjectID), ProjectBudget = CONVERT(varchar, (SELECT SUM(Budget) FROM tblTasks WHERE fkProjectID = @ProjectID), 1), ProjectHours = IsNull((SELECT HoursByProject FROM vw_SummarizeTasksByProject WHERE fkProjectID = @ProjectID), 0), ProjectTotal = CONVERT(varchar, IsNull((SELECT TotalByProject FROM vw_SummarizeTasksByProject WHERE fkProjectID = @ProjectID), 0), 1) FROM tblProjects P, tblMembers M WHERE P.fkCreatorID = M.pkMemberID AND P.pkProjectID = @ProjectID Listing 10.5 sp_RetrieveProjectDetails There are a number of subqueries in this routine, so let’s go through it a little bit at a time. We first tie into the tblMembers table to get the project lead’s name. As in the other stored procedure, you could change this to return a link to the member’s profile. We return a text version of the completion status so that we can display Yes or No. We then calculate the number of tasks in the project. This value is used to determine whether or not the project can be deleted, since we don’t allow deletions of projects once tasks have been added. Next, we add up the task budgets to determine the overall project budget. Doing this summarization this way means less data entry and math for the team leader. Once the task budgets are entered, the project budget is just a summary of them. Teamwork Network: Project Tracking 561 We then use the view we created to determine how many hours and dollars have been expended on this project to date. Since there is a possibility that no time has been spent on this project, we have code that converts null values to zeroes. We also format the result using grouping symbols (commas) and two decimal places. We don’t need to convert the number of hours, but we do check it for possible null results. All of these results are shown on the project viewer in the header of the page. The next stored procedure returns all the tasks for a particular project. The code for it is shown in Listing 10.6. CREATE PROCEDURE dbo.sp_RetrieveTasksByProject @ProjectID int AS SELECT T.pkTaskID, T.fkProjectID, T.Name, ‘<a href=”task_view.aspx?id=’ + convert(varchar, T.pkTaskID) + ‘“>’ + T.Name + ‘</a>’ As TaskNameWithLink, T.Description, T.DueDate, DueDateStatus = (CASE WHEN DueDate < getdate() THEN ‘#FF0000’ ELSE ‘#000000’ END), T.Budget, TaskStatus = (CASE WHEN T.IsCompleted = 1 THEN ‘Yes’ ELSE ‘No’ END) FROM tblTasks T WHERE T.fkProjectID = @ProjectID ORDER BY DueDate Listing 10.6 sp_RetrieveTasksByProject This routine, given a project number, returns all the tasks associated with that proj- ect. Besides returning the files, it also returns other status information that is used in the display. The DueDateStatus looks at whether the due date of the task has passed. If so, it returns a color code for red. This color code is used to change the color of the due date field in the list. The TaskStatus field changes the 0 or 1 of the IsCompleted field into a Yes or No for display purposes. We can use the data without having to do a lot of manual formatting. Once the user selects a task, we have to show the details of that task. The stored pro- cedure to perform this function is similar to the one used to retrieve the project details and is shown in Listing 10.7. 562 Project 10 TEAMFLY Team-Fly ® Teamwork Network: Project Tracking 563 CREATE PROCEDURE sp_RetrieveTaskDetails @TaskID int as select T.*, P.fkCreatorID, TaskBudget = CONVERT(varchar, Budget, 1), TaskHours = IsNull((SELECT HoursByTask FROM vw_SummarizeTimecardsByTask WHERE fkTaskID = @TaskID), 0), TaskTotal = CONVERT(varchar, IsNull((SELECT TotalByTask FROM vw_SummarizeTimecardsByTask WHERE fkTaskID = @TaskID), 0), 1), TimecardCount = (SELECT COUNT(*) FROM tblTimecards WHERE fkTaskID = @TaskID) FROM tblTasks T, tblProjects P WHERE T.pkTaskID = @TaskID AND T.fkProjectID = P.pkProjectID Listing 10.7 sp_RetrieveTaskDetails Instead of looking at the project summary view, we use the task summary view, which adds the timecards by task number. We take this information, accounting for any nulls that might exist for tasks without timecards, and use it in the Task Detail win- dow. The final stored procedure that we need retrieves the timecards for a particular task. We can show the work that has been accomplished for a particular task in the task viewer. The code for this stored procedure is shown in Listing 10.8. CREATE PROCEDURE sp_RetrieveTimecardsByTask @TaskID int, @MemberID int as SELECT T.pkTimecardID, M.LastName + ‘, ‘ + M.FirstName As MemberName, CardDate = (CASE WHEN IsBilled = 0 AND (T.fkMemberID = @MemberID OR fkTaskID IN (SELECT pkTaskID FROM tblTasks WHERE fkProjectID IN (SELECT pkProjectID FROM tblProjects WHERE fkCreatorID = @MemberID))) THEN ‘<a href=”tcard_create.aspx?id=’ + convert(varchar, pkTimecardID) + ‘“>’ + convert(varchar, TimecardDate, 101) + ‘</a>’ ELSE convert(varchar, TimecardDate, 101) END), Listing 10.8 sp_RetrieveTimecardsByTask WorkDescription, HourCount, convert(varchar, HourlyRate, 1) as Rate, IsBilled, BilledStatus = (CASE WHEN T.IsBilled = 0 THEN ‘No’ ELSE ‘Yes’ END) FROM tblTimecards T, tblMembers M WHERE T.fkMemberID = M.pkMemberID AND T.fkTaskID = @TaskID ORDER BY TimecardDate Listing 10.8 sp_RetrieveTimecardsByTask (continued) As with some of the other bit fields, we change the IsBilled bit into a Yes or No, which is shown in the Repeater control. We also show the member name by joining to the tblMembers table. You see who has been working on the task without having to click any additional links. As with the other member names that we’ve shown, you could change this into a link to the Member Profile page. This page also generates a link to the timecard editor only if the member viewing the information (supplied by the member ID parameter) is the creator of the timecard or is the project manager. In addition, the timecard has to be in an unbilled state. Otherwise, we would be changing history, and most accountants and CFOs get a bit upset about this. When viewing this page, unauthorized users will just see the date of the timecard and not the link. We have another stored procedure that returns the total hours and total dollars expended to date on each task. This code is shown in Listing 10.9. CREATE procedure sp_RetrieveTaskStatistics @TaskID int as SELECT IsNull(HoursByTask, 0) As TaskHours, IsNull(TotalByTask, 0) As TaskMoney FROM vw_SummarizeTimecardsByTask WHERE fkTaskID = @TaskID Listing 10.9 sp_RetrieveTaskStatistics This stored procedure is used as we loop through the tasks and display them and the related statistics to the user. You’ll use this stored procedure later in the project when you build the task viewing page. The next stored procedure we need to build retrieves timecards that have not been billed yet. This data is shown on the project invoice, and the code is shown in Listing 10.10. 564 Project 10 CREATE PROCEDURE sp_RetrieveUnbilledTimecardsByProject @ProjectID int AS SELECT TC.TimecardDate, M.LastName + ‘, ‘ + M.FirstName As MemberName, T.Name As TaskName, TC.WorkDescription, TC.HourCount, CONVERT(varchar, TC.HourlyRate, 1) As Rate, CONVERT(varchar, TC.HourlyRate * TC.HourCount, 1) As TimecardTotal FROM tblTimecards TC, tblTasks T, tblMembers M WHERE fkTaskID IN (SELECT pkTaskID FROM tblTasks WHERE fkProjectID = @ProjectID) AND TC.fkTaskID = T.pkTaskID AND TC.fkMemberID = M.pkMemberID AND IsBilled = 0 Listing 10.10 sp_RetrieveUnbilledTimecardsByProject We use this stored procedure to retrieve all timecards for a project that has not been billed for yet. The Project Invoice page uses this routine to display the invoice, which the team leader can then print. This is a particularly complex query, since we have to drill into the tasks table and find all the possible tasks in the timecards table. It’s much easier to call the stored procedure from the ASP.NET page than to try to embed all this SQL code in the page. Another stored procedure used in the invoicing process is the following, which determines what the invoice total should be by adding all the unbilled timecards. The code is shown in Listing 10.11. CREATE PROCEDURE sp_RetrieveUnbilledTotalByProject @ProjectID int AS SELECT CONVERT(varchar, IsNull(SUM(TC.HourlyRate * TC.HourCount), 0), 1) As InvoiceTotal FROM tblTimecards TC WHERE fkTaskID IN (SELECT pkTaskID FROM tblTasks WHERE fkProjectID = @ProjectID) AND IsBilled = 0 Listing 10.11 sp_RetrieveUnbilledTotalByProject We’ll use the single field returned from this stored procedure to populate the Invoice Total field when we build the project invoice. The last stored procedure you need to build updates all unbilled timecards to a billed status on a per-project basis. The code is shown in Listing 10.12. Teamwork Network: Project Tracking 565 CREATE PROCEDURE sp_UpdateTimecardsByProject @ProjectID int AS UPDATE tblTimecards SET IsBilled = 1 WHERE IsBilled = 0 AND fkTaskID IN (SELECT pkTaskID FROM tblTasks WHERE fkProjectID = @ProjectID) Listing 10.12 sp_UpdateTimecardsByProject Once the team leader has determined that the billing has been done, he or she can select a page that calls this stored procedure, and everything that was shown as Unbilled will be changed to Billed. This function is restricted to the team leader for security reasons. With the stored procedures out of the way, we can build the business objects for the subsystem. Building the Business Objects We have a total of six new objects to add to our Teamwork Network assembly: Project, Task, Timecard, and three corresponding exception classes. We are following the same design pattern for these objects as we have used in previous projects, so you should be able to quickly build these objects. The first object is the Project object, and its code is shown in Listing 10.13. Imports AtWorkUtilities Public Class Project Inherits BaseServices ‘ ‘ If no arguments are supplied, build a separate ‘ database connection for this object. ‘ Public Sub New() MyBase.New(New Database(), “SELECT * FROM tblProjects WHERE 1=0”) End Sub ‘ ‘ If database connection is supplied, store it ‘ in the private connection variable for this ‘ object. ‘ Public Sub New(ByVal db As Database) Listing 10.13 Project class 566 Project 10 MyBase.New(db, “SELECT * FROM tblProjects WHERE 1=0”) End Sub ‘ ‘ If both database and ID are supplied, retrieve ‘ data into the object from the database. ‘ Public Sub New(ByVal db As Database, _ ByVal ID As Integer) MyBase.New(db, “SELECT * FROM tblProjects WHERE pkProjectID = “ _ & ID.ToString) End Sub ‘ ‘ Verify that all data validation rules have been ‘ met. Any errors get stored into the errors collection ‘ inherited from the BaseServices class. ‘ Public Sub Validate() Dim dr As DataRow ClearErrors() For Each dr In m_DS.Tables(0).Rows If dr.RowState = DataRowState.Added _ Or dr.RowState = DataRowState.Modified Then ValidateRow(dr) End If Next End Sub ‘ ‘ Checks an individual row for validation rule ‘ compliance. Any errors are added to the errors ‘ collection. ‘ Private Sub ValidateRow(ByVal dr As DataRow) If IsDBNull(dr(“fkTeamID”)) Then AddError(“Team number is missing.”) End If If IsDBNull(dr(“fkCreatorID”)) Then AddError(“Owner’s member number is missing.”) End If CheckRequiredField(dr, “Name”, “Project name”, 80) If IsDBNull(dr(“StartDate”)) Then AddError(“Project start date is missing.”) End If Listing 10.13 Project class (continued) Teamwork Network: Project Tracking 567 If IsDBNull(dr(“DueDate”)) Then AddError(“Project due date is missing.”) End If If IsDBNull(dr(“IsCompleted”)) Then AddError(“Completion flag is missing.”) End If End Sub ‘ ‘ The base Save method stores the DataRow into the ‘ DataSet, whether it’s a new or existing row. The ‘ rest of this routine handles specific validation ‘ for this type of data. ‘ Public Overloads Sub SaveRow(ByVal dr As DataRow) If IsDBNull(dr(“IsCompleted”)) Then dr(“IsCompleted”) = 0 End If MyBase.SaveRow(dr) Validate() End Sub ‘ ‘ We separate the SaveRow method from the Save method ‘ to give us a chance to handle any validation. We have ‘ a verification here that the data is good before we ‘ continue, however. ‘ Public Sub Save() If Not Me.IsValid Then Throw New ProjectException(Me.ValidationError) Exit Sub End If m_DA.Update(m_DS) End Sub ‘ ‘ Since we only have a single row in our DataSet, ‘ delete it and then update the database with the ‘ change. ‘ Public Sub Delete() If m_DS.Tables(0).Rows.Count > 0 Then m_DS.Tables(0).Rows(0).Delete() m_DA.Update(m_DS) End If End Sub End Class Listing 10.13 Project class (continued) 568 Project 10 [...]... align=center> . spent, what date they were used, and what the individual’s hourly rate is. This way, we can generate project- status information to be shown at the task and project levels. We also require that a description. ‘ Public Sub Validate() Dim dr As DataRow ClearErrors() For Each dr In m_DS.Tables(0).Rows If dr.RowState = DataRowState.Added _ Or dr.RowState = DataRowState.Modified Then ValidateRow(dr) End If Next End. m_DS.Tables(0).Rows If dr.RowState = DataRowState.Added _ Or dr.RowState = DataRowState.Modified Then ValidateRow(dr) End If Next End Sub ‘ ‘ Checks an individual row for validation rule ‘ compliance.