private void checkFolderStateChanging(FolderStates stateOld, FolderStates stateNew) { // TODO: требуется уточнение графа переходов состояний Папки if (stateOld == FolderStates.Frozen && stateNew != FolderStates.Open && stateNew != FolderStates.WaitingToClose) { throw new XBusinessLogicException("Папка из состояния \"" + FolderStatesItem.Frozen.Description + "\" может перейти только в состояния \"" + FolderStatesItem.Open.Description + "\" или \"" + FolderStatesItem.WaitingToClose.Description + "\""); } if (stateNew == FolderStates.Frozen && stateOld != FolderStates.Open && stateOld != FolderStates.WaitingToClose) { throw new XBusinessLogicException("В состояние \"" + FolderStatesItem.Frozen.Description + "\" папка может перейти только из состояний \"" + FolderStatesItem.Open.Description + "\" или \"" + FolderStatesItem.WaitingToClose.Description + "\""); } }
protected override void buildReport(Croc.XmlFramework.ReportService.Layouts.ReportLayoutData data) { Guid folderID = (Guid)data.Params.GetParam("ID").Value; IDictionary f = null; using (IDataReader r = data.DataProvider.GetDataReader("dsMain", data.CustomData)) { if (r.Read()) { f = _GetDataFromDataRow(r); } } if (null == f) { // Проект не найден writeEmptyBody(data.RepGen, "Проект не найден"); return; } data.RepGen.WriteLayoutMaster(); data.RepGen.StartPageSequence(); data.RepGen.StartPageBody(); data.RepGen.Header("<fo:basic-link color=\"#ffffff\" external-destination=\"x-tree.aspx?METANAME=Main&LocateFolderByID=" + folderID.ToString() + "\" target=\"_blank\" show-destination=\"new\">" + xmlEncode(f["Name"]) + "</fo:basic-link>"); data.RepGen.TStart(false, "CELL_CLASS", false); data.RepGen.TAddColumn(null, align.ALIGN_LEFT, valign.VALIGN_TOP, "30%"); data.RepGen.TAddColumn(null, align.ALIGN_LEFT, valign.VALIGN_TOP, "70%"); // основные свойства проекта data.RepGen.TRStart(); data.RepGen.TRAddCell("Клиент", null, 1, 1, "BOLD"); data.RepGen.TRAddCell(xmlEncode(f["CustomerName"]), null); data.RepGen.TREnd(); data.RepGen.TRStart(); data.RepGen.TRAddCell("Название", null, 1, 1, "BOLD"); data.RepGen.TRAddCell(_GetFolderAnchor(f["Name"], (Guid)f["ObjectID"], false), null); data.RepGen.TREnd(); if (null != f["Description"]) { data.RepGen.TRStart(); data.RepGen.TRAddCell("Описание", null, 1, 1, "BOLD"); data.RepGen.TRAddCell(_LongText(f["Description"]), null); data.RepGen.TREnd(); } if (null != f["FirstDate"]) { data.RepGen.TRStart(); data.RepGen.TRAddCell("Дата начала", null, 1, 1, "BOLD"); data.RepGen.TRAddCell(xmlEncode(((DateTime)f["FirstDate"]).ToLongDateString()) + " (" + getUserMailAnchor(f["FirstName"] + ", " + f["FirstDep"], f["FirstMail"], (Guid)f["FirstID"], folderID) + ")", null); data.RepGen.TREnd(); } if (null != f["LastDate"]) { data.RepGen.TRStart(); data.RepGen.TRAddCell("Дата последней активности", null, 1, 1, "BOLD"); data.RepGen.TRAddCell(xmlEncode(((DateTime)f["LastDate"]).ToLongDateString()) + " (" + getUserMailAnchor(f["LastName"] + ", " + f["LastDep"], f["LastMail"], (Guid)f["LastID"], folderID) + ")", null); data.RepGen.TREnd(); } FolderStates folderState = (FolderStates)f["State"]; data.RepGen.TRStart(); data.RepGen.TRAddCell("Состояние", null, 1, 1, "BOLD"); data.RepGen.TRAddCell(xmlEncode(FolderStatesItem.GetItem(folderState).Description), null); data.RepGen.TREnd(); FolderTypeEnum folderType = (FolderTypeEnum)f["Type"]; data.RepGen.TRStart(); data.RepGen.TRAddCell("Тип", null, 1, 1, "BOLD"); data.RepGen.TRAddCell(xmlEncode(FolderTypeEnumItem.GetItem(folderType).Description), null); data.RepGen.TREnd(); data.RepGen.TRStart(); data.RepGen.TRAddCell("Тип проектной активности", null, 1, 1, "BOLD"); data.RepGen.TRAddCell(xmlEncode(f["ActivityTypeName"]), null); data.RepGen.TREnd(); // Код проекта: // - только для активности; // - м.б. не задан: в этом случае показываем что данных нет if (folderType != FolderTypeEnum.Directory) { data.RepGen.TRStart(); data.RepGen.TRAddCell("Код", null, 1, 1, "BOLD"); if (null != f["ExternalID"]) { data.RepGen.TRAddCell(xmlEncode(f["ExternalID"]), null); } else { data.RepGen.TRAddCell(xmlEncode("(не задан)"), null); } data.RepGen.TREnd(); } if (null != f["DefaultIncidentTypeName"]) { data.RepGen.TRStart(); data.RepGen.TRAddCell("Тип инцидента по умолчанию", null, 1, 1, "BOLD"); data.RepGen.TRAddCell(xmlEncode(f["DefaultIncidentTypeName"]), null); data.RepGen.TREnd(); } if ("0" != f["IsLocked"].ToString()) { data.RepGen.TRStart(); data.RepGen.TRAddCell("", null, 1, 1, "BOLD"); data.RepGen.TRAddCell("Списания на папку заблокированы", null, 1, 1, "BOLD-RED"); data.RepGen.TREnd(); } int nTotalSpent = (int)f["SummarySpent"]; if (0 != nTotalSpent) { data.RepGen.TRStart(); data.RepGen.TRAddCell("Суммарные трудозатраты", null, 1, 1, "BOLD"); data.RepGen.TRAddCell( string.Format("<fo:basic-link external-destination=\"x-get-report.aspx?name=r-ProjectIncidentsAndExpenses.xml&Folder={1}\">{0}</fo:basic-link>", xmlEncode(_FormatTimeStringAtServer(nTotalSpent, int.MaxValue)), folderID) , null); data.RepGen.TREnd(); } // Направления StringBuilder directions = new StringBuilder(); using (IDataReader r = data.DataProvider.GetDataReader("dsDirections", data.CustomData)) { while (r.Read()) { directions.AppendFormat("<fo:block>{0}</fo:block>", xmlEncode(r.GetString(0))); } } if (directions.Length != 0) { // Шапка data.RepGen.TRStart(); data.RepGen.TRAddCell("Направления", null, 1, 1, "BOLD"); data.RepGen.TRAddCell(directions.ToString(), null); data.RepGen.TREnd(); } data.RepGen.TEnd(); // проектная команда insertAdditionalProjectReports(data, folderID); // история insertFolderHistory(data, folderID); // инциденты insertWorkStaff(data, folderID); data.RepGen.EndPageBody(); data.RepGen.EndPageSequence(); }
public override void Execute(XTriggerArgs args, Croc.XmlFramework.Core.IXExecutionContext context) { DomainObjectData xobj = args.TriggeredObject; bool bUpdatedState = xobj.HasUpdatedProp("State"); bool bUpdateIsLocked = xobj.HasUpdatedProp("IsLocked"); if (!bUpdatedState && !bUpdateIsLocked) { return; } // если здесь значит изменилось хотя бы одно из полей: Состояние (State), Дата блокирования списаний (BlockDate) // Теперь надо зачитать предыдущие значения из БД, но только тех свойств, которые обновляются preloadProps(xobj, bUpdatedState, bUpdateIsLocked, context); if (bUpdateIsLocked) { bool oldValue = (bool)xobj.GetLoadedPropValue("IsLocked"); bool newValue = (bool)xobj.GetUpdatedPropValue("IsLocked"); if (oldValue != newValue) { // изменение признака допустимости списания DomainObjectData xobjHistory = getFolderHistoryObject(args.DataSet, xobj); xobjHistory.SetUpdatedPropValue("Event", newValue?FolderHistoryEvents.IsLockedSetToTrue:FolderHistoryEvents.IsLockedSetToFalse); } } if (bUpdatedState) { FolderStates stateOld = (FolderStates)xobj.GetLoadedPropValue("State"); FolderStates stateNew = (FolderStates)xobj.GetUpdatedPropValue("State"); if (stateOld != stateNew) { // состояние изменилось // - проверим на запрещенные переходы checkFolderStateChanging(stateOld, stateNew); DomainObjectData xobjHistory = getFolderHistoryObject(args.DataSet, xobj); FolderHistoryEvents eventType; if (stateNew == FolderStates.Closed) { eventType = FolderHistoryEvents.Closing; } else if (stateNew == FolderStates.Frozen) { eventType = FolderHistoryEvents.Frozing; } else if (stateNew == FolderStates.WaitingToClose) { eventType = FolderHistoryEvents.WaitingToClose; } else // if (stateNew == FolderStates.Open) { eventType = FolderHistoryEvents.Opening; } xobjHistory.SetUpdatedPropValue("Event", eventType); // обработаем переход в состояние "Закрыто": if (!xobj.IsNew && (stateNew == FolderStates.Closed || stateNew == FolderStates.WaitingToClose)) { // 1. Проверим, что все инциденты (во всех вложенных папках) находятся в конечных состояниях XDbCommand cmd = context.Connection.CreateCommand(@" SELECT 1 FROM Folder f_s WITH(NOLOCK) JOIN Folder f WITH(NOLOCK) ON f.LIndex >= f_s.LIndex AND f.RIndex <= f_s.RIndex AND f.Customer = f_s.Customer JOIN Incident i WITH(NOLOCK) ON f.ObjectID = i.Folder JOIN IncidentState i_st WITH(NOLOCK) ON i.State = i_st.ObjectID AND i_st.Category IN (1,2) WHERE f_s.ObjectID = @ObjectID " ); cmd.Parameters.Add("ObjectID", DbType.Guid, ParameterDirection.Input, false, xobj.ObjectID); if (cmd.ExecuteScalar() != null) { throw new XBusinessLogicException("Папка не может быть переведена в состояние \"Закрыто\" или \"Ожидание закрытия\", так как содержит незавершенные инциденты"); } } // добавим в датаграмму подчиненные папки с установленным состоянием новым состояние // Обработка папок зависит от нового состояния fillDataSetWithChildFoldersWithUpdatedState(context.Connection, args.DataSet, xobj.ObjectID, stateNew); } } }
private void fillDataSetWithChildFoldersWithUpdatedState(XStorageConnection con, DomainObjectDataSet dataSet, Guid objectID, FolderStates folderState) { // зачитаем идентификаторы всех подчиненных папок, состояние которых отличается от требуемого XDbCommand cmd = con.CreateCommand(@" SELECT f.ObjectID FROM Folder f_s WITH(NOLOCK) JOIN Folder f WITH(NOLOCK) ON f.LIndex > f_s.LIndex AND f.RIndex < f_s.RIndex AND f.Customer = f_s.Customer WHERE f_s.ObjectID = @ObjectID AND f.State <> @TargetState " ); // закрытие закрывает все вложенные папки без учета их состояния: // if (folderState == FolderStates.Closed) - Nothing to do // замораживание замораживает открытые и ожидающие закрытия if (folderState == FolderStates.Frozen) { cmd.CommandText = cmd.CommandText + " AND f.State IN (" + FolderStatesItem.Open.IntValue + "," + FolderStatesItem.WaitingToClose.IntValue + ")"; } // перевод в "ожидание закрытие" применим только для открытых (т.е. замороженные и закрытые не трогаются) //else if (folderState == FolderStates.WaitingToClose) //cmd.CommandText = cmd.CommandText + " AND f.State = " + FolderStatesItem.Open.IntValue; cmd.Parameters.Add("ObjectID", DbType.Guid, ParameterDirection.Input, false, objectID); cmd.Parameters.Add("TargetState", DbType.Int16, ParameterDirection.Input, false, (Int16)folderState); using (IDataReader reader = cmd.ExecuteReader()) { DomainObjectData xobjSubFolder; while (reader.Read()) { xobjSubFolder = dataSet.GetLoadedStub("Folder", reader.GetGuid(0)); xobjSubFolder.SetUpdatedPropValue("State", folderState); } } }