private void ExportAbsenceEntriesToCSV(MongoCursor <BsonDocument> cursor) { foreach (BsonDocument document in cursor) { var csvLine = new TroToVismaCsvLine(); if (!document.Contains("user")) { throw new Exception("Absence entry is missing pserson"); } if (!document.Contains("absenceentrytype")) { throw new Exception("Absence entry is missing absence type"); } ObjectId userId = (ObjectId)document["user"][0]; ObjectId absenceTypeId = (ObjectId)document["absenceentrytype"][0]; BsonDocument userDocument = database.GetCollection("user").FindOne(Query.EQ(DBQuery.Id, userId)); BsonDocument absenceTypeDocument = database.GetCollection("absenceentrytype").FindOne(Query.EQ(DBQuery.Id, absenceTypeId)); if (userDocument == null) { throw new Exception("Person not found for absence entry"); } if (absenceTypeDocument == null) { throw new Exception("Absence type not found for absence entry"); } BsonDocument profitcenterDocument = database.GetCollection("profitcenter").FindOne(Query.EQ(DBQuery.Id, userDocument["profitcenter"][0])); csvLine.Values[(int)TroToVismaColumns.Paytype] = (string)absenceTypeDocument["identifier"]; TimeSpan duration = (DateTime)document["endtimestamp"] - (DateTime)document["starttimestamp"]; // Hours + minutes in one value with the accuracy of two decimal spaces. Double hours = (double)duration.Hours + Math.Round(((double)duration.Minutes) / 60, 2); csvLine.Values[(int)TroToVismaColumns.Hours] = Convert.ToString(hours, new CultureInfo("fi-FI")); csvLine.Values[(int)TroToVismaColumns.Days] = "0"; csvLine.Values[(int)TroToVismaColumns.PersonnelNumber] = (string)userDocument["identifier"]; csvLine.Values[(int)TroToVismaColumns.Paytype] = (string)absenceTypeDocument["identifier"]; csvLine.Values[(int)TroToVismaColumns.Amount] = "0"; csvLine.Values[(int)TroToVismaColumns.Euro] = "0"; csvLine.Values[(int)TroToVismaColumns.ProfitCenter] = (string)profitcenterDocument["identifier"]; DateTime date = (DateTime)document["starttimestamp"]; csvLine.Values[(int)TroToVismaColumns.StartDate] = string.Format("{0:00}.{1:00}.{2:0000}", date.Day, date.Month, date.Year); rawCsvLines.Add(csvLine); } }
private void ExportTimesheetFragmentsToCsv(List <TimesheetEntryFragment> timesheetEntryFragments) { foreach (TimesheetEntryFragment fragment in timesheetEntryFragments) { var csvLine = new TroToVismaCsvLine(); csvLine.Values[(int)TroToVismaColumns.PersonnelNumber] = fragment.WorkerId; csvLine.Values[(int)TroToVismaColumns.Paytype] = Convert.ToString(fragment.PayType); csvLine.Values[(int)TroToVismaColumns.Hours] = Convert.ToString((fragment.End - fragment.Start).Hours); csvLine.Values[(int)TroToVismaColumns.Days] = "0"; // Todo: Figure out if this is ok. csvLine.Values[(int)TroToVismaColumns.Amount] = Convert.ToString(fragment.Detail["amount"]); csvLine.Values[(int)TroToVismaColumns.Euro] = Convert.ToString(fragment.Detail["sum"], CultureInfo.InvariantCulture); csvLine.Values[(int)TroToVismaColumns.StartDate] = string.Format("{0:0000}-{1:00}-{2:00}", fragment.Start.Year, fragment.Start.Month, fragment.Start.Day); exportedLines.Add(csvLine); } }
private void AddPayType(TimesheetEntryFragment fragment, string payType, double payTypeFactor, bool isPrimary) { var csvLine = new TroToVismaCsvLine(); csvLine.Values[(int)TroToVismaColumns.PersonnelNumber] = fragment.WorkerId; csvLine.Values[(int)TroToVismaColumns.Paytype] = payType; TimeSpan duration = fragment.End - fragment.Start; // Hours + minutes in one value with the accuracy of two decimal spaces. Double hours = Math.Round(((double)duration.Hours + ((double)duration.Minutes) / 60) * payTypeFactor, 2) + ((int)duration.TotalDays * 24); csvLine.Values[(int)TroToVismaColumns.Hours] = Convert.ToString(hours, new CultureInfo("fi-FI")); csvLine.Values[(int)TroToVismaColumns.Days] = "0"; // Amount and sum are only added to the primary pay type. if (fragment.Detail != null && isPrimary) { csvLine.Values[(int)TroToVismaColumns.Amount] = Convert.ToString(fragment.Detail.GetValue("amount", 0)); csvLine.Values[(int)TroToVismaColumns.Euro] = Convert.ToString(fragment.Detail.GetValue("sum", 0), new CultureInfo("fi-FI")); } else { csvLine.Values[(int)TroToVismaColumns.Amount] = "0"; csvLine.Values[(int)TroToVismaColumns.Euro] = "0"; } csvLine.Values[(int)TroToVismaColumns.StartDate] = string.Format("{0:00}.{1:00}.{2:0000}", fragment.Start.Day, fragment.Start.Month, fragment.Start.Year); csvLine.Values[(int)TroToVismaColumns.ProfitCenter] = fragment.WorkerProfitCenter; if (fragment.Detail != null && (bool)fragment.Detail.GetValue("substractlunchbreak", false)) { csvLine.Values[(int)TroToVismaColumns.LunchBreak] = "true"; } else { csvLine.Values[(int)TroToVismaColumns.LunchBreak] = "false"; } rawCsvLines.Add(csvLine); }
/// <summary> /// Postprocess stage will take the expored data, group it to users, days and payment types. The grouping is /// done at this stage mostly for historical reasons but it does provide a decent debug option to observe the /// raw data befor postporcess stage. /// </summary> private void PostProcessData() { // Dictionary of dictoinaries of DailyHourTotalsHelpers // user // date // paytype // helper object with row totals var dailyTotals = new DataTree(); // Count hour totals using helper object for (int i = rawCsvLines.Count - 1; i >= 0; i--) { TroToVismaCsvLine line = rawCsvLines[i]; string payType = line.Values[(int)TroToVismaColumns.Paytype]; string userId = line.Values[(int)TroToVismaColumns.PersonnelNumber]; string date = line.Values[(int)TroToVismaColumns.StartDate]; string profitCenter = line.Values[(int)TroToVismaColumns.ProfitCenter]; bool lunchBreak = ((string)line.Values[(int)TroToVismaColumns.LunchBreak] == "true"); double hours = Convert.ToDouble(line.Values[(int)TroToVismaColumns.Hours], new CultureInfo("fi-FI")); double euro = Convert.ToDouble(line.Values[(int)TroToVismaColumns.Euro], new CultureInfo("fi-FI")); int days = Convert.ToInt32(line.Values[(int)TroToVismaColumns.Days]); int amount = Convert.ToInt32(line.Values[(int)TroToVismaColumns.Amount]); decimal currentHours = 0; decimal currentEuro = 0; int currentDays = 0; int currentAmount = 0; if (dailyTotals[userId][date][payType]["hours"].Exists) { currentHours = (decimal)dailyTotals[userId][date][payType]["hours"]; } if (dailyTotals[userId][date][payType]["euro"].Exists) { currentEuro = (decimal)dailyTotals[userId][date][payType]["euro"]; } if (dailyTotals[userId][date][payType]["days"].Exists) { currentDays = (int)dailyTotals[userId][date][payType]["days"]; } if (dailyTotals[userId][date][payType]["amount"].Exists) { currentAmount = (int)dailyTotals[userId][date][payType]["amount"]; } dailyTotals[userId][date][payType]["hours"] = (decimal)(currentHours + Convert.ToDecimal(hours)); dailyTotals[userId][date][payType]["euro"] = (decimal)(currentEuro + Convert.ToDecimal(euro)); dailyTotals[userId][date][payType]["days"] = currentDays + days; dailyTotals[userId][date][payType]["amount"] = currentAmount + amount; if (lunchBreak) { dailyTotals[userId][date][payType]["lunchbreak"] = true; } dailyTotals[userId]["profitcenter"] = profitCenter; } // Add lunch breaks for applicable pay types foreach (DataTree user in dailyTotals) { foreach (DataTree date in user) { // Lunch breaks for basic hours are special and contain pay double basicHours = (double)(decimal)date[IntegrationHelpers.PayTypeBasic]["hours"].GetValueOrDefault((decimal)0); double overtime50 = (double)(decimal)date[IntegrationHelpers.PayTypeOvertime50]["hours"].GetValueOrDefault((decimal)0); double overtime100 = (double)(decimal)date[IntegrationHelpers.PayTypeOvertime100]["hours"].GetValueOrDefault((decimal)0); double totalBasicHours = basicHours + overtime50 + overtime100; // Lunc break is substracted if basic hours amount is greater than (and not equal) to 6h. The corner case // was verified from Visma if (totalBasicHours > MinDayLengthWithLunchBreak) { logger.LogTrace("Adding lunch break", user.Name, date.Name, "Hours before lunch break" + basicHours, "Basic hours"); date[IntegrationHelpers.PayTypeBasic]["hours"] = (decimal)(basicHours - LunchBreakLength); date[IntegrationHelpers.PayTypeBasic]["lunchbreak"] = true; } foreach (DataTree payType in date) { if (!payType.Contains("lunchbreak")) { continue; } double hours = (double)(decimal)payType["hours"].GetValueOrDefault((decimal)0); if (hours > MinDayLengthWithLunchBreak) { logger.LogTrace("Adding lunch break", user.Name, date.Name, "Hours before lunch break" + basicHours, "PayType: " + payType.Name); payType["hours"] = (decimal)(basicHours - LunchBreakLength); payType["lunchbreak"] = true; } } } } foreach (DataTree user in dailyTotals) { foreach (DataTree date in user) { foreach (DataTree payType in date) { var csvLine = new TroToVismaCsvLine(); csvLine.Values[(int)TroToVismaColumns.Hours] = Convert.ToString((double)(decimal)payType["hours"].GetValueOrDefault(0), new CultureInfo("fi-FI")); csvLine.Values[(int)TroToVismaColumns.Days] = Convert.ToString(payType["days"].GetValueOrDefault(0)); csvLine.Values[(int)TroToVismaColumns.PersonnelNumber] = user.Name; csvLine.Values[(int)TroToVismaColumns.Paytype] = payType.Name; csvLine.Values[(int)TroToVismaColumns.Amount] = Convert.ToString((double)(decimal)payType["amount"].GetValueOrDefault(0), new CultureInfo("fi-FI")); csvLine.Values[(int)TroToVismaColumns.Euro] = Convert.ToString((double)(decimal)payType["euro"].GetValueOrDefault(0), new CultureInfo("fi-FI")); csvLine.Values[(int)TroToVismaColumns.StartDate] = date.Name; csvLine.Values[(int)TroToVismaColumns.ProfitCenter] = user["profitcenter"]; processedCsvLines.Add(csvLine); } } } WriteAnnotatedHoursToDisk(dailyTotals); WriteAnnotatedTotalHoursToDisk(dailyTotals); }
private void ExportDailyEntriesToCSV(MongoCursor <BsonDocument> cursor) { foreach (BsonDocument document in cursor) { var csvLine = new TroToVismaCsvLine(); ObjectId userId = (ObjectId)document["user"][0]; BsonDocument userDocument = database.GetCollection("user").FindOne(Query.EQ(DBQuery.Id, userId)); BsonDocument dayEntryType = database.GetCollection("dayentrytype").FindOne(Query.EQ(DBQuery.Id, document["dayentrytype"][0])); if (userDocument == null) { throw new Exception("User not found for daily entry"); } if (dayEntryType == null) { throw new Exception("Entry type not for daily entry"); } BsonDocument profitcenterDocument = database.GetCollection("profitcenter").FindOne(Query.EQ(DBQuery.Id, userDocument["profitcenter"][0])); if (profitcenterDocument == null) { throw new Exception("Profit center not for daily entry"); } csvLine.Values[(int)TroToVismaColumns.PersonnelNumber] = (string)userDocument["identifier"]; csvLine.Values[(int)TroToVismaColumns.Paytype] = (string)dayEntryType["identifier"]; csvLine.Values[(int)TroToVismaColumns.Hours] = "0"; csvLine.Values[(int)TroToVismaColumns.Euro] = "0"; csvLine.Values[(int)TroToVismaColumns.Amount] = "0"; csvLine.Values[(int)TroToVismaColumns.Days] = "0"; // Daily entry amount is set as either euro, pieces or days edepending no daily entry type if (dayEntryType.GetValue("unit", string.Empty) == "euro") { csvLine.Values[(int)TroToVismaColumns.Euro] = Convert.ToString((int)document.GetValue("amount", 0)); } else if (dayEntryType.GetValue("unit", string.Empty) == "pcs") { csvLine.Values[(int)TroToVismaColumns.Amount] = Convert.ToString((int)document.GetValue("amount", 0)); } else { csvLine.Values[(int)TroToVismaColumns.Days] = Convert.ToString((int)document.GetValue("amount", 0)); } csvLine.Values[(int)TroToVismaColumns.ProfitCenter] = (string)profitcenterDocument["identifier"]; // XXX: Implemented a quick temporary fix for DT-394. The correct fix is to implement date picker to always return // dates with UTC time 0:00 for correct date. DateTime date = ((DateTime)document["date"]).AddHours(12); csvLine.Values[(int)TroToVismaColumns.StartDate] = string.Format("{0:00}.{1:00}.{2:0000}", date.Day, date.Month, date.Year); rawCsvLines.Add(csvLine); } }