/// <summary> /// Update task list to match list just updated by user. /// </summary> /// <param name="tasks"></param> /// <param name="newToOldMap"></param> static public void SyncChanges(BindingList <Task> tasks, List <Task> deletedTasks, Dictionary <Task, Task> newToOldMap) { s_AllTasks = new List <Task>(); // First just replace old list to keep the ordering specified foreach (Task c in tasks) { s_AllTasks.Add(c); } // Now replace items in new list with old objects if they still exist. // This is to maintain reference links in TimeLogEntries for (int idx = 0; idx < s_AllTasks.Count; idx++) { Task newTask = s_AllTasks[idx]; if (newToOldMap.ContainsKey(newTask)) { Task oldTask = newToOldMap[newTask]; if (null != oldTask) { oldTask.CopySettingsFrom(newTask); // Put old task object back in list s_AllTasks[idx] = oldTask; } } } foreach (Task task in deletedTasks) { // Clean up all log entries for deleted tasks TimeLogEntry.DeleteEntriesForTask(task); } }
/// <summary> /// Load the data file for the given month. /// </summary> /// <param name="monthToLoad"></param> /// <returns><b>true</b> if loaded sucessfully.</returns> static public bool LoadDataFile(DateTime monthToLoad) { bool retVal = false; string dataFileName = GetDataFileName(monthToLoad); if (File.Exists(dataFileName)) { XmlDocument dataXml = new XmlDocument(); try { dataXml.Load(dataFileName); Customer.ReadCustomers(dataXml); Task.ReadTasks(dataXml); TimeLogEntry.ReadLogEntries(dataXml); // Update last save time s_CanSaveToPrimaryDataFile = true; s_LastSaveTime = DateTime.Now; retVal = true; } catch (IOException ex) { string msg = string.Format("Error Loading Data File. No data will be saved until the error is corrected and PTT is restarted. Error: {0}", ex.Message); MessageBox.Show("Error Loading File", msg, MessageBoxButtons.OK, MessageBoxIcon.Error); } } if (s_CanSaveToPrimaryDataFile) { s_MonthsLoaded.Add(monthToLoad); } return(retVal); }
/// <summary> /// Update customer list to match list just updated by user. /// </summary> /// <param name="customers"></param> /// <param name="newToOldMap"></param> static public void SyncChanges(BindingList <Customer> customers, List <Customer> deletedCustomers, Dictionary <Customer, Customer> newToOldMap) { s_AllCustomers = new List <Customer>(); // First just replace old list to keep the ordering specified foreach (Customer c in customers) { s_AllCustomers.Add(c); } // Now replace items in new list with old objects if they still exist. // This is to maintain reference links in TimeLogEntries for (int idx = 0; idx < s_AllCustomers.Count; idx++) { Customer newCustomer = s_AllCustomers[idx]; if (newToOldMap.ContainsKey(newCustomer)) { Customer oldCustomer = newToOldMap[newCustomer]; if (null != oldCustomer) { oldCustomer.CopySettingsFrom(newCustomer); // Put old customer object back in list s_AllCustomers[idx] = oldCustomer; } } } foreach (Customer customer in deletedCustomers) { // Clean up all log entries for deleted customers TimeLogEntry.DeleteEntriesForCustomer(customer); } }
private void m_removeButton_Click(object sender, EventArgs e) { DataGridViewRow row = m_dataGridView.CurrentRow; if (null != row) { Customer customer = m_customers[row.Index]; // See if this customer has associated time logs if (TimeLogEntry.DoEntriesExistForCustomer(customer)) { string msg = string.Format("Delete all logs for the customer '{0}'.", customer.Name); if (DialogResult.No == MessageBox.Show(this, "Customer Has Log Entries", msg, MessageBoxButtons.YesNo, MessageBoxIcon.Question)) { return; } } row.Selected = false; m_newToOldMap.Remove(customer); m_deletedCustomers.Add(customer); m_customers.RemoveAt(row.Index); // set dirty flag m_isDirty = true; } }
/// <summary> /// Save the tasks for the current month. /// </summary> /// <returns><b>true</b> if sucessful.</returns> static public bool SaveDataFile() { bool retVal = true; if (s_CanSaveToPrimaryDataFile) { List <DateTime> monthsThatFailedToSave = new List <DateTime>(); foreach (DateTime monthLoaded in s_MonthsLoaded) { if (DoesMonthHaveEditedTimes(monthLoaded)) { // See if we need rolled over to a new month file string dataFileName = GetDataFileName(monthLoaded); XmlDocument dataXml = new XmlDocument(); try { XmlNode rootNode = dataXml.CreateNode(XmlNodeType.Element, "ptt_data", dataXml.NamespaceURI); dataXml.AppendChild(rootNode); Customer.SaveCustomers(rootNode); Task.SaveTasks(rootNode); TimeLogEntry.SaveLogs(rootNode, monthLoaded); using (XmlTextWriter writer = new XmlTextWriter(dataFileName, Encoding.UTF8)) { writer.Formatting = Formatting.Indented; dataXml.Save(writer); } // Update last save time s_LastSaveTime = DateTime.Now; } catch (IOException ex) { monthsThatFailedToSave.Add(monthLoaded); MessageBox.Show("Error Saving Data File", ex.Message, MessageBoxButtons.OK, MessageBoxIcon.Error); retVal = false; } } } // Clear dirty months s_MonthsEdited.Clear(); // Add back any months that had save errors s_MonthsEdited.AddRange(monthsThatFailedToSave); } return(retVal); }
/// <summary> /// Print Report showing daily activity (optionally showing all tasks) /// </summary> /// <param name="e"></param> private void PrintActivityPage(PrintPageEventArgs e, Point drawLocation) { bool skipRestOfPage = m_needToShowOverallTotals; while (!skipRestOfPage && drawLocation.Y < e.MarginBounds.Bottom && m_currentDateBeingPrinted <= m_endDate) { LogFileManager.LoadDataFileIfNotAlreadyLoaded(m_currentDateBeingPrinted); SortableBindingList <TimeLogEntry> logs = TimeLogEntry.LogsForDay(m_currentDateBeingPrinted); if (null == logs || 0 == logs.Count) { if (!m_skipDaysWithoutTasks) { PrintDayHeaderLine(e, ref drawLocation); e.Graphics.DrawString(Properties.Resources.ReportStrNoActivity, m_detailFont, m_detailBrush, drawLocation); drawLocation.Y += m_detailFont.Height; m_lastDayDisplayed = m_currentDateBeingPrinted; } // Move to next day m_currentDateBeingPrinted = m_currentDateBeingPrinted.AddDays(1); m_customersSeenForDay.Clear(); m_indexOfTaskInCurrentDay = 0; continue; } // If not already past end and do not need to show summary from last page else if (!m_needToShowSummary && m_indexOfTaskInCurrentDay < logs.Count) { TimeLogEntry log = logs[m_indexOfTaskInCurrentDay]; if (!m_customersSeenForDay.Contains(log.Customer.Name)) { m_customersSeenForDay.Add(log.Customer.Name); } // Add to overall totals TimeSpan[] timeSpans = null; if (!m_customerSummaryData.ContainsKey(log.Customer.Name)) { timeSpans = new TimeSpan[2]; m_customerSummaryData[log.Customer.Name] = timeSpans; } else { timeSpans = m_customerSummaryData[log.Customer.Name]; } if (log.IsBillable) { timeSpans[BILLABLE_TIME_INDEX] += log.Duration; } else { timeSpans[NON_BILLABLE_TIME_INDEX] += log.Duration; } if (m_lastDayDisplayed != m_currentDateBeingPrinted) { PrintDayHeaderLine(e, ref drawLocation); m_lastDayDisplayed = m_currentDateBeingPrinted; } if (m_showTasks) { // {0} for {1} from {2} to {3}. Duration{4}({5}) Billable:{6} string durationStr = string.Format("{0}:{1}", log.Duration.Hours.ToString(), log.Duration.Minutes.ToString("00")); string durationPercentStr = string.Format("{0}.{1}", log.Duration.Hours.ToString(), ((int)(log.Duration.Minutes * 100) / 60).ToString("00")); string billiableFlagStr = ""; if (!log.IsBillable) { billiableFlagStr = Properties.Resources.ReportStrNotBillableReportTag; } string taskLine = string.Format(Properties.Resources.ReportStrActivityLine, log.Task.Name, log.Customer.Name, log.StartTime.ToShortTimeString(), log.StopTime.ToShortTimeString(), durationStr, durationPercentStr, billiableFlagStr); e.Graphics.DrawString(taskLine, m_detailFont, m_detailBrush, drawLocation); drawLocation.Y += m_detailFont.Height; } // Move to next item (could bump to next day) ++m_indexOfTaskInCurrentDay; } Point startPoint; Point endPoint; if (null != logs && m_indexOfTaskInCurrentDay >= logs.Count) { if (0 < m_customersSeenForDay.Count) { m_needToShowSummary = true; // See if there is enough room to display the summary (# customers + 1 for totals + 1 for horz line + 1 for header) int summaryHeight = m_summaryFont.Height * (3 + m_customersSeenForDay.Count); if (drawLocation.Y + summaryHeight > e.MarginBounds.Bottom) { // Must wait for next page skipRestOfPage = true; break; } m_needToShowSummary = false; // we are going to print it so clear flag // OK, we have enough room for the summary DrawSummaryHeader(ref drawLocation, e, Properties.Resources.ReportStrCustomerColHeader); TimeSpan dailyTotalTime = new TimeSpan(); TimeSpan dailyBillableTime = new TimeSpan(); TimeSpan dailyNonBillableTime = new TimeSpan(); // Sort customers alphabetically m_customersSeenForDay.Sort(); foreach (string customerName in m_customersSeenForDay) { TimeSpan billableTime = new TimeSpan(); TimeSpan nonBillableTime = new TimeSpan(); // Total up time for this customer foreach (TimeLogEntry log in logs) { if (log.Customer.Name == customerName) { if (log.IsBillable) { billableTime += log.Duration; dailyBillableTime += log.Duration; } else { nonBillableTime += log.Duration; dailyNonBillableTime += log.Duration; } } } DrawSummaryLine(ref drawLocation, e, customerName, billableTime, nonBillableTime); } // Update the overall totals m_overallBillableTime += dailyBillableTime; m_overallNonBillableTime += dailyNonBillableTime; DrawSummaryLine(ref drawLocation, e, Properties.Resources.ReportStrTotalTitle, dailyBillableTime, dailyNonBillableTime); // Draw a dividing line between details and summary (if showing details) startPoint = drawLocation; startPoint.Y += m_summaryFont.Height / 2; endPoint = new Point(e.MarginBounds.Left + e.MarginBounds.Width / 2, drawLocation.Y + m_summaryFont.Height / 2); e.Graphics.DrawLine(m_linePen, startPoint, endPoint); drawLocation.Y += m_summaryFont.Height; } m_indexOfTaskInCurrentDay = 0; m_currentDateBeingPrinted = m_currentDateBeingPrinted.AddDays(1); m_customersSeenForDay.Clear(); } } e.HasMorePages = skipRestOfPage || (m_currentDateBeingPrinted < m_endDate); // See if we need to display the grand totals if (m_needToShowOverallTotals || !e.HasMorePages) { m_needToShowOverallTotals = true; // See if there is enough room to display the summary (# customers + 1 for totals + 1 for horz line + 1 for header) int summaryHeight = m_summaryFont.Height * (3 + m_customerSummaryData.Count); if (drawLocation.Y + summaryHeight > e.MarginBounds.Bottom) { e.HasMorePages = true; // Must put summary on last page } else { m_needToShowOverallTotals = false; // we are going to print it so clear flag if (drawLocation.Y > e.MarginBounds.Top) { drawLocation.Y += m_summaryFont.Height; } // draw a horz line all the way across Point startPoint = drawLocation; startPoint.Y += m_summaryFont.Height / 2; Point endPoint = new Point(e.MarginBounds.Right, drawLocation.Y + m_summaryFont.Height / 2); e.Graphics.DrawLine(m_linePen, startPoint, endPoint); drawLocation.Y += m_summaryFont.Height; DrawSummaryHeader(ref drawLocation, e, Properties.Resources.ReportStrCustomerColHeader); // Sort customers alphabetically List <string> allCustomerNames = new List <string>(); foreach (string key in m_customerSummaryData.Keys) { allCustomerNames.Add(key); } allCustomerNames.Sort(); // Now show each customer's totals TimeSpan overallBillable = new TimeSpan(); TimeSpan overallNonBillable = new TimeSpan(); TimeSpan overallTotal = new TimeSpan(); foreach (string name in allCustomerNames) { TimeSpan[] timeSpans = m_customerSummaryData[name]; DrawSummaryLine(ref drawLocation, e, name, timeSpans[BILLABLE_TIME_INDEX], timeSpans[NON_BILLABLE_TIME_INDEX]); // Add to global total overallBillable += timeSpans[BILLABLE_TIME_INDEX]; overallNonBillable += timeSpans[NON_BILLABLE_TIME_INDEX]; overallTotal += timeSpans[BILLABLE_TIME_INDEX] + timeSpans[NON_BILLABLE_TIME_INDEX]; } DrawSummaryLine(ref drawLocation, e, Properties.Resources.ReportStrTotalTitle, overallBillable, overallNonBillable); e.HasMorePages = false; // this is the end } } }
/// <summary> /// Run through all tasks in given time period and calculate sums. /// </summary> private void CalculateSummaries() { if (m_showTasks) { m_taskSummaryData = new Dictionary <string, Dictionary <string, TimeSpan[]> >(); } while (m_currentDateBeingPrinted <= m_endDate) { LogFileManager.LoadDataFileIfNotAlreadyLoaded(m_currentDateBeingPrinted); SortableBindingList <TimeLogEntry> logs = TimeLogEntry.LogsForDay(m_currentDateBeingPrinted); if (null != logs && 0 != logs.Count) { foreach (TimeLogEntry log in logs) { if (m_showTasks) { // Update task level totals Dictionary <string, TimeSpan[]> taskList = null; // Get the task list for this customer (or create one) if (m_taskSummaryData.ContainsKey(log.Customer.Name)) { taskList = m_taskSummaryData[log.Customer.Name]; } else { taskList = new Dictionary <string, TimeSpan[]>(); m_taskSummaryData[log.Customer.Name] = taskList; } // See if this task exists yet TimeSpan[] taskTimes = null; if (taskList.ContainsKey(log.Task.Name)) { taskTimes = taskList[log.Task.Name]; } else { taskTimes = new TimeSpan[2]; } if (log.IsBillable) { taskTimes[0] += log.Duration; } else { taskTimes[1] += log.Duration; } taskList[log.Task.Name] = taskTimes; } // Update customer level totals TimeSpan[] customerTotals = null; if (m_customerSummaryData.ContainsKey(log.Customer.Name)) { customerTotals = m_customerSummaryData[log.Customer.Name]; } else { customerTotals = new TimeSpan[2]; m_customerSummaryData[log.Customer.Name] = customerTotals; } if (log.IsBillable) { customerTotals[0] += log.Duration; } else { customerTotals[1] += log.Duration; } } } // Move to next day m_currentDateBeingPrinted = m_currentDateBeingPrinted.AddDays(1); } // Sort customer names foreach (string s in m_customerSummaryData.Keys) { m_customersSeenForDay.Add(s); } m_customersSeenForDay.Sort(); // Get ready to run through the list m_customerEnumerator = m_customersSeenForDay.GetEnumerator(); m_needNextCustomer = true; m_needToShowSummary = true; }