public override void Execute(XTriggerArgs args, IXExecutionContext context) { DomainObjectData xobj = args.TriggeredObject; // если изменилось значение признака "Архивное", проверим отсутствие у подразделения работающих сотрудников или не архивных департаментов. bool bUpdateIsArchive = xobj.HasUpdatedProp("IsArchive"); if (bUpdateIsArchive) { bool newValue = (bool)xobj.GetUpdatedPropValue("IsArchive"); if (!xobj.IsNew && newValue) { // 1. Проверим, что все сотрудники (во всех вложенных департаментах) уволены XDbCommand cmd = context.Connection.CreateCommand(@" SELECT 1 FROM dbo.Department d_s WITH(NOLOCK) JOIN dbo.Department d WITH(NOLOCK) ON d.LIndex >= d_s.LIndex AND d.RIndex <= d_s.RIndex AND d.Organization = d_s.Organization JOIN Employee e WITH(NOLOCK) ON (d.ObjectID = e.Department) and (e.WorkEndDate is null) WHERE d_s.ObjectID = @ObjectID " ); cmd.Parameters.Add("ObjectID", DbType.Guid, ParameterDirection.Input, false, xobj.ObjectID); if (cmd.ExecuteScalar() != null) { throw new XBusinessLogicException("Департамент не может быть переведен с состояние \"Архивное\", так как содержит работающих сотрудников"); } } // добавим в датаграмму подчиненные департаменты UpdateChildDepartments(context.Connection, args.DataSet, xobj.ObjectID, newValue); } }
/// <summary> /// Создает объект "Списание времение" на основании шаблона /// </summary> private DomainObjectData createTimeLossObject(DomainObjectDataSet dataSet, DomainObjectData template, ITUser user) { DomainObjectData xobj = dataSet.CreateStubNew("TimeLoss"); xobj.SetUpdatedPropValue("Cause", template.GetUpdatedPropValue("Cause")); if (template.HasUpdatedProp("Worker")) { xobj.SetUpdatedPropValue("Worker", template.GetUpdatedPropValue("Worker")); } else { xobj.SetUpdatedPropValue("Worker", user.EmployeeID); } if (template.HasUpdatedProp("Folder")) { xobj.SetUpdatedPropValue("Folder", template.GetUpdatedPropValue("Folder")); } return(xobj); }
public override void Execute(XTriggerArgs args, IXExecutionContext context) { DomainObjectData xobj = args.TriggeredObject; // Признак изменения Временной нетрудоспособности bool bUpdatedTemporaryDisability = xobj.HasUpdatedProp("TemporaryDisability"); // Признак изменения Даты начала работы bool bUpdateWorkBeginDay = xobj.HasUpdatedProp("WorkBeginDate"); // Признак изменения Даты окончания работы bool bUpdateWorkEndDay = xobj.HasUpdatedProp("WorkEndDate"); // Если что-то менялось, то запишем это событие в историю if (bUpdatedTemporaryDisability) { bool oldValue = (bool)xobj.GetLoadedPropValueOrLoad(context.Connection, "TemporaryDisability"); bool newValue = (bool)xobj.GetUpdatedPropValue("TemporaryDisability"); // Если свойство действительно обновилось, то запишем в "историю" if (oldValue != newValue) { setEmployeeHistoryEvent(args, EmployeeHistoryEvents.TemporaryDisability); // Если сотрудник получил признак "Временная нетрудоспособность", то надо автоматически задать // норму рабочего времени 0 if (newValue) { setEmployeeRate(args, "Временная нетрудоспособность", DateTime.Now); } } } if (bUpdateWorkBeginDay) { setEmployeeHistoryEvent(args, EmployeeHistoryEvents.WorkBeginDay); } if (bUpdateWorkEndDay) { setEmployeeHistoryEvent(args, EmployeeHistoryEvents.WorkEndDay); // Если для сотрудника задали "Дату окончания работы", то надо автоматически задать // норму рабочего времени 0 if (xobj.GetUpdatedPropValue("WorkEndDate") != DBNull.Value) { setEmployeeRate(args, "Уволен", (DateTime)xobj.GetUpdatedPropValue("WorkEndDate")); } } }
private void checkAndSyncReverseProps(DomainObjectDataSet dataSet) { IEnumerator enumerator = dataSet.GetModifiedObjectsEnumerator(true); object vPropValue; while (enumerator.MoveNext()) { DomainObjectData xobj = (DomainObjectData)enumerator.Current; foreach (string sPropName in xobj.UpdatedPropNames) { XPropInfoBase propInfo = xobj.TypeInfo.GetProp(sPropName); Debug.Assert(propInfo != null); if (propInfo.VarType != XPropType.vt_object) { continue; } XPropInfoObject propInfoObj = (XPropInfoObject)propInfo; if (propInfoObj.Capacity == XPropCapacity.CollectionMembership || propInfoObj.Capacity == XPropCapacity.Link) { Guid[] values = (Guid[])xobj.GetUpdatedPropValue(sPropName); if (values.Length > 0) { foreach (Guid valueObjectID in values) { bool bError = false; DomainObjectData xobjValue = dataSet.Find(propInfoObj.ReferedType.Name, valueObjectID); // объект-значение объектного свойства sPropName есть в контексте и он будет сохраняться if (xobjValue != null && xobjValue.HasNewData) { // если текущее свойство "членство в коллекции" и в объекте-значении есть обратное свойство (коллекция), // проверим, что обратное свойство содержит ссылку на текущий объект (xobj) if (propInfoObj.Capacity == XPropCapacity.CollectionMembership && xobjValue.HasUpdatedProp(propInfoObj.ReverseProp.Name)) { Guid[] propRevValues = (Guid[])xobjValue.GetUpdatedPropValue(propInfoObj.ReverseProp.Name); Debug.Assert(propRevValues != null); // если обратное свойство (коллекция) не содержит ссылку на текущий объект - исключение if (Array.IndexOf(propRevValues, xobj.ObjectID) == -1) { bError = true; } } // если текущее свойство линк, то установим обратное свойство - объектный скаляр else if (propInfoObj.Capacity == XPropCapacity.Link) { vPropValue = xobjValue.GetUpdatedPropValue(propInfoObj.ReverseProp.Name); // если обратное свойство (скаляр) установлено, то проверим, что оно ссылается на текущий объект if (vPropValue != null) { Debug.Assert(vPropValue is Guid); if ((vPropValue is DBNull) || ((Guid)vPropValue) != xobj.ObjectID) { bError = true; } } // обратное свойство неустановлено - установим его на текущий объект else { xobjValue.SetUpdatedPropValue(propInfoObj.ReverseProp.Name, xobj.ObjectID); } } if (bError) { throw new XInvalidXmlForestException( String.Format("Не согласованы свойства объектов: {0}[ID='{1}'], свойство {2} и {3}[ID='{4}'], свойство {5}", xobj.ObjectType, // 0 xobj.ObjectID, // 1 sPropName, // 2 xobjValue.ObjectType, // 3 xobjValue.ObjectID, // 4 propInfoObj.ReverseProp.Name // 5 )); } } } } } } } }
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 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); } } }
public override void Execute(XTriggerArgs args, IXExecutionContext context) { DomainObjectData xobjTask = args.TriggeredObject; // для нового объекта, если не задан планировщик, или // если изменилось запланированное время при обновлении установим планировщиком задания текущего сотрудника if (xobjTask.IsNew && xobjTask.GetUpdatedPropValue("Planner") == null || !xobjTask.IsNew && xobjTask.HasUpdatedProp("PlannedTime")) { xobjTask.SetUpdatedPropValue("Planner", ((ITUser)XSecurityManager.Instance.GetCurrentUser()).EmployeeID); } }
public bool HasFolderPrivilege(ITUser user, FolderPrivileges privilege, DomainObjectData xobjFolder, XStorageConnection con) { // т.к. компонент может использоваться не только через методы XSecurityManager: GetObjectRights, HasSaveObjectRights, etc. if (user.IsUnrestricted) { return(true); } string sPrivilege = FolderPrivilegesItem.GetItem(privilege).Name; Guid orgID; Guid activityTypeID; FolderStates folderState; if (xobjFolder.IsNew) { if (!xobjFolder.HasUpdatedProp("Customer") || xobjFolder.GetUpdatedPropValue("Customer") == DBNull.Value) { return(false); } orgID = (Guid)xobjFolder.GetUpdatedPropValue("Customer"); if (!xobjFolder.HasUpdatedProp("ActivityType") || xobjFolder.GetUpdatedPropValue("ActivityType") == DBNull.Value) { return(false); } activityTypeID = (Guid)xobjFolder.GetUpdatedPropValue("ActivityType"); if (!xobjFolder.HasUpdatedProp("State") || xobjFolder.GetUpdatedPropValue("State") == DBNull.Value) { return(false); } folderState = (FolderStates)xobjFolder.GetUpdatedPropValue("State"); } else { if (!xobjFolder.HasLoadedProp("Customer") || !xobjFolder.HasLoadedProp("ActivityType") || !xobjFolder.HasLoadedProp("Parent") || !xobjFolder.HasLoadedProp("State")) { xobjFolder.Load(con); } orgID = (Guid)xobjFolder.GetLoadedPropValue("Customer"); activityTypeID = (Guid)xobjFolder.GetLoadedPropValue("ActivityType"); folderState = (FolderStates)xobjFolder.GetLoadedPropValue("State"); } // в закрытой папке ни у кого нет никаких привилегий if (folderState == FolderStates.Closed) { return(false); } if (user.ManageOrganization(orgID) || user.ManageActivityType(activityTypeID)) { return(true); } if (!xobjFolder.IsNew) { // для объекта из БД, проверим определение привилегий для участника проекта заданной и вышестоящих папок if (m_folderPrivilegesDefinitionContainer.GetPrivileges(user, xobjFolder.ObjectID, con).Contains(sPrivilege)) { return(true); } } else { // Для нового объекта зачитывать привилегии из БД бессмысленно - // проверим ссылку на родителя, если она задана, то зачитаем его привилегии object vValue = xobjFolder.GetUpdatedPropValue("Parent"); if (vValue is Guid) { if (m_folderPrivilegesDefinitionContainer.GetPrivileges(user, (Guid)vValue, con).Contains(sPrivilege)) { return(true); } } } return(false); }