/// <summary> /// Метод запуска операции на выполнение, «входная» точка операции /// ПЕРЕГРУЖЕННЫЙ, СТРОГО ТИПИЗИРОВАННЫЙ МЕТОД /// ВЫЗЫВАЕТСЯ ЯДРОМ АВТОМАТИЧЕСКИ /// </summary> /// <param name="request">Запрос на выполнение операции</param> /// <param name="context">Контекст выполнения операции</param> /// <returns>Результат выполнения</returns> public virtual XResponse Execute(SaveObjectInternalRequest request, IXExecutionContext context) { // #1: Проверка прав // Примечание: делаем это здесь, а не в гварде, ради человеческой диагностики XSecurityManager sec_man = XSecurityManager.Instance; IEnumerator enumerator = request.DataSet.GetModifiedObjectsEnumerator(false); DomainObjectData xobj; while (enumerator.MoveNext()) { xobj = (DomainObjectData)enumerator.Current; if (xobj.ToDelete) { sec_man.DemandDeleteObjectPrivilege(xobj); } else { sec_man.DemandSaveObjectPrivilege(xobj); } } // #2: Запись данных XStorageGateway.Save(context, request.DataSet, request.TransactionID); // #3: Вызовем post-call-процедуры (если таковые определены) //executePostCalls(request.PostCalls, context); // Специального результата операция не возвращает return(new XResponse()); }
public XResponse Execute(MoveObjectsRequest request, IXExecutionContext context) { DomainObjectDataSet dataSet = new DomainObjectDataSet(context.Connection.MetadataManager.XModel); foreach (Guid oid in request.SelectedObjectsID) { DomainObjectData xobj = dataSet.CreateStubLoaded(request.SelectedObjectType, oid, -1); xobj.SetUpdatedPropValue(request.ParentPropName, request.NewParent); // для объекта проверим права XSecurityManager.Instance.DemandSaveObjectPrivilege(xobj); } XStorageGateway.Save(context, dataSet, Guid.NewGuid()); return(new XResponse()); }
/// <summary> /// Метод выполнения операции, типизированная реализация /// </summary> /// <param name="oRequest">Объект-запрос на выполнение операции</param> /// <param name="oContext">Представление контекста выполнения операции</param> /// <returns> /// Экземпляр объекта-результата выполнения операции /// </returns> public XResponse Execute(XDeleteObjectRequest oRequest, IXExecutionContext oContext) { // Проверка параметров запроса XRequest.ValidateRequiredArgument(oRequest.TypeName, "XDeleteObjectRequest.TypeName"); XRequest.ValidateRequiredArgument(oRequest.ObjectID, "XDeleteObjectRequest.ObjectID"); DomainObjectData objData = DomainObjectData.CreateStubLoaded(oContext.Connection, oRequest.TypeName, oRequest.ObjectID); XObjectRights rights = XSecurityManager.Instance.GetObjectRights(objData); if (!rights.AllowDelete) { throw new XSecurityException(String.Format( "Выполнение операции невозможно: нет прав на удаление объекта \"{0}\" ({1}[oid='{2}'])", objData.TypeInfo.Description, oRequest.TypeName, oRequest.ObjectID )); } // Вызываем метод подсистемы Storage (уровень Level-2); экземпляр // Storage представлен в рамках контекста выполнения опеарции return(new XDeleteObjectResponse(XStorageGateway.Delete(oContext, oRequest.TypeName, oRequest.ObjectID))); }
/// <summary> /// Типизированная реализация /// </summary> /// <param name="request">Запрос на выполнение операции</param> /// <param name="context">Контекст выполнения операции</param> /// <returns>Результат выполнения</returns> public XResponse Execute(UpdateActivityStateRequest request, IXExecutionContext context) { //для того, чтобы изменение прошло от именя сотрудника, переданного в request.Initiator, //прейдется подменить CurrentPrincipal // для начала запомним текущий IPrincipal originalPrincipal = Thread.CurrentPrincipal; try { // если подсунули нам инициатора, вытащим имя пользователя и подменим CurrentPrincipal { var ds = new DomainObjectDataSet(context.Connection.MetadataManager.XModel); if (request.Initiator != Guid.Empty) { var employee = ds.Load(context.Connection, "Employee", request.Initiator); var userID = employee.GetLoadedPropValue("SystemUser"); if (userID == DBNull.Value) { throw new XBusinessLogicException("Сотрудник не является пользователем системы"); } var user = ds.Load(context.Connection, "SystemUser", (Guid)userID); Thread.CurrentPrincipal = new GenericPrincipal( new GenericIdentity((string)user.GetLoadedPropValue("Login")), new string[] { "XUser" }); } } // собственно внесем изменение { var ds = new DomainObjectDataSet(context.Connection.MetadataManager.XModel); // Загрузим объект var activity = ds.Load(context.Connection, "Folder", request.Activity); activity.SetUpdatedPropValue("State", request.NewState); // Если задано, то и описание обновим if (!String.IsNullOrEmpty(request.Description)) { var description = activity.GetLoadedPropValueOrLoad(context.Connection, "Description"); activity.SetUpdatedPropValue( "Description", description == DBNull.Value || string.IsNullOrEmpty((string)description) ? request.Description : string.Format("{0}\n{1}", (string)description, request.Description) ); } XStorageGateway.Save(context, ds, Guid.NewGuid()); } } finally { // в любом случае вернем все как было Thread.CurrentPrincipal = originalPrincipal; } return(new XResponse()); }
public XResponse Execute(MoveFolderRequest request, IXExecutionContext context) { XDbCommand cmd; DomainObjectDataSet dataSet = new DomainObjectDataSet(context.Connection.MetadataManager.XModel); foreach (Guid oid in request.ObjectsID) { DomainObjectData xobj = dataSet.CreateStubLoaded("Folder", oid, -1); if (request.NewParent != Guid.Empty) { // задана родительская папка xobj.SetUpdatedPropValue("Parent", request.NewParent); // родительская папка задана, однако, она может принадлежать другому клиенту и/или типу проектных затрат cmd = context.Connection.CreateCommand(@" SELECT CASE WHEN f1.Customer <> f2.Customer THEN f1.Customer ELSE cast(NULL as uniqueidentifier) END AS Customer, CASE WHEN f1.ActivityType <> f2.ActivityType THEN f1.ActivityType ELSE cast(NULL as uniqueidentifier) END AS ActivityType FROM Folder f1, Folder f2 WHERE f1.ObjectID = @NewParentID AND f2.ObjectID = @ObjectID AND (f1.Customer <> f2.Customer OR f1.ActivityType <> f2.ActivityType) " ); cmd.Parameters.Add("NewParentID", DbType.Guid, ParameterDirection.Input, false, request.NewParent); cmd.Parameters.Add("ObjectID", DbType.Guid, ParameterDirection.Input, false, oid); using (IDataReader reader = cmd.ExecuteReader()) { int nIndex; if (reader.Read()) { nIndex = reader.GetOrdinal("Customer"); if (!reader.IsDBNull(nIndex)) { xobj.SetUpdatedPropValue("Customer", reader.GetGuid(nIndex)); } nIndex = reader.GetOrdinal("ActivityType"); if (!reader.IsDBNull(nIndex)) { xobj.SetUpdatedPropValue("ActivityType", reader.GetGuid(nIndex)); } } } // Далее мы проверяем следующее: // каталог может быть подчинен папке любого типа, однако проект только проекту, // а пресейл и тендер вообще никому cmd.CommandText = @" SELECT f1.Type AS FolderType, f2.Type AS ParentFolderType, f1.LIndex, f1.RIndex, f2.LIndex AS ParentLIndex, f2.RIndex AS ParentRIndex FROM Folder f1, Folder f2 WHERE f1.ObjectID = @ObjectID AND f2.ObjectID = @NewParentID " ; // Примечание: используем созданную ранее команду с параметрами NewParentID и ObjectID using (IDataReader reader = cmd.ExecuteReader()) { if (reader.Read()) { FolderTypeEnum folderType = (FolderTypeEnum)reader.GetInt16(reader.GetOrdinal("FolderType")); FolderTypeEnum parentFolderType = (FolderTypeEnum)reader.GetInt16(reader.GetOrdinal("ParentFolderType")); // Если у папки не изменилась ссылка на организацию клиента, то проверим, что ее не переносят в одну из дочерних папок if (!xobj.HasUpdatedProp("Customer")) { // Примечание: LIndex/RIndex будут пересчитываться в триггере int nLIndex = reader.GetInt32(reader.GetOrdinal("LIndex")); int nRIndex = reader.GetInt32(reader.GetOrdinal("RIndex")); int nParentLIndex = reader.GetInt32(reader.GetOrdinal("ParentLIndex")); int nParentRIndex = reader.GetInt32(reader.GetOrdinal("ParentRIndex")); // Проверим, что новый родитель не является дочерним (рекурсивно) узлом переносимой папки if (nParentLIndex >= nLIndex && nParentRIndex <= nRIndex) { throw new XBusinessLogicException(FolderTypeEnumItem.GetItem(folderType).Description + " не может быть перенесен в подчиненный " + FolderTypeEnumItem.GetItem(parentFolderType).Description.ToLower()); } } if (folderType == FolderTypeEnum.Project) { if (parentFolderType != FolderTypeEnum.Project) { throw new XBusinessLogicException("Проект не может быть перенесен в " + FolderTypeEnumItem.GetItem(parentFolderType).Description.ToLower()); } } else if (folderType == FolderTypeEnum.Tender || folderType == FolderTypeEnum.Presale) { throw new XBusinessLogicException("Тендер (тендерная активность) и пресейл (пресейл-активноть) не могут быть перенесены в папку"); } } } } else { // перенос в корень xobj.SetUpdatedPropValue("Parent", DBNull.Value); // однако при этом мог измениться клиент или тип проектных затрат if (request.NewActivityType != Guid.Empty) { // если изменился тип проектных затрат, то ссылка на клиента также должна быть задана (даже если он не изменился) if (request.NewCustomer == Guid.Empty) { throw new ArgumentException("Если задана ссылка на тип проектных затрат, то должна быть задана ссылка на организацию-клиента"); } // также нужно проверить, что выбранный типа проектных затрат поддерживает тип переносимой папки cmd = context.Connection.CreateCommand(@" SELECT 1 FROM ActivityType WHERE ObjectID = @ActivityTypeID AND FolderType & (SELECT [Type] FROM Folder WHERE ObjectID = @ObjectID) > 0 " ); cmd.Parameters.Add("ActivityTypeID", DbType.Guid, ParameterDirection.Input, false, request.NewActivityType); cmd.Parameters.Add("ObjectID", DbType.Guid, ParameterDirection.Input, false, oid); if (cmd.ExecuteScalar() == null) { throw new XBusinessLogicException("Перенос папки невозможен. Выбранный тип проектных затрат не может содержать папку переносимого типа."); } xobj.SetUpdatedPropValue("ActivityType", request.NewActivityType); xobj.SetUpdatedPropValue("Customer", request.NewCustomer); } else if (request.NewCustomer != Guid.Empty) { // Если выбрали организацию без изменения типа проектных затрат, то подразумевается, что он (ActivityTyoe) остается прежним, // однако такое возможно только при переносе либо между организациями-клиентами, либо между организациями-владельцами. cmd = context.Connection.CreateCommand(@" SELECT 1 FROM Organization c1, Organization c2 WHERE c1.ObjectID = (SELECT Customer FROM Folder WHERE ObjectID = @ObjectID) AND c2.ObjectID = @NewCustomerID AND c1.Home <> c2.Home " ); cmd.Parameters.Add("NewCustomerID", DbType.Guid, ParameterDirection.Input, false, request.NewCustomer); cmd.Parameters.Add("ObjectID", DbType.Guid, ParameterDirection.Input, false, oid); if (cmd.ExecuteScalar() != null) { throw new XBusinessLogicException("Перенос папки невозможен. Для переноса папок между разными типами проектных затрат следует выбрать узел, соответствующий требуемому типу проектных затрат."); } xobj.SetUpdatedPropValue("Customer", request.NewCustomer); } else { // Parent = Null, Customer = Null, ActivityType = Null - такого быть не может throw new ArgumentException("При незаданной ссылке на родительскую папку должны быть заданы ссылки на Клиента и/или Тип проектных затрат"); } } XSecurityManager.Instance.DemandSaveObjectPrivilege(xobj); } XStorageGateway.Save(context, dataSet, Guid.NewGuid()); // если все сохранилось хорошо, то отработали триггеры, изменяющие LIndex/RIndex, и возможно Customer. // Если у переносимой папки изменилась ссылка на тип проектных затрат , // то необходимо изменить эту ссылку всем подчиненным папкам (если они есть) StringBuilder cmdBuilder = new StringBuilder(); cmdBuilder.AppendFormat( @"UPDATE f SET f.ActivityType = p.ActivityType FROM Folder f JOIN Folder p ON f.LIndex > p.LIndex AND f.RIndex < p.RIndex AND f.Customer = p.Customer WHERE p.ObjectID IN (" ); cmd = context.Connection.CreateCommand(); string sParamName; int nParamIndex = 0; foreach (Guid oid in request.ObjectsID) { ++nParamIndex; sParamName = "ObjectID" + nParamIndex; cmd.Parameters.Add(sParamName, XPropType.vt_uuid, ParameterDirection.Input, false, oid); cmdBuilder.Append(context.Connection.GetParameterName(sParamName)); cmdBuilder.Append(","); } // Отрежим последнюю запятую cmdBuilder.Length--; cmdBuilder.Append(")"); cmd.CommandText = cmdBuilder.ToString(); cmd.ExecuteNonQuery(); // Если у переносимой папки были заданы направления, которые не соответствуют направлениям новой // родительской папки, то удалим эти направления if (request.NewParent != Guid.Empty) { cmdBuilder = new StringBuilder(); cmd = context.Connection.CreateCommand(); nParamIndex = 0; foreach (Guid oid in request.ObjectsID) { ++nParamIndex; sParamName = "FolderID" + nParamIndex; cmd.Parameters.Add(sParamName, XPropType.vt_uuid, ParameterDirection.Input, false, oid); cmdBuilder.Append(context.Connection.GetParameterName(sParamName)); cmdBuilder.Append(","); } // Отрежим последнюю запятую cmdBuilder.Length--; cmd.CommandText = @"IF (EXISTS( SELECT top 1 fd.Direction FROM dbo.FolderDirection fd WHERE (fd.Direction not in ( SELECT Direction FROM dbo.FolderDirection WHERE Folder = @ParentID) AND fd.Folder IN (" + cmdBuilder.ToString() + @")) OR EXISTS( SELECT COUNT(*) FROM dbo.FolderDirection WHERE Folder IN (" + cmdBuilder.ToString() + @") HAVING COUNT(*)>1))) AND EXISTS( SELECT TOP 1 * FROM dbo.Folder WHERE ObjectID = @ParentID ) BEGIN DELETE fd FROM FolderDirection fd WHERE fd.Folder IN (" + cmdBuilder.ToString() + @") END" ; cmd.Parameters.Add("ParentID", DbType.Guid, ParameterDirection.Input, false, request.NewParent); cmd.ExecuteNonQuery(); } return(new XResponse()); }
public XResponse Execute(SaveObjectInternalRequest request, IXExecutionContext context) { ITUser user = (ITUser)XSecurityManager.Instance.GetCurrentUser(); ArrayList aObjects = request.DataSet.GetModifiedObjectsByType("TimeLoss", true); foreach (DomainObjectData xobj in aObjects) { if (!xobj.HasUpdatedProp("Worker") && xobj.IsNew) { xobj.SetUpdatedPropValue("Worker", user.EmployeeID); } // получим папку, проверим у папки отсутствие флага IsLocked object vValue = xobj.GetPropValue("Folder", DomainObjectDataSetWalkingStrategies.UseUpdatedPropsThanLoadedProps); DomainObjectData xobjFolder = null; if (vValue is Guid) { xobjFolder = xobj.Context.Get(context.Connection, xobj, "Folder", DomainObjectDataSetWalkingStrategies.UseUpdatedPropsThanLoadedProps, true); // проверим состояние папки. если она закрыта или в ожидании закрытия, то запретим списание if ((FolderStates)xobjFolder.GetLoadedPropValue("State") == FolderStates.Closed || (FolderStates)xobjFolder.GetLoadedPropValue("State") == FolderStates.WaitingToClose || (FolderStates)xobjFolder.GetLoadedPropValue("State") == FolderStates.Frozen) { throw new XSecurityException("Списания в папку в состоянии \"Закрыто\" или \"Ожидание закрытия\" запрещены. По вопросам списаний обращайтесь к менеджеру."); } // проверим наличие у папки специального аттрибута, запрещающего списание if ((bool)xobjFolder.GetPropValue("IsLocked", DomainObjectDataSetWalkingStrategies.UseUpdatedPropsThanLoadedProps)) { throw new XSecurityException("<b>Списания в данную папку запрещены.</b><br/>По вопросам списаний обращайтесь к менеджеру."); } FolderPrivilegeManager manager = (XSecurityManager.Instance.SecurityProvider as SecurityProvider).FolderPrivilegeManager; // у пользователя должны быть права на списание в данной папке if (!manager.HasFolderPrivilege(user, FolderPrivileges.SpentTimeByProject, xobjFolder, context.Connection)) { throw new XSecurityException("<b>Пользователь должен обладать в проекте привилегией \"Списание времение на проект\".</b><br/>По вопросам списаний обращайтесь к менеджеру."); } // если у пользователя нет привилегии «Разрешение списания на папку с неоднозначным определением направления» // проверим - является ли данная папка папкой с неоднозначным определением направления if (!manager.HasFolderPrivilege(user, FolderPrivileges.TimeLossOnUnspecifiedDirection, xobjFolder, context.Connection)) { using (XDbCommand c = context.Connection.CreateCommand()) { c.CommandType = CommandType.Text; c.CommandText = @" IF ( SELECT t.AccountRelated FROM dbo.ActivityType t WITH(NOLOCK) JOIN dbo.Folder fT WITH(NOLOCK) ON fT.ActivityType = t.ObjectID WHERE fT.ObjectID = @FolderID ) = 0 SELECT 1 ELSE SELECT TOP 1 t.DirsQnt FROM ( SELECT fU.ObjectID, fU.LRLevel, ( SELECT COUNT(*) FROM dbo.FolderDirection fd WITH(NOLOCK) WHERE fd.Folder = fU.ObjectID ) AS DirsQnt FROM dbo.Folder fT WITH(NOLOCK) JOIN dbo.Folder fU WITH(NOLOCK) ON fU.Customer = fT.Customer AND fU.LIndex <= fT.LIndex AND fU.RIndex >= fT.RIndex WHERE fT.ObjectID = @FolderID ) t WHERE DirsQnt > 0 ORDER BY LRLevel DESC "; c.Parameters.Add("FolderID", DbType.Guid, ParameterDirection.Input, false, vValue); object oResult = c.ExecuteScalar(); string sReport = null; if (null == oResult || DBNull.Value == oResult) { sReport = "для которой не определено ни одного направления"; } else if (1 != (int)oResult) { sReport = "для которой определено более одного направления"; } if (null != sReport) { throw new XSecurityException( "<b>Списания в данную папку запрещены.</b><br/>" + "У Вас нет прав списывать в папку, " + sReport + ". " + "По вопросам списаний <b>обращайтесь к менеджеру</b>."); } } } } // если в объекте заданы "виртуальные" свойства if (xobj.HasUpdatedProp("LossFixedStart") && xobj.GetUpdatedPropValue("LossFixedStart") is DateTime && xobj.HasUpdatedProp("LossFixedEnd") && xobj.GetUpdatedPropValue("LossFixedEnd") is DateTime) { if (!xobj.IsNew) { throw new ApplicationException("Задание диапазона допустимо только при создании объекта \"Списание времени\""); } // получим даты начала и окончания периода DateTime dtPeriodStart = (DateTime)xobj.GetUpdatedPropValue("LossFixedStart"); DateTime dtPeriodEnd = (DateTime)xobj.GetUpdatedPropValue("LossFixedEnd"); if (dtPeriodStart > dtPeriodEnd) { DateTime dtTemp = dtPeriodEnd; dtPeriodEnd = dtPeriodStart; dtPeriodStart = dtTemp; } Guid employeeID = (Guid)xobj.GetUpdatedPropValue("Worker"); // проверим, что в данном периоде отсутствуют списания XDbCommand cmd = context.Connection.CreateCommand(""); cmd.CommandText = @"SELECT DISTINCT CONVERT(varchar, x.SpentDate, 104) FROM ( SELECT ts.RegDate AS SpentDate FROM TimeSpent ts JOIN Task t ON ts.Task = t.ObjectID WHERE t.Worker = @EmployeeID AND ts.RegDate >= @dtPeriodStart AND ts.RegDate < @dtPeriodEnd UNION SELECT ts.LossFixed AS SpentDate FROM TimeLoss ts WHERE ts.Worker = @EmployeeID AND ts.LossFixed >= @dtPeriodStart AND ts.LossFixed < @dtPeriodEnd ) x" ; cmd.Parameters.Add("EmployeeID", DbType.Guid, ParameterDirection.Input, false, employeeID); cmd.Parameters.Add("dtPeriodStart", DbType.Date, ParameterDirection.Input, false, dtPeriodStart); // AddDays(1) - т.к. в условии запроса знак "меньше" cmd.Parameters.Add("dtPeriodEnd", DbType.Date, ParameterDirection.Input, false, dtPeriodEnd.AddDays(1)); using (IDataReader reader = cmd.ExecuteReader()) { if (reader.Read()) { StringBuilder bld = new StringBuilder(); do { if (bld.Length > 0) { bld.Append(", "); } bld.Append(reader.GetString(0)); } while(reader.Read()); throw new XBusinessLogicException( String.Format("У вас уже имеются списания на следующие даты в заданном периоде ({0},{1}): {2}", dtPeriodStart.ToShortDateString(), dtPeriodEnd.ToShortDateString(), bld.ToString()) ); } } // Получаем "набор пар" дата - норма сотрудника на дату Dictionary <DateTime, int> dictDateRates = new Dictionary <DateTime, int>(); dictDateRates = GetDayRates(context.Connection, dtPeriodStart, dtPeriodEnd, employeeID); // пойдем по всем дням периода (dtPeriodEnd, dtPeriodStart) // и для каждой даты создадим новый объект Списание времени с количеством времени равном рабочему дню // При этом, будем игнорировать даты, попадающие на выходные/праздники TimeSpan period = (dtPeriodEnd - dtPeriodStart); for (int nOffSet = 0; nOffSet <= period.TotalDays; ++nOffSet) { int nRate = 0; DateTime dtDate = dtPeriodStart.AddDays(nOffSet); // Если удалось получить норму сотрудника на соотв. дату, то создадим объект if (dictDateRates.TryGetValue(dtDate, out nRate)) { // Если полученная норма больше 0, тогда создадим списание if (nRate > 0) { DomainObjectData xobjNew = createTimeLossObject(request.DataSet, xobj, user); xobjNew.SetUpdatedPropValue("LossFixed", dtDate); // списание длиной в количество минут в рабочем дне xobjNew.SetUpdatedPropValue("LostTime", nRate); } } } // исходный объект надо удалить request.DataSet.Remove(xobj); } } XSecurityManager sec_man = XSecurityManager.Instance; IEnumerator enumerator = request.DataSet.GetModifiedObjectsEnumerator(false); DomainObjectData xobject; while (enumerator.MoveNext()) { xobject = (DomainObjectData)enumerator.Current; if (xobject.ToDelete) { sec_man.DemandDeleteObjectPrivilege(xobject); } else { sec_man.DemandSaveObjectPrivilege(xobject); } } // #1: Запись данных XStorageGateway.Save(context, request.DataSet, request.TransactionID); // Специального результата операция не возвращает return(new XResponse()); }