/// <summary> /// Параметризированный конструктор. Инициализирует свойства класса на /// основании данных параметров, представленных в коллекции ReportParams. /// </summary> /// <param name="Params">Данные параметов, передаваемые в отчет</param> /// <param name="cn">Соединение с БД (для подгрузки данных)</param> /// <remarks> /// При необходимости выполняет коррекцию значений параметров /// </remarks> public ThisReportParams(ReportParams Params, IReportDataProvider provider) { // #1: ЗАЧИТЫВАЕМ ПАРАМЕТРЫ, ЗАДАННЫЕ ЯВНО // Идентификатор сотрудника EmployeeID = (Guid)Params.GetParam("Employee").Value; // Задание дат начала и конца отчетного периода IsSpecifiedIntervalBegin = !Params.GetParam("IntervalBegin").IsNull; IntervalBegin = (IsSpecifiedIntervalBegin? Params.GetParam("IntervalBegin").Value : DBNull.Value); IsSpecifiedIntervalEnd = !Params.GetParam("IntervalEnd").IsNull; IntervalEnd = (IsSpecifiedIntervalEnd? Params.GetParam("IntervalEnd").Value : DBNull.Value); // Признак "Показывть выходные дни без списаний" ShowFreeWeekends = (0 != (int)Params.GetParam("ShowFreeWeekends").Value); // Представление времени TimeMeasure = (TimeMeasureUnits)((int)Params.GetParam("TimeMeasureUnits").Value); // Признак отображения параметров отчета в заголовке ShowRestrictions = (0 != (int)Params.GetParam("ShowRestrictions").Value); // #2: ДОГРУЖАЕМ ВСЕ ДАННЫЕ ПО СОТРУДНИКУ using (IDataReader reader = provider.GetDataReader("dsAdditionaly", null)) { if (reader.Read()) { int nOrdinal = reader.GetOrdinal("FullName"); FullName = reader.IsDBNull(nOrdinal)? null : reader.GetString(nOrdinal); nOrdinal = reader.GetOrdinal("WorkBeginDate"); WorkBeginDate = reader.IsDBNull(nOrdinal) ? DateTime.MinValue : reader.GetDateTime(nOrdinal); nOrdinal = reader.GetOrdinal("WorkEndDate"); WorkEndDate = reader.IsDBNull(nOrdinal) ? DateTime.MaxValue : reader.GetDateTime(nOrdinal); nOrdinal = reader.GetOrdinal("WorkdayDuration"); WorkdayDuration = reader.IsDBNull(nOrdinal) ? 0 : reader.GetInt32(nOrdinal); } } }
/// <summary> /// Фомрирование "тела" отчета /// </summary> /// <param name="fo"></param> /// <param name="cn"></param> private void writeBody(XslFOProfileWriter fo, IReportDataProvider Provider) { // Запускаем процесс расчета данных: IDataReader reader; reader = Provider.GetDataReader("dsMain", m_oParams); if (!reader.Read()) { writeEmptyBody(fo, "Нет данных"); return; } // Формируем заголовок отчета: fo.TStart(true, "TABLE", false); writeHeader(fo); int nOrd_CalendarDate = reader.GetOrdinal("CalendarDate"); int nOrd_DayName = reader.GetOrdinal("DayName"); int nOrd_IsWorkday = reader.GetOrdinal("IsWorkday"); int nOrd_SpentForIncidents = reader.GetOrdinal("SpentForIncidents"); int nOrd_SpentForProcesses = reader.GetOrdinal("SpentForProcesses"); int nOrd_Rate = reader.GetOrdinal("Rate"); int nQntDays = 0; int nQntWorkDays = 0; int nSumBalance = 0; for (bool bHasMoreRows = true; bHasMoreRows; bHasMoreRows = reader.Read()) { // Данные результата, текущая строка DateTime dtCalendarDate = reader.GetDateTime(nOrd_CalendarDate); bool bIsWorkDay = reader.GetBoolean(nOrd_IsWorkday); int nSpentForIncidents = reader.GetInt32(nOrd_SpentForIncidents); int nSpentForProcesses = reader.GetInt32(nOrd_SpentForProcesses); int nDayRate = reader.GetInt16(nOrd_Rate); string sCellLinkParams = String.Format( DEF_FMT_ReportRef, m_oParams.EmployeeID.ToString(), // 0 - Идентификатор сотрудника dtCalendarDate.ToString("yyyy-MM-dd"), // 1 - Дата начала периода, она же - дата окончания ((int)m_oParams.TimeMeasure).ToString()); // 2 - Форма представления времени string sFullDayName = String.Format( "<fo:basic-link {0}>{1}</fo:basic-link>" + "<fo:block font-weight='normal'>{2}</fo:block>", sCellLinkParams, dtCalendarDate.ToString("dd.MM.yyyy"), reader.GetString(nOrd_DayName)); // Вычисляемые значения: bool bIsWorkingPeriod = (dtCalendarDate >= m_oParams.WorkBeginDate && dtCalendarDate <= m_oParams.WorkEndDate); int nDayBalance = (nSpentForIncidents + nSpentForProcesses); if (bIsWorkDay && bIsWorkingPeriod) { nDayBalance -= nDayRate; } nSumBalance += nDayBalance; nQntDays += 1; nQntWorkDays += (bIsWorkingPeriod && bIsWorkDay ? 1 : 0); // Выводим данные: если это // (а) рабочий день; // (б) не рабочий, но есть списания, // (в) не рабочий, но включен режим отображения не-рабочих: if (bIsWorkDay || nDayBalance > 0 || m_oParams.ShowFreeWeekends) { string sCellData = String.Format( "<fo:inline font-weight='bold'>{0}</fo:inline>" + "<fo:block font-weight='normal'>( {1} / {2} )</fo:block>", formatExpenseValue(nSpentForIncidents + nSpentForProcesses), formatExpenseValue(nSpentForIncidents), formatExpenseValue(nSpentForProcesses) ); string sInfoTestStyle; string sDayBalanceStyle; string sSumBalanceStyle; if (bIsWorkingPeriod) { sInfoTestStyle = bIsWorkDay ? "TABLE_CELL" : "TABLE_CELL_COLOR_FREE"; sDayBalanceStyle = nDayBalance < 0 ? "TABLE_CELL_COLOR_RED" : "TABLE_CELL_COLOR_GREEN"; sSumBalanceStyle = nSumBalance < 0 ? "TABLE_CELL_COLOR_RED" : "TABLE_CELL_COLOR_GREEN"; } else { sInfoTestStyle = bIsWorkDay ? "TABLE_CELL_COLOR_NOWORK" : "TABLE_CELL_COLOR_FREE"; sDayBalanceStyle = nDayBalance > 0 ? "TABLE_CELL_COLOR_GREEN" : "TABLE_CELL_COLOR_NOWORK"; sSumBalanceStyle = nSumBalance > 0 ? "TABLE_CELL_COLOR_GREEN" : "TABLE_CELL_COLOR_NOWORK"; if (nSpentForIncidents + nSpentForProcesses <= 0) { sCellData = "( списания не требуются )"; } } fo.TRStart(); fo.TRAddCell(sFullDayName, "string", 1, 1, sInfoTestStyle); fo.TRAddCell(sCellData, "string", 1, 1, sInfoTestStyle); fo.TRAddCell(formatExpenseValue(nDayBalance), "string", 1, 1, sDayBalanceStyle); fo.TRAddCell(formatExpenseValue(nSumBalance), "string", 1, 1, sSumBalanceStyle); fo.TREnd(); } } // Подвал: итоговое количество дней (вообще и рабочих), итоговый баланс за период: fo.TRStart(); fo.TRAddCell( String.Format( "<fo:block font-weight='bold' text-align='left'>Итого, за период: " + "<fo:block font-weight='normal' padding-left='15px'> календарных дней: <fo:inline font-weight='bold'>{0}</fo:inline>, </fo:block>" + "<fo:block font-weight='normal' padding-left='15px'> из них рабочих: <fo:inline font-weight='bold'>{1}</fo:inline></fo:block>" + "</fo:block>", nQntDays, nQntWorkDays ), "string", 3, 1, "TABLE_FOOTER"); fo.TRAddCell( String.Format("<fo:inline font-weight='bold'>{0}</fo:inline>", formatExpenseValue(nSumBalance)), "string", 1, 1, nSumBalance < 0 ? "TABLE_CELL_COLOR_RED" : "TABLE_CELL_COLOR_GREEN"); fo.TREnd(); fo.TEnd(); }
/// <summary> /// Фомрирование "тела" отчета /// </summary> /// <param name="foWriter"></param> /// <param name="oParams"></param> /// <param name="cn"></param> private void writeBody(XslFOProfileWriter foWriter, ThisReportParams oParams, IReportDataProvider Provider) { using (IDataReader reader = Provider.GetDataReader("dsMain", oParams)) { // Вне зависимости от параметров в результате первым идет рекордсет с // результатами анализа допустимости анализа - это будет или одна строка // с текстом "несоответствий не обрнаружено" или массированный рекордсет // с перечнем активностей, для которых обнаружены баги. // NB! Если рекордсет пришел пустой - это ошибка! if (!reader.Read()) { throw new ApplicationException("Ошибка получения данных отчета: запрос процедуры вернул пустой результирующий набор!"); } // ... проверим набор с данными первого рекордсета: если это строка // в один столбец, то это хороший результат; иначе - это набор с перечнем // активностей, в которых найдены несоответствия IDictionary rowData = _GetDataFromDataRow(reader); if (1 != rowData.Count) { #region Отображение данных с ошибками // Обнаружены случаи недопустимости выполнения анализа. // В рекордсете идут: // - CustomerID - идентификатор организации, Guid // - CustomerName - наименование организации // - FolderID - иденетификатор активности, в котором обнаружена проблема // - FullName - полное наименование (путь) проекта // - ErrorType - "тип" проблемы, здесь: // 1 - нет направлений, // 2 - не заданы доли, // 3 - нарушения в задании направлений для подчиненных активностей/каталогов, // 4 - что-то еще (ошибка в определении типа проблемы, по идее так быть не должно) // #1: Служебное сообщение об обнаружении ошибок foWriter.TStart(false, "WARNING_MESSAGE_TABLE", false); foWriter.TAddColumn("Сообщение"); foWriter.TRStart(); foWriter.TRAddCell( "Внимание! Выполнение анализа затрат невозможно: обнаружены ошибки определения направлений для активностей!", null, 1, 1, "WARNING_MESSAGE"); foWriter.TREnd(); foWriter.TEnd(); // #2: Заголовок таблицы с перечнем активностей, в которых обнаружены нарушения foWriter.TStart(true, "TABLE", false); foWriter.TAddColumn("Активность", align.ALIGN_LEFT, valign.VALIGN_MIDDLE, null, String.Empty, align.ALIGN_NONE, valign.VALIGN_NONE, "TABLE_HEADER"); foWriter.TAddColumn("Ошибка определения", align.ALIGN_LEFT, valign.VALIGN_MIDDLE, null, String.Empty, align.ALIGN_NONE, valign.VALIGN_NONE, "TABLE_HEADER"); // ... данные сгруппированы по организации-Клиенту: string sCustomerName = rowData["CustomerName"].ToString(); bool bIsNextCusomerData = true; for (bool bMoreRows = true; bMoreRows;) { // ...если при последнем чтении строки обнаружено условие перехода // к данным следующей организации-Клиента, то выводим заголовок // группы - наименование Клиента: if (bIsNextCusomerData) { foWriter.TRStart(); foWriter.TRAddCell(xmlEncode(sCustomerName), null, 2, 1, "GROUP_HEADER"); foWriter.TREnd(); } // Текстовое представление "типа" нарушения: string sErrorDescription; switch ((int)rowData["ErrorType"]) { case 1: sErrorDescription = "(ТИП-1) Для активности не заданы направления"; break; case 2: sErrorDescription = "(ТИП-2) Для направления активности не заданы доли затрат"; break; case 3: sErrorDescription = "(ТИП-3) Для подчиненной активности / каталога указано направление, отличное от направлений, заданных для активности"; break; default: sErrorDescription = "(Ошибка определения типа несоответствия)"; break; } // Выводим данные по одной активности; при этом наименование активности // оформляется как анкер, при клике на который будет отображаться всплывающее // меню с доступными операциями - просмотр, редактирование, отчеты и т.д. foWriter.TRStart(); foWriter.TRAddCell(_GetFolderAnchor(rowData["FullName"], (Guid)rowData["FolderID"], true), null, 1, 1, "TABLE_CELL"); foWriter.TRAddCell(sErrorDescription, null, 1, 1, "TABLE_CELL"); foWriter.TREnd(); // Читаем след. строку (если данные еще есть); при этом определяем // условие перехода к следующей грппе данных, по след. Клиенту: bMoreRows = reader.Read(); if (bMoreRows) { rowData = _GetDataFromDataRow(reader); } bIsNextCusomerData = (sCustomerName != rowData["CustomerName"].ToString()); if (bIsNextCusomerData) { sCustomerName = rowData["CustomerName"].ToString(); } } foWriter.TEnd(); #endregion // На этом отчет заканчивается! return; } // Несоответствий нет - выводим данные: if (!reader.NextResult()) { throw new ApplicationException("Отсутствует основной результирующий набор! Текст запроса: "); } // ЗАГОЛОВОК ОСНОВНОЙ ТАБЛИЦЫ string sDirsColumnName; bool bWithActivityQnt = (ThisReportParams.AnalysisDirectionEnum.ByActivity != oParams.AnalysisDirection); if (ThisReportParams.AnalysisDirectionEnum.ByCustomer_AllCustomners == oParams.AnalysisDirection && oParams.ShowDetails) { sDirsColumnName = "Направления / организации"; } else if (ThisReportParams.AnalysisDirectionEnum.ByActivity == oParams.AnalysisDirection && oParams.ShowDetails) { sDirsColumnName = "Направления / активности"; } else { sDirsColumnName = "Направления"; } foWriter.TStart(true, "TABLE", false); foWriter.TAddColumn("№", align.ALIGN_LEFT, valign.VALIGN_MIDDLE, null, "5%", align.ALIGN_NONE, valign.VALIGN_NONE, "TABLE_HEADER"); foWriter.TAddColumn(sDirsColumnName, align.ALIGN_LEFT, valign.VALIGN_MIDDLE, null, (bWithActivityQnt ? "40%" : "55%"), align.ALIGN_NONE, valign.VALIGN_NONE, "TABLE_HEADER"); if (bWithActivityQnt) { foWriter.TAddColumn("Количество активностей", align.ALIGN_RIGHT, valign.VALIGN_MIDDLE, null, "15%", align.ALIGN_NONE, valign.VALIGN_NONE, "TABLE_HEADER"); } foWriter.TAddColumn("Затраты", (TimeMeasureUnits.Days == oParams.TimeMeasure ? align.ALIGN_LEFT : align.ALIGN_RIGHT), valign.VALIGN_MIDDLE, null, "20%", align.ALIGN_NONE, valign.VALIGN_NONE, "TABLE_HEADER"); //foWriter.TAddColumn("Сумма затрат", align.ALIGN_RIGHT, valign.VALIGN_MIDDLE, null, "20%", align.ALIGN_NONE, valign.VALIGN_NONE, "TABLE_HEADER"); // ПОКА - КОНСТАНТА! TODO! int nWorkDayDuration = 600; if (oParams.ShowDetails) { #region ДАННЫЕ С ДЕТАЛИЗАЦИЕЙ // - DirectionName - Наименование направления; // - DetailID - Идентификатор детализирующей сущности (Организация / Активность) // - DetailName - Наименование детализируюшей сущности // - ActivityQnt - Количество активностей // - ExpensesSum - Сумма затрат string sDirectionName = null; int nSubTotalTime = 0; // промежуточный итог по сумме затраченного времени int nTotalTime = 0; // общий итог по сумме затраченного времени int nSubTotalQnt = 0; // промежуточный итог по количеству активностей (по направлению) int nTotalQnt = 0; // общий итог по количеству активностей int nRowNum = 0; // сквозная нумерация строк детализации // Признак, что наименование детализирующей сущности есть гиперссылка: // Сейчас это единственный случай - при направлении анализа "Организации..", // в случае указания конкретной организации - когда в кач. детализации // выводятся активности. Для них и формляем анкер с всплывающим меню операций: bool bIsDetailNameAsHref = (ThisReportParams.AnalysisDirectionEnum.ByCustomer_TargetCustomer == oParams.AnalysisDirection); while (reader.Read()) { IDictionary rec = _GetDataFromDataRow(reader); // Если очередная рассматриваемая строка относится уже к другой группе, // то сформируем строку отчета с данными подытога if (null == sDirectionName || sDirectionName != rec["DirectionName"].ToString()) { if (null != sDirectionName) { foWriter.TRStart(); foWriter.TRAddCell("Итого по направлению", "string", 2, 1, "GROUP_FOOTER"); if (bWithActivityQnt) { foWriter.TRAddCell(nSubTotalQnt, "i4", 1, 1, "GROUP_FOOTER"); } if (TimeMeasureUnits.Days == oParams.TimeMeasure) { foWriter.TRAddCell(_FormatTimeStringAtServer(nSubTotalTime, nWorkDayDuration), "string", 1, 1, "GROUP_FOOTER"); } else { foWriter.TRAddCell(string.Format("{0:0.##}", nSubTotalTime / 60.0), "r8", 1, 1, "GROUP_FOOTER"); } //foWriter.TRAddCell( (nSubTotalSum / 100.0).ToString("F2"), "fixed.14.4", 1, 1, "GROUP_FOOTER" ); foWriter.TREnd(); } sDirectionName = rec["DirectionName"].ToString(); nSubTotalTime = 0; nSubTotalQnt = 0; // Текст с "заголовком" следующей группы: foWriter.TRStart(); foWriter.TRAddCell(xmlEncode(sDirectionName), "string", (bWithActivityQnt ? 5 : 4), 1, "GROUP_HEADER"); foWriter.TREnd(); } // Зачитываем данные int nTime = Int32.Parse(rec["ExpensesTime"].ToString()); nSubTotalTime += nTime; nTotalTime += nTime; /*int nSum = Int32.Parse( rec["ExpensesSum"].ToString() ); * nSubTotalSum += nSum; * nTotalSum += nSum;*/ int nQnt = Int32.Parse(rec["ActivityQnt"].ToString()); nSubTotalQnt += nQnt; nTotalQnt += nQnt; nRowNum += 1; // Формируем соответствующую строку отчета: foWriter.TRStart(); foWriter.TRAddCell(nRowNum, "i4", 1, 1, "TABLE_CELL_ROWNUM"); if (bIsDetailNameAsHref) { foWriter.TRAddCell(_GetFolderAnchor(rec["DetailName"].ToString(), (Guid)rec["DetailID"], true), "string", 1, 1, "TABLE_CELL"); } else { foWriter.TRAddCell(xmlEncode(rec["DetailName"]), "string", 1, 1, "TABLE_CELL"); } if (bWithActivityQnt) { foWriter.TRAddCell(nQnt, "i4", 1, 1, "TABLE_CELL"); } if (TimeMeasureUnits.Days == oParams.TimeMeasure) { foWriter.TRAddCell(_FormatTimeStringAtServer(nTime, nWorkDayDuration), "string", 1, 1, "TABLE_CELL"); } else { foWriter.TRAddCell(string.Format("{0:0.##}", nTime / 60.0), "r8", 1, 1, "TABLE_CELL"); } //foWriter.TRAddCell( (nSum / 100.0).ToString("F2"), "fixed.14.4", 1, 1, "TABLE_CELL" ); foWriter.TREnd(); } // Строка с последним накопленным подитогом (если такой был) if (null != sDirectionName) { foWriter.TRStart(); foWriter.TRAddCell("Итого по направлению", "string", 2, 1, "GROUP_FOOTER"); if (bWithActivityQnt) { foWriter.TRAddCell(nSubTotalQnt, "i4", 1, 1, "GROUP_FOOTER"); } if (TimeMeasureUnits.Days == oParams.TimeMeasure) { foWriter.TRAddCell(_FormatTimeStringAtServer(nSubTotalTime, nWorkDayDuration), "string", 1, 1, "GROUP_FOOTER"); } else { foWriter.TRAddCell(string.Format("{0:0.##}", nSubTotalTime / 60.0), "r8", 1, 1, "GROUP_FOOTER"); } //foWriter.TRAddCell( (nSubTotalSum / 100.0).ToString("F2"), "fixed.14.4", 1, 1, "GROUP_FOOTER" ); foWriter.TREnd(); } // Строка с общим итогом по всему отчету foWriter.TRStart(); foWriter.TRAddCell("Итого", "string", 2, 1, "TABLE_FOOTER"); if (bWithActivityQnt) { foWriter.TRAddCell(nTotalQnt, "i4", 1, 1, "TABLE_FOOTER"); } if (TimeMeasureUnits.Days == oParams.TimeMeasure) { foWriter.TRAddCell(_FormatTimeStringAtServer(nTotalTime, nWorkDayDuration), "string", 1, 1, "TABLE_FOOTER"); } else { foWriter.TRAddCell(string.Format("{0:0.##}", nTotalTime / 60.0), "r8", 1, 1, "TABLE_FOOTER"); } //foWriter.TRAddCell( (nTotalSum / 100.0).ToString("F2"), "fixed.14.4", 1, 1, "TABLE_FOOTER" ); foWriter.TREnd(); #endregion } else { #region ДАННЫЕ БЕЗ ДЕТАЛИЗАЦИИ // - DirectionName - Наименование направления; // - ActivityQnt - Количество активностей // - ExpensesSum - Сумма затрат int nTotalTime = 0; int nTotalQnt = 0; int nRowNum = 0; while (reader.Read()) { // Зачитываем значения текущей строки: IDictionary rec = _GetDataFromDataRow(reader); int nTime = Int32.Parse(rec["ExpensesTime"].ToString()); nTotalTime += nTime; /* Убираем Фин данные из отчета * int nSum = Int32.Parse( rec["ExpensesSum"].ToString() ); * nTotalSum += nSum;*/ int nQnt = Int32.Parse(rec["ActivityQnt"].ToString()); nTotalQnt += nQnt; nRowNum += 1; // Формируем представление строки в XSL-FO: foWriter.TRStart(); foWriter.TRAddCell(nRowNum, "i4", 1, 1, "TABLE_CELL"); foWriter.TRAddCell(xmlEncode(rec["DirectionName"]), "string", 1, 1, "TABLE_CELL"); if (bWithActivityQnt) { foWriter.TRAddCell(nQnt, "i4", 1, 1, "TABLE_CELL"); } if (TimeMeasureUnits.Days == oParams.TimeMeasure) { foWriter.TRAddCell(_FormatTimeStringAtServer(nTime, nWorkDayDuration), "string", 1, 1, "TABLE_CELL"); } else { foWriter.TRAddCell(string.Format("{0:0.##}", nTime / 60.0), "r8", 1, 1, "TABLE_CELL"); } //foWriter.TRAddCell( (nSum / 100.0).ToString("F2"), "fixed.14.4", 1, 1, "TABLE_CELL" ); foWriter.TREnd(); } foWriter.TRStart(); foWriter.TRAddCell("Итого", "string", 2, 1, "TABLE_FOOTER"); if (bWithActivityQnt) { foWriter.TRAddCell(nTotalQnt, "i4", 1, 1, "TABLE_FOOTER"); } if (TimeMeasureUnits.Days == oParams.TimeMeasure) { foWriter.TRAddCell(_FormatTimeStringAtServer(nTotalTime, nWorkDayDuration), "string", 1, 1, "TABLE_FOOTER"); } else { foWriter.TRAddCell(string.Format("{0:0.##}", nTotalTime / 60.0), "r8", 1, 1, "TABLE_FOOTER"); } //foWriter.TRAddCell( (nTotalSum / 100.0).ToString("F2"), "fixed.14.4", 1, 1, "TABLE_FOOTER" ); foWriter.TREnd(); #endregion } foWriter.TEnd(); } #region ДОПОЛНИТЕЛНЫЕ ДАННЫЕ // Если направление анализа - "Активность-Направления" и задан признак отображения // данных о последнем изменении определения направления для активности: if (ThisReportParams.AnalysisDirectionEnum.ByActivity == oParams.AnalysisDirection && oParams.ShowHistoryInfo) { // Получение исторической информации - когда и кто последний раз изменил определение направлений object oScalar = Provider.GetValue("dsHistory", oParams); string sNoteText = String.Format( "<fo:inline>Последнее изменение определения направлений: </fo:inline><fo:inline font-weight=\"bold\">{0}</fo:inline>", (null != oScalar? xmlEncode(oScalar.ToString()) : "(нет данных") ); // Формируем таблицу с "исторической справкой": // NB! Заголовок здесь НЕ ОТОБРАЖАЕТСЯ! foWriter.TStart(false, "TABLE_NOTE", false); foWriter.TAddColumn("Замечание", align.ALIGN_LEFT, valign.VALIGN_MIDDLE, null, "100%", align.ALIGN_NONE, valign.VALIGN_NONE, "TABLE_NOTE_HEADER"); foWriter.TRStart(); foWriter.TRAddCell(sNoteText, null, 1, 1, "TABLE_NOTE_CELL"); foWriter.TREnd(); foWriter.TEnd(); } #endregion }
/// <summary> /// Формирует текст XSL-FO, представляющий данные заданных параметров, и /// записывает его как текст подзаголовка формируемого отчета /// </summary> /// <param name="foWriter"></param> /// <param name="cn"></param> public void WriteParamsInHeader(XslFOProfileWriter foWriter, IReportDataProvider Provider) { // XSL-FO с перечнем параметров будем собирать сюда: StringBuilder sbBlock = new StringBuilder(); string sParamValue; // #1: Дата начала и окончания отчетного периода. // Любая из этих дат может быть не задана; если это так, то, с соответствии // с требованиями, в заголовке отчета должны выводиться соответствующие // расчетные даты - соответственно дата самого раннего и дата самого позднего // списаний (для множества проектов, получаемых в соотв. ограничениями, // задаваемыми остальными парамитрами). Расчетные данные получаются при // помощи специальной UDF; запрос будет выполняться только если необходимо: string sPossibleIntervalBegin = "нет данных"; // Строка с расчетной датой начала периода string sPossibleIntervalEnd = "нет данных"; // Строка с расчетной датой завершения периода if (!IsSpecifiedIntervalBegin || !IsSpecifiedIntervalEnd) { // Для расчета самой возможных даты надо выполнить UDF dbo.GetMinimaxBoundingDates: using (IDataReader reader = Provider.GetDataReader("dsDates", this)) { if (!reader.Read()) { throw new ApplicationException("Ошибка получения дополнительных данных (расчетная дата начала периода)"); } // Расчетная дата начала периода (первый столбец в рекордсете): if (!reader.IsDBNull(0)) { sPossibleIntervalBegin = reader.GetDateTime(0).ToString("dd.MM.yyyy"); } // Расчетная дата завершения периода (второй столбец в рекордсете): if (!reader.IsDBNull(1)) { sPossibleIntervalEnd = reader.GetDateTime(1).ToString("dd.MM.yyyy"); } } } if (IsSpecifiedIntervalBegin) { sParamValue = ((DateTime)IntervalBegin).ToString("dd.MM.yyyy"); } else { sParamValue = String.Format("не задана (расчетная дата - {0})", sPossibleIntervalBegin); } sbBlock.Append(getParamValueAsFoBlock("Дата начала отчетного периода", sParamValue)); if (IsSpecifiedIntervalEnd) { sParamValue = ((DateTime)IntervalEnd).ToString("dd.MM.yyyy"); } else { sParamValue = String.Format("не задана (расчетная дата - {0})", sPossibleIntervalEnd); } sbBlock.Append(getParamValueAsFoBlock("Дата окончания отчетного периода", sParamValue)); // #2: Направление анализа: if (AnalysisDirectionEnum.ByActivity == AnalysisDirection) { sParamValue = "Активности - Направления"; } else { sParamValue = "Организации - Направления"; } sbBlock.Append(getParamValueAsFoBlock("Направление анализа", sParamValue)); // #3: Организация-Клиент или Активность: // Если задано, то что-то одно (определимся на основании "направления анализа"), // возможно ни то, ни другое. Если задано, то полное наименование возьмем из БД: if (AnalysisDirectionEnum.ByCustomer_AllCustomners == AnalysisDirection) { sbBlock.Append(getParamValueAsFoBlock("Организация", "Все организации")); } else { sParamValue = (string)Provider.GetValue("dsParams", this); if (AnalysisDirectionEnum.ByActivity == AnalysisDirection) { sbBlock.Append(getParamValueAsFoBlock("Активность", sParamValue)); } else { sbBlock.Append(getParamValueAsFoBlock("Организация", sParamValue)); } } // #4: Доп. условия на типы активности и на отбор только открытых активностей - // работает только в случае направления анализа "Организации - Направления": if (AnalysisDirectionEnum.ByActivity != AnalysisDirection) { FolderTypeFlags flags = (0 != FolderType)? (FolderTypeFlags)FolderType : (FolderTypeFlags.Project | FolderTypeFlags.Tender | FolderTypeFlags.Presale); sParamValue = FolderTypeFlagsItem.ToStringOfDescriptions(flags); sbBlock.Append(getParamValueAsFoBlock("Включать данные активностей (по типам)", sParamValue)); sbBlock.Append(getParamValueAsFoBlock( "Включать данные только открытых активностей", OnlyActiveFolders? "Да (активности в состояниях \"Открыто\" и \"Ожидание закрытия\")" : "Нет (активности во всех состояниях)" )); } else { sbBlock.Append(getParamValueAsFoBlock( "Отображать данные о последнем изменении определения направлений", ShowHistoryInfo? "Да" : "Нет" )); } // #5: Общие параметры: детализация: if (!ShowDetails) { sParamValue = "Нет"; } else { sParamValue = (AnalysisDirectionEnum.ByCustomer_AllCustomners == AnalysisDirection)? "По организациям" : "По активностям"; } sbBlock.Append(getParamValueAsFoBlock("Детализация", sParamValue)); // ...форма представления времени sbBlock.Append(getParamValueAsFoBlock( "Представление времени", TimeMeasureUnitsItem.GetItem(TimeMeasure).Description )); // ...сортировка: sbBlock.Append(getParamValueAsFoBlock("Сортировка", (0 == SortBy? "По направлению" : "По сумме затрат"))); // ВЫВОД ПОДЗАГОЛОВКА: foWriter.AddSubHeader( @"<fo:block text-align=""left""><fo:block font-weight=""bold"">Параметры отчета:</fo:block>" + sbBlock.ToString() + @"</fo:block>" ); }
protected void buildThisReport(XslFOProfileWriter w, ReportParams Params, IReportDataProvider Provider, object CustomData) { // Получим параметры object IntervalBegin = Params.GetParam("IntervalBegin").Value; object IntervalEnd = Params.GetParam("IntervalEnd").Value; Guid Employee = (Guid)Params.GetParam("Employee").Value; int NonProjectExpences = (int)Params.GetParam("NonProjectExpences").Value; int IncludeParams = (int)Params.GetParam("IncludeParams").Value; int AnalysDirection = (int)Params.GetParam("AnalysDirection").Value; int TimeLossReason = (int)Params.GetParam("TimeLossReason").Value; int SectionByActivity = (int)Params.GetParam("SectionByActivity").Value; int ExepenseDetalization = (int)Params.GetParam("ExepenseDetalization").Value; int TimeMeasureUnits = (int)Params.GetParam("TimeMeasureUnits").Value; object ActivityType = Params.GetParam("ActivityType").Value; object ExpenseType = Params.GetParam("ExpenseType").Value; object IncidentState = Params.GetParam("IncidentState").Value; int Sort = (int)Params.GetParam("Sort").Value; int SortOrder = (int)Params.GetParam("SortOrder").Value; bool bIncidentAttributes = 0 != (int)Params.GetParam("IncidentAttributes").Value; bool bDate = 0 != (int)Params.GetParam("Date").Value; bool bNumberOfTasks = 0 != (int)Params.GetParam("NumberOfTasks").Value; bool bRemaining = 0 != (int)Params.GetParam("Remaining").Value; bool bNewState = 0 != (int)Params.GetParam("NewState").Value; bool bComment = 0 != (int)Params.GetParam("Comment").Value; StringBuilder sb = new StringBuilder(); switch (ExepenseDetalization) { case (int)ExpenseDetalization.ByExpences: bNumberOfTasks = false; break; case (int)ExpenseDetalization.BySubActivity: bNewState = bComment = bIncidentAttributes = false; break; } using (IDataReader r = Provider.GetDataReader("dsMain", null)) { IDictionary headerData; w.WriteLayoutMaster(); w.StartPageSequence(); w.StartPageBody(); sb.Append("<fo:block>Список инцидентов и затрат сотрудника</fo:block>"); if (!r.Read()) { //TODO: EmptyBody } headerData = _GetDataFromDataRow(r); IntervalBegin = headerData["IntervalBegin"]; IntervalEnd = headerData["IntervalEnd"]; // Создадим заголовок if (0 != IncludeParams) { sb.Append("<fo:block/>"); sb.Append("<fo:block font-size='14px'>Параметры отчета:</fo:block>"); sb.Append("<fo:table color='#FFFFFF' text-align='left' font-size='12px' font-family='MS Sans-serif'>"); sb.Append("<fo:table-column/>"); sb.Append("<fo:table-column/>"); sb.Append("<fo:table-body>"); sb.Append("<fo:table-row>"); sb.Append("<fo:table-cell width='35%'><fo:block>Направление анализа:</fo:block></fo:table-cell>"); sb.Append("<fo:table-cell><fo:block>" + xmlEncode(headerData["AnalysDirection"]) + "</fo:block></fo:table-cell>"); sb.Append("</fo:table-row>"); sb.Append("<fo:table-row>"); sb.Append("<fo:table-cell width='35%'><fo:block>Период времени:</fo:block></fo:table-cell>"); sb.Append("<fo:table-cell><fo:block>" + xmlEncode(headerData["DateInterval"]) + "</fo:block></fo:table-cell>"); sb.Append("</fo:table-row>"); sb.Append("<fo:table-row>"); sb.Append("<fo:table-cell width='35%'><fo:block>Сотрудник:</fo:block></fo:table-cell>"); sb.Append("<fo:table-cell><fo:block><fo:basic-link color=\"#ffffff\" external-destination=\"vbscript:ShowContextForEmployeeLite("" + Employee + "","" + xmlEncode(headerData["EMail"]) + "")\">" + headerData["EmployeeName"] + "</fo:basic-link></fo:block></fo:table-cell>"); sb.Append("</fo:table-row>"); sb.Append("<fo:table-row>"); sb.Append("<fo:table-cell width='35%'><fo:block>Тип активности:</fo:block></fo:table-cell>"); sb.Append("<fo:table-cell><fo:block>" + xmlEncode(headerData["ActivityType"]) + "</fo:block></fo:table-cell>"); sb.Append("</fo:table-row>"); sb.Append("<fo:table-row>"); sb.Append("<fo:table-cell width='35%'><fo:block>Детализация затрат:</fo:block></fo:table-cell>"); sb.Append("<fo:table-cell><fo:block>" + xmlEncode(headerData["ExepenseDetalization"]) + "</fo:block></fo:table-cell>"); sb.Append("</fo:table-row>"); sb.Append("<fo:table-row>"); sb.Append("<fo:table-cell width='35%'><fo:block>Вид трудозатрат:</fo:block></fo:table-cell>"); sb.Append("<fo:table-cell><fo:block>" + xmlEncode(headerData["ExpenseType"]) + "</fo:block></fo:table-cell>"); sb.Append("</fo:table-row>"); sb.Append("<fo:table-row>"); sb.Append("<fo:table-cell width='35%'><fo:block>Состояние инцидента:</fo:block></fo:table-cell>"); sb.Append("<fo:table-cell><fo:block>" + xmlEncode(headerData["IncidentState"]) + "</fo:block></fo:table-cell>"); sb.Append("</fo:table-row>"); sb.Append("<fo:table-row>"); sb.Append("<fo:table-cell width='35%'><fo:block>Единицы измерения времени:</fo:block></fo:table-cell>"); sb.Append("<fo:table-cell><fo:block>" + xmlEncode(TimeMeasureUnitsItem.GetItem((Croc.IncidentTracker.TimeMeasureUnits)TimeMeasureUnits).Description) + "</fo:block></fo:table-cell>"); sb.Append("</fo:table-row>"); sb.Append("<fo:table-row>"); sb.Append("<fo:table-cell width='35%'><fo:block> </fo:block></fo:table-cell>"); sb.Append("<fo:table-cell><fo:block>" + xmlEncode(headerData["NonProjectExpences"]) + "</fo:block></fo:table-cell>"); sb.Append("</fo:table-row>"); sb.Append("</fo:table-body>"); sb.Append("</fo:table>"); } int nActivityColspan = 9; int nWorkDayDuration = (int)headerData["WorkDayDuration"]; int nReportWorkDayDuration = TimeMeasureUnits == (int)IncidentTracker.TimeMeasureUnits.Hours?int.MaxValue:nWorkDayDuration; w.Header(sb.ToString()); int nRowNum = 0; if (!r.NextResult()) { throw new ApplicationException("Отсутствует основной рекордсет"); } nActivityColspan = 9; w.TStart(0 == SectionByActivity, "CELL_CLASS", false); w.TAddColumn("№ п/п", align.ALIGN_RIGHT, valign.VALIGN_MIDDLE, null, String.Empty, align.ALIGN_NONE, valign.VALIGN_NONE, "TABLE_HEADER"); if (bIncidentAttributes) { w.TAddColumn("Причина списания", align.ALIGN_LEFT, valign.VALIGN_MIDDLE, null, String.Empty, align.ALIGN_NONE, valign.VALIGN_NONE, "TABLE_HEADER"); } else { --nActivityColspan; } w.TAddColumn("Наименование активности", align.ALIGN_LEFT, valign.VALIGN_MIDDLE, null, String.Empty, align.ALIGN_NONE, valign.VALIGN_NONE, "TABLE_HEADER"); if (bDate) { w.TAddColumn("Дата", align.ALIGN_CENTER, valign.VALIGN_MIDDLE, null, String.Empty, align.ALIGN_NONE, valign.VALIGN_NONE, "TABLE_HEADER"); } else { --nActivityColspan; } int nTotalColspan = nActivityColspan - 5 - 1; if (bNumberOfTasks) { w.TAddColumn("Кол-во заданий", align.ALIGN_RIGHT, valign.VALIGN_MIDDLE, null, String.Empty, align.ALIGN_NONE, valign.VALIGN_NONE, "TABLE_HEADER"); } else { --nActivityColspan; } w.TAddColumn("Затрачено/Списано", align.ALIGN_CENTER, valign.VALIGN_MIDDLE, null, String.Empty, align.ALIGN_NONE, valign.VALIGN_NONE, "TABLE_HEADER"); if (bRemaining) { w.TAddColumn("Осталось", align.ALIGN_CENTER, valign.VALIGN_MIDDLE, null, String.Empty, align.ALIGN_NONE, valign.VALIGN_NONE, "TABLE_HEADER"); } else { --nActivityColspan; } if (bNewState) { w.TAddColumn(ExepenseDetalization == (int)ExpenseDetalization.ByExpences ? "Новое состояние" : "Состояние", align.ALIGN_LEFT, valign.VALIGN_MIDDLE, null, String.Empty, align.ALIGN_NONE, valign.VALIGN_NONE, "TABLE_HEADER"); } else { --nActivityColspan; } if (bComment) { w.TAddColumn("Комментарии", align.ALIGN_LEFT, valign.VALIGN_MIDDLE, null, String.Empty, align.ALIGN_NONE, valign.VALIGN_NONE, "TABLE_HEADER"); } else { --nActivityColspan; } Guid currentTopActivityID = Guid.Empty; int totalSpent = 0; int totalLeft = 0; int totalTasks = 0; int totalLost = 0; int totalSpentPerActivity = 0; int totalLeftPerActivity = 0; int totalTasksPerActivity = 0; int previousCauseType = -1; int nCauseType = -1; while (r.Read()) { IDictionary rec = _GetDataFromDataRow(r); previousCauseType = nCauseType; nCauseType = (int)rec["CauseType"]; bool bMergeIncidentAttributes = (3 == nCauseType) && bIncidentAttributes; if (0 != SectionByActivity) { Guid topActivityID = (Guid)rec["topFolder"]; if (currentTopActivityID != topActivityID) { if (currentTopActivityID != Guid.Empty) { w.TRStart(); w.TRAddCell(" ", null); w.TRAddCell(previousCauseType == 3?"Всего по непроектным списаниям":"Всего по активности:", null, nTotalColspan, 1); if (bNumberOfTasks) { w.TRAddCell(totalTasksPerActivity, "i4"); } w.TRAddCell(this._FormatTimeStringAtServer(totalSpentPerActivity, nReportWorkDayDuration), null); if (bRemaining) { w.TRAddCell(this._FormatTimeStringAtServer(totalLeftPerActivity, nReportWorkDayDuration), null); } if (bNewState) { w.TRAddCell(" ", null); } if (bComment) { w.TRAddCell(" ", null); } w.TREnd(); totalSpentPerActivity = 0; totalLeftPerActivity = 0; totalTasksPerActivity = 0; } currentTopActivityID = topActivityID; w.TRStart(); w.TROmitCell(); w.TRAddCell(3 == nActivityColspan?xmlEncode(rec["topName"]):this._GetFolderAnchor(rec["topName"], (Guid)rec["ActivityID"], Employee, true, IntervalBegin != null?(DateTime)IntervalBegin:DateTime.MinValue, IntervalEnd != null?(DateTime)IntervalEnd:DateTime.MaxValue), null, nActivityColspan, 1, "TABLE_HEADER"); w.TREnd(); w.TRStart(); w.TRAddCell("№ п/п", null, 1, 1, "TABLE_HEADER"); if (bIncidentAttributes) { w.TRAddCell("Причина списания", null, bMergeIncidentAttributes?2:1, 1, "TABLE_HEADER"); } if (!bMergeIncidentAttributes) { w.TRAddCell(3 == nCauseType?"Причина списания":"Наименование активности", null, 1, 1, "TABLE_HEADER"); } if (bDate) { w.TRAddCell("Дата", null, 1, 1, "TABLE_HEADER"); } if (bNumberOfTasks) { w.TRAddCell(3 == nCauseType?"Кол-во списаний":"Кол-во заданий", null, 1, 1, "TABLE_HEADER"); } w.TRAddCell(3 == nCauseType?"Списано":"Затрачено/Списано", null, 1, 1, "TABLE_HEADER"); if (bRemaining) { w.TRAddCell("Осталось", null, 1, 1, "TABLE_HEADER"); } if (bNewState) { w.TRAddCell("Новое состояние", null, 1, 1, "TABLE_HEADER"); } if (bComment) { w.TRAddCell("Комментарии", null, 1, 1, "TABLE_HEADER"); } w.TREnd(); } } if (3 == nCauseType || 4 == nCauseType) { totalLost += (int)rec["Spent"]; } else { totalSpent += (int)rec["Spent"]; } totalSpentPerActivity += (int)rec["Spent"]; totalTasks += (3 == nCauseType ? 0 : (null != rec["NumberOfTasks"]?(int)rec["NumberOfTasks"]:0)); totalTasksPerActivity += ((1 != nCauseType && 2 != nCauseType)? 0 : null != rec["NumberOfTasks"]? (int)rec["NumberOfTasks"] : 0); totalLeft += 3 == nCauseType?0:(null != rec["LeftTime"]?(int)rec["LeftTime"]:0); totalLeftPerActivity += 3 == nCauseType?0:(null != rec["LeftTime"]?(int)rec["LeftTime"]:0); w.TRStart(); w.TRAddCell(++nRowNum, "i4"); if (bIncidentAttributes) { w.TRAddCell( 1 == nCauseType? // только для инцидентов this._GetIncidentAnchor(rec["CauseName"], (Guid)rec["CauseID"], true) : xmlEncode(rec["CauseName"]), null, bMergeIncidentAttributes? 2 : 1, 1); } if (!bMergeIncidentAttributes) { if (3 != nCauseType) { w.TRAddCell(this._GetFolderAnchor(rec["ActivityName"], (Guid)rec["ActivityID"], Employee, true, IntervalBegin != null?(DateTime)IntervalBegin : DateTime.MinValue, IntervalEnd != null?(DateTime)IntervalEnd:DateTime.MaxValue), null); } else { w.TRAddCell(xmlEncode(rec["ActivityName"]), null); } } if (bDate) { w.TRAddCell(xmlEncode(rec["DateSpent"]), null); } if (bNumberOfTasks) { // Кол-во инцидентов показваем только для инцидентов или проектов if (1 == nCauseType || 2 == nCauseType) { w.TRAddCell("" + rec["NumberOfTasks"], "i4"); } else { w.TRAddCell(" - ", "string"); } } w.TRAddCell(this._FormatTimeStringAtServer((int)rec["Spent"], nReportWorkDayDuration), null); if (bRemaining) { object objLeftTime = rec["LeftTime"]; w.TRAddCell(objLeftTime == null?string.Empty:this._FormatTimeStringAtServer((int)objLeftTime, nReportWorkDayDuration), null); } if (bNewState) { w.TRAddCell(xmlEncode(rec["NewState"]), null); } if (bComment) { w.TRAddCell(xmlEncode(rec["Comments"]), null); } w.TREnd(); } if (0 != SectionByActivity && currentTopActivityID != Guid.Empty) { w.TRStart(); w.TRAddCell(" ", null); w.TRAddCell(nCauseType == 3?"Всего по непроектным списаниям":"Всего по активности:", null, nTotalColspan, 1); if (bNumberOfTasks) { w.TRAddCell(totalTasksPerActivity, "i4"); } w.TRAddCell(this._FormatTimeStringAtServer(totalSpentPerActivity, nReportWorkDayDuration), null); if (bRemaining) { w.TRAddCell(this._FormatTimeStringAtServer(totalLeftPerActivity, nReportWorkDayDuration), null); } if (bNewState) { w.TRAddCell(" ", null); } if (bComment) { w.TRAddCell(" ", null); } w.TREnd(); } // Итого if ((int)ExpencesType.Both == (int)ExpenseType || (int)ExpencesType.Incidents == (int)ExpenseType) { w.TRStart(); w.TRAddCell(" ", null, 1, 1, "TABLE_HEADER"); w.TRAddCell("Итого затрачено на задания по инцидентам", null, nTotalColspan, 1, "TABLE_HEADER"); if (bNumberOfTasks) { w.TRAddCell(totalTasks, "i4", 1, 1, "TABLE_HEADER"); } w.TRAddCell(this._FormatTimeStringAtServer(totalSpent, nReportWorkDayDuration), null, 1, 1, "TABLE_HEADER"); if (bRemaining) { w.TRAddCell(this._FormatTimeStringAtServer(totalLeft, nReportWorkDayDuration), null, 1, 1, "TABLE_HEADER"); } if (bNewState) { w.TRAddCell(" ", null, 1, 1, "TABLE_HEADER"); } if (bComment) { w.TRAddCell(" ", null, 1, 1, "TABLE_HEADER"); } w.TREnd(); } if ((int)ExpencesType.Both == (int)ExpenseType || (int)ExpencesType.Discarding == (int)ExpenseType) { w.TRStart(); w.TRAddCell(" ", null, 1, 1, "TABLE_HEADER"); w.TRAddCell("Итого списано ", null, nTotalColspan, 1, "TABLE_HEADER"); if (bNumberOfTasks) { w.TRAddCell(" ", null, 1, 1, "TABLE_HEADER"); } w.TRAddCell(this._FormatTimeStringAtServer(totalLost, nReportWorkDayDuration), null, 1, 1, "TABLE_HEADER"); if (bRemaining) { w.TRAddCell(" ", null, 1, 1, "TABLE_HEADER"); } if (bNewState) { w.TRAddCell(" ", null, 1, 1, "TABLE_HEADER"); } if (bComment) { w.TRAddCell(" ", null, 1, 1, "TABLE_HEADER"); } w.TREnd(); } if ((int)ExpencesType.Both == (int)ExpenseType) { w.TRStart(); w.TRAddCell(" ", null, 1, 1, "TABLE_HEADER"); w.TRAddCell("Общие трудозатраты ", null, nTotalColspan, 1, "TABLE_HEADER"); if (bNumberOfTasks) { w.TRAddCell(" ", null, 1, 1, "TABLE_HEADER"); } w.TRAddCell(this._FormatTimeStringAtServer(totalLost + totalSpent, nReportWorkDayDuration), null, 1, 1, "TABLE_HEADER"); if (bRemaining) { w.TRAddCell(" ", null, 1, 1, "TABLE_HEADER"); } if (bNewState) { w.TRAddCell(" ", null, 1, 1, "TABLE_HEADER"); } if (bComment) { w.TRAddCell(" ", null, 1, 1, "TABLE_HEADER"); } w.TREnd(); } w.TRStart(); w.TRAddCell(" ", null, 1, 1, "TABLE_HEADER"); w.TRAddCell(xmlEncode(string.Format("Всего рабочих дней за период - {0}, Норма рабочего времени за период - {1}", headerData["WorkDays"], this._FormatTimeStringAtServer((int)headerData["WorkDays"] * nWorkDayDuration, int.MaxValue))), null, nActivityColspan - 1, 1, "TABLE_HEADER"); w.TREnd(); w.TEnd(); w.EndPageBody(); w.EndPageSequence(); } }
private void buildThisReport(XslFOProfileWriter w, ReportParams Params, IReportDataProvider Provider) { DateTime dtActualBegin; DateTime dtActualEnd; DateTime dtBegin; DateTime dtEnd; string OrganizationName; ArrayList arrDates = new ArrayList(); object IntervalBegin = Params.GetParam("IntervalBegin").Value; object IntervalEnd = Params.GetParam("IntervalEnd").Value; object Folder = Params.GetParam("Folder").Value; object Customer = Params.GetParam("Customer").Value; int ActivityAnalysDepth = (int)Params.GetParam("ActivityAnalysDepth").Value; int InsertRestrictions = (int)Params.GetParam("InsertRestrictions").Value; int FolderType = (int)Params.GetParam("FolderType").Value; int SectionByActivity = (int)Params.GetParam("SectionByActivity").Value; int DateDetalization = (int)Params.GetParam("DateDetalization").Value; int TimeMeasureUnits = (int)Params.GetParam("TimeMeasureUnits").Value; int ExpencesType = (int)Params.GetParam("ExpensesType").Value; int IncludeSubProjects = (int)Params.GetParam("IncludeSubProjects").Value; int SortType = (int)Params.GetParam("SortType").Value; int SortOrder = (int)Params.GetParam("SortOrder").Value; int ShowColumnWorkTimeNorm = (int)Params.GetParam("ShowColumnWorkTimeNorm").Value; /*int ShowColumnOverheads = (int)Params.GetParam("ShowColumnOverheads").Value; * int ShowColumnSalaryExpenses = (int)Params.GetParam("ShowColumnSalaryExpenses").Value;*/ CustomDataForDS oCustomData = new CustomDataForDS(); oCustomData.sTempTableName = "##UsersExpences_" + Guid.NewGuid().ToString("n"); // Первый запрос возвращает 2 рекордсета // 1 - Папка, Организация, Начало, Конец // 2 - Даты для которых строится отчёт using (IDataReader rdr = Provider.GetDataReader("dsUserExpencesPrimary", oCustomData)) { rdr.Read(); // По любому одна строка :) OrganizationName = rdr.GetString(1); dtActualBegin = rdr.IsDBNull(2)?DateTime.MinValue:rdr.GetDateTime(2); dtActualEnd = rdr.IsDBNull(3)?DateTime.MaxValue:rdr.GetDateTime(3); oCustomData.dtActualBegin = dtActualBegin; oCustomData.dtActualEnd = dtActualEnd; dtBegin = IntervalBegin == null?dtActualBegin:(DateTime)IntervalBegin; dtEnd = IntervalEnd == null?dtActualEnd:(DateTime)IntervalEnd; StringBuilder sb = new StringBuilder("<fo:block>Динамика затрат сотрудников</fo:block>"); // Создадим заголовок if (0 != InsertRestrictions) { sb.Append("<fo:block/>"); sb.Append("<fo:block font-size='14px'>Параметры отчета:</fo:block>"); sb.Append("<fo:table color='#FFFFFF' text-align='left' font-size='12px' font-family='MS Sans-serif'>"); sb.Append("<fo:table-column/>"); sb.Append("<fo:table-column/>"); sb.Append("<fo:table-body>"); sb.Append("<fo:table-row>"); sb.Append("<fo:table-cell width='35%'><fo:block>Период времени:</fo:block></fo:table-cell>"); sb.Append("<fo:table-cell><fo:block>" + xmlEncode(rdr.GetString(4)) + "</fo:block></fo:table-cell>"); sb.Append("</fo:table-row>"); sb.Append("<fo:table-row>"); sb.Append("<fo:table-cell width='35%'><fo:block>Клиент:</fo:block></fo:table-cell>"); if (null == Customer) { sb.Append("<fo:table-cell><fo:block>" + xmlEncode(OrganizationName) + "</fo:block></fo:table-cell>"); } else { // ShowContextForOrganization(sID, sExtID, sDirectorEMail) sb.Append("<fo:table-cell><fo:block><fo:basic-link color=\"#ffffff\" external-destination=\"vbscript:ShowContextForOrganization("" + Customer + "","" + rdr.GetString(6) + "","" + rdr.GetString(7) + "")\">" + xmlEncode(OrganizationName) + "</fo:basic-link></fo:block></fo:table-cell>"); } sb.Append("</fo:table-row>"); if (!rdr.IsDBNull(5)) { sb.Append("<fo:table-row>"); sb.Append("<fo:table-cell width='35%'><fo:block>Активность:</fo:block></fo:table-cell>"); sb.Append("<fo:table-cell><fo:block><fo:basic-link color=\"#ffffff\" external-destination=\"vbscript:ShowContextForFolderEx("" + rdr.GetGuid(5) + "",true, " + ((dtBegin == DateTime.MinValue)?"NULL":dtBegin.ToString("#MM'/'dd'/'yyyy#")) + ", " + ((dtEnd == DateTime.MaxValue)?"NULL":dtEnd.ToString("#MM'/'dd'/'yyyy#")) + ")\">" + xmlEncode(rdr.GetString(0)) + "</fo:basic-link></fo:block></fo:table-cell>"); sb.Append("</fo:table-row>"); } sb.Append("<fo:table-row>"); sb.Append("<fo:table-cell width='35%'><fo:block>Тип активности:</fo:block></fo:table-cell>"); sb.Append("<fo:table-cell><fo:block>" + xmlEncode(FolderTypeEnumItem.ToStringOfDescriptions((FolderTypeEnum)FolderType)) + "</fo:block></fo:table-cell>"); sb.Append("</fo:table-row>"); sb.Append("<fo:table-row>"); sb.Append("<fo:table-cell width='35%'><fo:block>Глубина анализа активностей:</fo:block></fo:table-cell>"); sb.Append("<fo:table-cell><fo:block>" + xmlEncode(ActivityAnalysDepthItem.GetItem((ActivityAnalysDepth)ActivityAnalysDepth).Description) + "</fo:block></fo:table-cell>"); sb.Append("</fo:table-row>"); sb.Append("<fo:table-row>"); sb.Append("<fo:table-cell width='35%'><fo:block>Секционирование по активностям:</fo:block></fo:table-cell>"); sb.Append("<fo:table-cell><fo:block>" + xmlEncode(SectionByActivityItem.GetItem((SectionByActivity)SectionByActivity).Description) + "</fo:block></fo:table-cell>"); sb.Append("</fo:table-row>"); sb.Append("<fo:table-row>"); sb.Append("<fo:table-cell width='35%'><fo:block>Детализация по датам:</fo:block></fo:table-cell>"); sb.Append("<fo:table-cell><fo:block>" + xmlEncode(DateDetalizationItem.GetItem((DateDetalization)DateDetalization).Description) + "</fo:block></fo:table-cell>"); sb.Append("</fo:table-row>"); sb.Append("<fo:table-row>"); sb.Append("<fo:table-cell width='35%'><fo:block>Виды трудозатрат:</fo:block></fo:table-cell>"); sb.Append("<fo:table-cell><fo:block>" + xmlEncode(ExpencesTypeItem.GetItem((ExpencesType)ExpencesType).Description) + "</fo:block></fo:table-cell>"); sb.Append("</fo:table-row>"); sb.Append("<fo:table-row>"); sb.Append("<fo:table-cell width='35%'><fo:block>Включать в проект затраты подпроектов:</fo:block></fo:table-cell>"); sb.Append("<fo:table-cell><fo:block>" + ((IncludeSubProjects == 0)?"нет":"да") + "</fo:block></fo:table-cell>"); sb.Append("</fo:table-row>"); sb.Append("<fo:table-row>"); sb.Append("<fo:table-cell width='35%'><fo:block>Сортировка:</fo:block></fo:table-cell>"); sb.Append("<fo:table-cell><fo:block>" + xmlEncode(SortExpencesItem.GetItem((SortExpences)SortType).Description) + ", " + xmlEncode(SortOrderItem.GetItem((SortOrder)SortOrder).Description) + "</fo:block></fo:table-cell>"); sb.Append("</fo:table-row>"); sb.Append("<fo:table-row>"); sb.Append("<fo:table-cell width='35%'><fo:block>Единицы измерения времени:</fo:block></fo:table-cell>"); sb.Append("<fo:table-cell><fo:block>" + xmlEncode(TimeMeasureUnitsItem.GetItem((TimeMeasureUnits)TimeMeasureUnits).Description) + "</fo:block></fo:table-cell>"); sb.Append("</fo:table-row>"); sb.Append("</fo:table-body>"); sb.Append("</fo:table>"); } if (rdr.NextResult()) { while (rdr.Read()) { arrDates.Add(rdr.GetDateTime(0)); } } oCustomData.arrDates = arrDates; w.WriteLayoutMaster(); w.StartPageSequence(); w.StartPageBody(); w.Header(sb.ToString()); } if (dtActualBegin == DateTime.MinValue) { // Пустой отчёт, данные не нужны, табличка дропнута w.EmptyBody("Нет данных"); } else { // Теперь сформируем вторичный запрос на получение данных using (IDataReader rdr2 = Provider.GetDataReader("dsUserExpencesSecondary", oCustomData)) { int nActivityColSpan = 2; int nTotalColspan = 1; int nFieldCount = rdr2.FieldCount; const int nFirstDateColumn = 9; // Выведем тело отчёта w.TStart(true, "CELL_CLASS", false); /* * if(HideGroupColumns) * { ++nActivityColSpan; * w.TAddColumn("Активность", align.ALIGN_LEFT, valign.VALIGN_MIDDLE, "TABLE_HEADER"); * } */ w.TAddColumn("Сотрудник", align.ALIGN_LEFT, valign.VALIGN_MIDDLE, "TABLE_HEADER"); if (0 != ShowColumnWorkTimeNorm) { w.TAddColumn("Норма рабочего времени", align.ALIGN_CENTER, valign.VALIGN_MIDDLE, "TABLE_HEADER"); ++nActivityColSpan; ++nTotalColspan; } w.TAddColumn("Сумма затрат (час)", align.ALIGN_CENTER, valign.VALIGN_MIDDLE, "TABLE_HEADER"); //w.TAddColumn("Сумма затрат", align.ALIGN_RIGHT, valign.VALIGN_MIDDLE, "TABLE_HEADER"); /*if(0!=ShowColumnOverheads) * { * w.TAddColumn("Накладные расходы", align.ALIGN_RIGHT, valign.VALIGN_MIDDLE, "TABLE_HEADER"); ++nActivityColSpan; * } * if(0!=ShowColumnSalaryExpenses) * { * w.TAddColumn("Затраты на з/п", align.ALIGN_RIGHT, valign.VALIGN_MIDDLE, "TABLE_HEADER"); ++nActivityColSpan; * } */ for (int i = nFirstDateColumn; i < nFieldCount; ++i) { w.TAddColumn(rdr2.GetName(i), align.ALIGN_CENTER, valign.VALIGN_MIDDLE, "TABLE_HEADER"); ++nActivityColSpan; } Guid prevFolderID = Guid.Empty; int[] arrLocalTotals = new int[nFieldCount]; int[] arrGlobalTotals = new int[nFieldCount]; int nLocalTotalTime = 0; int nGlobalTotalTime = 0; int nTimeFormatValue = (TimeMeasureUnits == (int)IncidentTracker.TimeMeasureUnits.Hours)?int.MaxValue:/*rdr.GetInt32(5)*/ 600; while (rdr2.Read()) { if (SectionByActivity != (int)Croc.IncidentTracker.SectionByActivity.NoSection) { Guid thisFolderID = rdr2.GetGuid(0); if (thisFolderID != prevFolderID) { if (prevFolderID != Guid.Empty) { // Итого ;) w.TRStart("SUBTOTAL"); w.TRAddCell("Итого по активности:", null, nTotalColspan, 1); w.TRAddCell(xmlEncode(_FormatTimeStringAtServer(nLocalTotalTime, nTimeFormatValue)), null); //w.TRAddCell(rLocalTotalCost, fixed_14_4); /*if(0!=ShowColumnOverheads) * w.TRAddCell( rLocalTotalOverheads, fixed_14_4); * if(0!=ShowColumnSalaryExpenses) * w.TRAddCell( rLocalTotalSalaryExpenses, fixed_14_4);*/ for (int i = nFirstDateColumn; i < nFieldCount; ++i) { w.TRAddCell(xmlEncode(_FormatTimeStringAtServer(arrLocalTotals[i], nTimeFormatValue)), null); } w.TREnd(); // Почистим локальный "ИТОГО" nLocalTotalTime = 0; System.Array.Clear(arrLocalTotals, 0, arrLocalTotals.Length); } prevFolderID = thisFolderID; // Сформируем подзаголовок w.TRStart("SUBTITLE"); w.TRAddCell(this._GetFolderAnchor(rdr2.GetString(1), thisFolderID, Guid.Empty, true, dtBegin, dtEnd), null, nActivityColSpan, 1); w.TREnd(); } } bool bIncludeInGlobals = rdr2.GetInt32(7) == 0; w.TRStart("CELL_CLASS"); w.TRAddCell(_GetUserMailAnchor(rdr2.GetString(3), rdr2.GetString(4), rdr2.GetGuid(2), Guid.Empty, rdr2.IsDBNull(0)?(Folder == null?Guid.Empty:((Guid)Folder)):rdr2.GetGuid(0)), null); if (0 != ShowColumnWorkTimeNorm) { w.TRAddCell(xmlEncode(this._FormatTimeStringAtServer(rdr2.GetInt32(8), nTimeFormatValue)), null); } int nTempInt32 = rdr2.GetInt32(6); w.TRAddCell(xmlEncode(_FormatTimeStringAtServer(nTempInt32, nTimeFormatValue)), null); if (bIncludeInGlobals) { nGlobalTotalTime += nTempInt32; } nLocalTotalTime += nTempInt32; /*decimal rTempDecimal = rdr2.GetDecimal(11); * w.TRAddCell( rTempDecimal, fixed_14_4); * if(bIncludeInGlobals) rGlobalTotalCost += rTempDecimal; * rLocalTotalCost += rTempDecimal;*/ /*if(0!=ShowColumnOverheads) * { * rTempDecimal = rdr2.GetDecimal(7); * w.TRAddCell( rTempDecimal, fixed_14_4); * if(bIncludeInGlobals) rGlobalTotalOverheads += rTempDecimal; * rLocalTotalOverheads += rTempDecimal; * } * if(0!=ShowColumnSalaryExpenses) * { * rTempDecimal = rdr2.GetDecimal(8); * w.TRAddCell( rTempDecimal, fixed_14_4); * if(bIncludeInGlobals) rGlobalTotalSalaryExpenses += rTempDecimal; * rLocalTotalSalaryExpenses += rTempDecimal; * }*/ for (int i = nFirstDateColumn; i < nFieldCount; ++i) { nTempInt32 = rdr2.GetInt32(i); w.TRAddCell(xmlEncode(_FormatTimeStringAtServer(nTempInt32, nTimeFormatValue)), null); if (bIncludeInGlobals) { arrGlobalTotals[i] = arrGlobalTotals[i] + nTempInt32; } arrLocalTotals[i] = arrLocalTotals[i] + nTempInt32; } w.TREnd(); } if (SectionByActivity != (int)Croc.IncidentTracker.SectionByActivity.NoSection) { if (prevFolderID != Guid.Empty) { // Итого ;) w.TRStart("SUBTOTAL"); w.TRAddCell("Итого по активности:", null, nTotalColspan, 1); w.TRAddCell(xmlEncode(_FormatTimeStringAtServer(nLocalTotalTime, nTimeFormatValue)), null); //w.TRAddCell(rLocalTotalCost, fixed_14_4); /*if(0!=ShowColumnOverheads) * w.TRAddCell( rLocalTotalOverheads, fixed_14_4); * if(0!=ShowColumnSalaryExpenses) * w.TRAddCell( rLocalTotalSalaryExpenses, fixed_14_4); */ for (int i = nFirstDateColumn; i < nFieldCount; ++i) { w.TRAddCell(xmlEncode(_FormatTimeStringAtServer(arrLocalTotals[i], nTimeFormatValue)), null); } w.TREnd(); } } // Итого ;) w.TRStart("TABLE_FOOTER"); w.TRAddCell("Итого:", null, nTotalColspan, 1); w.TRAddCell(xmlEncode(_FormatTimeStringAtServer(nGlobalTotalTime, nTimeFormatValue)), null); //w.TRAddCell(rGlobalTotalCost, fixed_14_4); /*if(0!=ShowColumnOverheads) * w.TRAddCell( rGlobalTotalOverheads, fixed_14_4); * if(0!=ShowColumnSalaryExpenses) * w.TRAddCell( rGlobalTotalSalaryExpenses, fixed_14_4); */ for (int i = nFirstDateColumn; i < nFieldCount; ++i) { w.TRAddCell(xmlEncode(_FormatTimeStringAtServer(arrGlobalTotals[i], nTimeFormatValue)), null); } w.TREnd(); w.TEnd(); } } w.EndPageBody(); w.EndPageSequence(); }