Всем доброго времени суток!

Столкнулся с такой проблемой, валятся ошибки при использовании ATF.Repository объектной модели и выгрузке данных в список:

 

[Schema("Account")]
    public class Account : BaseModel
    {       
 
        [SchemaProperty("Name")]
        public string Name { get; set; }        
 
        [DetailProperty("Account")]
        public virtual List<AccountWorkingTime> WorkingTimes { get; set; }
    }
 
[Schema("AbWorkingTimeStation")]
    public class AccountWorkingTime : BaseModel
    {
        [SchemaProperty("Account")]
        public Guid AccountId { get; set; }
 
        [LookupProperty("DayOfWeek")]
        public virtual DayOfWeek DayOfWeek { get; set; }
 
        [SchemaProperty("From")]
        public DateTime From { get; set; }
 
        [SchemaProperty("To")]
        public DateTime To { get; set; }
 
    }
 
namespace PlatformIntegration.Files.cs
{
    [DefaultBinding(typeof(IAccountAdapter))]
    public class AccountAdapter : IAccountAdapter
    {
        public UserConnection UserConnection { get; set; }
 
        public AccountAdapter(UserConnection userConnection)
        {
            UserConnection = userConnection;
        }
        private IRepository _repository;
 
        private IRepository Repository
        {
            get => _repository ??= new Repository
                {UserConnection = UserConnection, UseAdminRight = false};
            set => _repository = value;
        }
 
        public Models.Account GetAccountById(Guid id)
        {
            var account = Repository.GetItem<Models.Account>(id);
            return account;
        }        
    }
}

При отработке метода GetAccountById - вылазят ошибки https://prnt.sc/uh1zso

Terrasoft.Web.Common.ServiceModel.ErrorHandler HandleError - Error getting value from 'WorkingTimes' on 'Castle.Proxies.AccountProxy'.

Newtonsoft.Json.JsonSerializationException: Error getting value from 'WorkingTimes' on 'Castle.Proxies.AccountProxy'. ---> System.Reflection.TargetInvocationException: Адресат вызова создал исключение. ---> System.InvalidCastException: Объект должен реализовывать интерфейс IConvertible.

   в System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)

   в ATF.Repository.Repository.FillPropertyValue[T](T model, PropertyInfo propertyInfo, Object value)

   в ATF.Repository.Repository.FillPropertyValues[T](T model, IDictionary`2 values)

   в ATF.Repository.Repository.CreateItem[T](IDictionary`2 values)

   в ATF.Repository.Repository.LoadModelByValues[T](IDictionary`2 values)

   в ATF.Repository.Repository.<>c__DisplayClass64_0`1.b__0(IDictionary`2 recordValues)

   в System.Collections.Generic.List`1.ForEach(Action`1 action)

   в ATF.Repository.Repository.GetItems[T](String filterPropertyName, Guid filterValue)

   --- Конец трассировки внутреннего стека исключений ---

   в System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)

   в System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)

   в System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

   в System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)

   в ATF.Repository.Repository.FillDetailValue[T](T model, ModelItem detail)

   в ATF.Repository.Builder.InstanceProxyHelper`1.FillProperty(IInvocation invocation, PropertyInfo property)

   в ATF.Repository.Builder.InstanceProxyHelper`1.InternalGet(IInvocation invocation, PropertyInfo property)

   в ATF.Repository.Builder.InstanceProxyHelper`1.Intercept(IInvocation invocation)

   в Castle.DynamicProxy.AbstractInvocation.Proceed()

   в Castle.Proxies.AccountProxy.get_WorkingTimes()

   в GetWorkingTimes(Object )

   в Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue(Object target)

   --- Конец трассировки внутреннего стека исключений ---

   в Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue(Object target)

   в Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CalculatePropertyValues(JsonWriter writer, Object value, JsonContainerContract contract, JsonProperty member, JsonProperty property, JsonContract& memberContract, Object& memberValue)

   в Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)

   в Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter, Object value, Type objectType)

   в Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value, Type objectType)

   в Newtonsoft.Json.JsonConvert.SerializeObjectInternal(Object value, Type type, JsonSerializer jsonSerializer)

   в Newtonsoft.Json.JsonConvert.SerializeObject(Object value)

   в AutobookingPlatformIntegration.AccountIntegrationService.GetAccountById(Guid id)

   в SyncInvokeGetAccountById(Object , Object[] , Object[] )

   в System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)

   в System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)

   в System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)

   в System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11(MessageRpc& rpc)

   в System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)

 

Кто нибудь сталкивался с таким поведением?



 

 

Нравится

3 комментария

Нашел в чем проблема. Не может десерелизовать в модель колонку с типом Время - https://prnt.sc/uhpw7o в DateTime. Кто работал с данным репозиторием знает решение данной проблемы?

Алексей, не вижу никаких упоминаний о ATF.Repository, кроме этой темы. Речь о тех ATF, которые сделали «.NET Core connector»? Там указан адрес на GitHub для связи с разработчиками. Возможно, они подскажут по этому механизму.

Зверев Александр,

да, эти, писал и на почту, правда по другому вопросу, по текущему на гитхабе- пока ответа не получал

Показать все комментарии

Добрый день. Подскажите, пожалуйста, как сделать фильтр в детали с редактируемым реестром для справочного поля, что бы при выборе из него убирались уже выбранные записи. К примеру есть список овощей, и при добавлении записи с выбранной "капустой", выбрать снова этот овощ нельзя для другой записи

Нравится

1 комментарий
Лучший ответ

Добрый день.

Вам нужно добавить дополнительную фильтрацию для справочного поля и отображать только те значения, которые не используются в других записях этой детали.

Ниже привожу пример подобной фильтрации:

if (project &amp;&amp; project.value) {
	var filterGroup = this.Terrasoft.createFilterGroup();
	var subFilters = Terrasoft.createFilterGroup();
    subFilters.addItem(Terrasoft.createColumnFilterWithParameter(
		Terrasoft.ComparisonType.EQUAL, "EWBProject", project.value));
	filterGroup.addItem(this.Terrasoft.createExistsFilter("[BTProjectDisease:BTDiseaseType:Id].Id",
		subFilters));
	var subFilters1 = Terrasoft.createFilterGroup();
	subFilters1.addItem(Terrasoft.createColumnFilterWithParameter(
		Terrasoft.ComparisonType.EQUAL, "ProjectSpecification", this.get("MasterRecordId")));
	filterGroup.addItem(this.Terrasoft.createNotExistsFilter(" 
       [SpecificationByDiseaseType:DiseaseType:Id].Id",	subFilters1));
}

В результате в базу данных сформируется такой SQL-запрос:

SELECT
	[BTDiseaseType].[Id] [Id],
	[BTDiseaseType].[Name] [Name],
	[BTDiseaseType].[BTTherapeuticAreaId] [BTTherapeuticAreaId],
	[BTTherapeuticArea].[Name] [BTTherapeuticArea.Name]
FROM
	[dbo].[BTDiseaseType] [BTDiseaseType] WITH(NOLOCK)
	LEFT OUTER JOIN [dbo].[BTTherapeuticarea] [BTTherapeuticArea] WITH(NOLOCK) ON ([BTTherapeuticArea].[Id] = [BTDiseaseType].[BTTherapeuticAreaId])
WHERE
	(EXISTS (
SELECT
	[SubBTProjectDisease].[Id] [Id]
FROM
	[dbo].[BTProjectDisease] [SubBTProjectDisease] WITH(NOLOCK)
WHERE
	[SubBTProjectDisease].[BTDiseaseTypeId] = [BTDiseaseType].[Id]
	AND [SubBTProjectDisease].[EWBProjectId] = @EWBProjectId)
AND NOT EXISTS (
SELECT
	[SubSpecificationByDiseaseType].[Id] [Id]
FROM
	[dbo].[SpecificationByDiseaseType] [SubSpecificationByDiseaseType] WITH(NOLOCK)
WHERE
	[SubSpecificationByDiseaseType].[DiseaseTypeId] = [BTDiseaseType].[Id]
	AND [SubSpecificationByDiseaseType].[ProjectSpecificationId] = @ProjectSpecificationId))
ORDER BY
	[BTTherapeuticArea.Name] DESC

 

Добрый день.

Вам нужно добавить дополнительную фильтрацию для справочного поля и отображать только те значения, которые не используются в других записях этой детали.

Ниже привожу пример подобной фильтрации:

if (project &amp;&amp; project.value) {
	var filterGroup = this.Terrasoft.createFilterGroup();
	var subFilters = Terrasoft.createFilterGroup();
    subFilters.addItem(Terrasoft.createColumnFilterWithParameter(
		Terrasoft.ComparisonType.EQUAL, "EWBProject", project.value));
	filterGroup.addItem(this.Terrasoft.createExistsFilter("[BTProjectDisease:BTDiseaseType:Id].Id",
		subFilters));
	var subFilters1 = Terrasoft.createFilterGroup();
	subFilters1.addItem(Terrasoft.createColumnFilterWithParameter(
		Terrasoft.ComparisonType.EQUAL, "ProjectSpecification", this.get("MasterRecordId")));
	filterGroup.addItem(this.Terrasoft.createNotExistsFilter(" 
       [SpecificationByDiseaseType:DiseaseType:Id].Id",	subFilters1));
}

В результате в базу данных сформируется такой SQL-запрос:

SELECT
	[BTDiseaseType].[Id] [Id],
	[BTDiseaseType].[Name] [Name],
	[BTDiseaseType].[BTTherapeuticAreaId] [BTTherapeuticAreaId],
	[BTTherapeuticArea].[Name] [BTTherapeuticArea.Name]
FROM
	[dbo].[BTDiseaseType] [BTDiseaseType] WITH(NOLOCK)
	LEFT OUTER JOIN [dbo].[BTTherapeuticarea] [BTTherapeuticArea] WITH(NOLOCK) ON ([BTTherapeuticArea].[Id] = [BTDiseaseType].[BTTherapeuticAreaId])
WHERE
	(EXISTS (
SELECT
	[SubBTProjectDisease].[Id] [Id]
FROM
	[dbo].[BTProjectDisease] [SubBTProjectDisease] WITH(NOLOCK)
WHERE
	[SubBTProjectDisease].[BTDiseaseTypeId] = [BTDiseaseType].[Id]
	AND [SubBTProjectDisease].[EWBProjectId] = @EWBProjectId)
AND NOT EXISTS (
SELECT
	[SubSpecificationByDiseaseType].[Id] [Id]
FROM
	[dbo].[SpecificationByDiseaseType] [SubSpecificationByDiseaseType] WITH(NOLOCK)
WHERE
	[SubSpecificationByDiseaseType].[DiseaseTypeId] = [BTDiseaseType].[Id]
	AND [SubSpecificationByDiseaseType].[ProjectSpecificationId] = @ProjectSpecificationId))
ORDER BY
	[BTTherapeuticArea.Name] DESC

 

Показать все комментарии

Добрый день,

 

Необходимо реализовать фильтрацию печатных форм в зависимости от выбранного значения справочника на странице. 

Переопределяя метод initCardPrintForms и в нем добавляя условия для выборки, в режиме отображения карточки все работает как надо.

​
var printFormsMenuCollection = resultCollection.filterByFn(function(item) {
    return item.get("ShowInCard") === true &&(this.getFilterForReportsOrBooleanValue(item));
}, this);
 
​
getFilterForReportsOrBooleanValue(item) {
		var value = this.get("NrbPurchaseMethod");
				switch(value.displayValue) {
					case "Аукцион":
							return	item.get("Caption") === "Извещение Аукцион" || 
									item.get("Caption") === "Документация Аукцион";
				default:
							return false;
		}
},

Но в комбинированном режиме почему-то вызывается опять же этот метод хотя для секции есть свой initSectionPrintForms, и в итоге загрузка страницы прекращается с ошибкой, из-за отсутствия полей.

Как правильно реализовать фильтрацию?

Нравится

1 комментарий

Павел, а в чём проблема, если в обоих случаях будет запускаться одна и та же функция с доработками?

И initCardPrintForms, и initSectionPrintForms применяются в BaseDataView, базовой странице раздела:

/**
 * Initializes print buttons menu.
 * @protected
 * @param {Function} callback Callback function.
 * @param {Object} scope Callback function scope.
 */
initPrintButtonsMenu: function(callback, scope) {
	this.initSectionPrintForms(this.initCardPrintForms, this);
	this.initModulePrintForms(callback, scope);
},

Там есть две кнопки печатных форм: одна — для раздельного режима, другая — для комбинированного:

{
	"operation": "insert",
	"name": "SeparateModeReportsButton",
	"parentName": "SeparateModeActionButtonsRightContainer",
	"propertyName": "items",
	"values": {
		"itemType": Terrasoft.ViewItemType.BUTTON,
		"caption": {"bindTo": "Resources.Strings.PrintButtonCaption"},
		"classes": {"wrapperClass": ["actions-button-margin-right"]},
		"controlConfig": {
			"menu": {"items": {"bindTo": "SectionPrintMenuItems"}},
			"visible": {"bindTo": "IsSectionPrintButtonVisible"}
		}
	}
},
...
{
	"operation": "insert",
	"name": "CombinedModePrintButton",
	"parentName": "CombinedModeActionButtonsCardRightContainer",
	"propertyName": "items",
	"values": {
		"itemType": Terrasoft.ViewItemType.BUTTON,
		"caption": {"bindTo": "Resources.Strings.PrintButtonCaption"},
		"classes": {"wrapperClass": ["actions-button-margin-right"]},
		"controlConfig": {"menu": {"items": {"bindTo": "CardPrintMenuItems"}}},
		"visible": {"bindTo": "IsCardPrintButtonVisible"}
	}
},

 

Показать все комментарии

Добрый вечер.

 

Подскажите, пжл, куда в Системе необходимо смотреть (где производить настройки/разработку), чтобы выполнить следующие требования в представлении "Календарь" раздела "Активности":

- закрепить шапку календаря (при наличии большого количества многодневных задач шапка после прокрутки скрывается, оставаясь "сверху")

- отсортировать записи не по полю "Начало", а по полю "Завершения"

Изображение удалено.

Нравится

5 комментариев
Лучший ответ

Попробовал уменьшить окно браузера.

Да, похоже шапка вместе с многодневными задачами закрепляется сверху над обычными задачами. А если они сами не влазят целиком, то вместе прокручиваются и шапка исчезает, остаётся только блок фильтров и всё, что выше его.

 

Это компонент ядра Terrasoft.controls.ScheduleEdit, он реализован в файле https://сайт/0/core/hash/Terrasoft/controls/schedule-edit/schedule-edit…

 

Там, если смотреть HTML-код страницы, есть три блока, сверху вниз: title-area, multi-day-item-area, scroll-area. Даже по названию видно, что прокручиваться предназначен третий, с обычными задачами.

 

То есть, это код ядра, не конфигурации.

Сортировка по началу, видимо, там же:

	/**
	 * Returns the sorted list of calendar items.
	 * @private
	 * @param {Array} schedulerItemList List of calendar items.
	 * @return {Array} Sorted list of calendar items.
	 */
	sortScheduleItemList: function(schedulerItemList) {
		const sortItemList = [];
		Terrasoft.each(schedulerItemList, function(item) {
			if (!item.isMultiDayItem()) {
				sortItemList.push(item);
			}
		});
		sortItemList.sort(this.sortScheduleItemListByStartDate);
		return sortItemList;
	},
 
	/**
	 * Returns the result of comparing 2 calendar items by date.
	 * @private
	 * @param {Object} itemA Item #.
	 * @param {Object} itemB Item B.
	 * @return {Array} The result of comparing 2 calendar items by date.
	 */
	sortScheduleItemListByStartDate: function(itemA, itemB) {
		let result = 0;
		const itemAStartDate = itemA.startDate;
		const itemADuration = itemA.dueDate - itemAStartDate;
		const itemBStartDate = itemB.startDate;
		const itemBDuration = itemB.dueDate - itemBStartDate;
		if ((itemAStartDate === itemBStartDate) &amp;&amp; (itemADuration === itemBDuration)) {
			result = 0;
		} else if (itemAStartDate &gt; itemBStartDate) {
			result = 1;
		} else if (itemAStartDate &lt; itemBStartDate) {
			result = -1;
		} else if (itemADuration &gt; itemBDuration) {
			result = -1;
		} else if (itemADuration &lt; itemBDuration) {
			result = 1;
		}
		return result;
	},

 

Уточните, где именно на скриншоте у Вас скрылась шапка? И блок фильтрации, и многодневные задачи там видны.

 

Возможно, если у Вас так много в расписании многодневных задач, есть смысл применить диаграмму Ганта? Есть пара дополнений с её реализацией.

Зверев Александр,

Вот как раз, если много задач, то при пролистывании ползунком скрывается шапка, прилагаю скриншот:

// + где настроить, чтобы  <отсортировать записи не по полю "Начало", а по полю "Завершения">/

Попробовал уменьшить окно браузера.

Да, похоже шапка вместе с многодневными задачами закрепляется сверху над обычными задачами. А если они сами не влазят целиком, то вместе прокручиваются и шапка исчезает, остаётся только блок фильтров и всё, что выше его.

 

Это компонент ядра Terrasoft.controls.ScheduleEdit, он реализован в файле https://сайт/0/core/hash/Terrasoft/controls/schedule-edit/schedule-edit…

 

Там, если смотреть HTML-код страницы, есть три блока, сверху вниз: title-area, multi-day-item-area, scroll-area. Даже по названию видно, что прокручиваться предназначен третий, с обычными задачами.

 

То есть, это код ядра, не конфигурации.

Сортировка по началу, видимо, там же:

	/**
	 * Returns the sorted list of calendar items.
	 * @private
	 * @param {Array} schedulerItemList List of calendar items.
	 * @return {Array} Sorted list of calendar items.
	 */
	sortScheduleItemList: function(schedulerItemList) {
		const sortItemList = [];
		Terrasoft.each(schedulerItemList, function(item) {
			if (!item.isMultiDayItem()) {
				sortItemList.push(item);
			}
		});
		sortItemList.sort(this.sortScheduleItemListByStartDate);
		return sortItemList;
	},
 
	/**
	 * Returns the result of comparing 2 calendar items by date.
	 * @private
	 * @param {Object} itemA Item #.
	 * @param {Object} itemB Item B.
	 * @return {Array} The result of comparing 2 calendar items by date.
	 */
	sortScheduleItemListByStartDate: function(itemA, itemB) {
		let result = 0;
		const itemAStartDate = itemA.startDate;
		const itemADuration = itemA.dueDate - itemAStartDate;
		const itemBStartDate = itemB.startDate;
		const itemBDuration = itemB.dueDate - itemBStartDate;
		if ((itemAStartDate === itemBStartDate) &amp;&amp; (itemADuration === itemBDuration)) {
			result = 0;
		} else if (itemAStartDate &gt; itemBStartDate) {
			result = 1;
		} else if (itemAStartDate &lt; itemBStartDate) {
			result = -1;
		} else if (itemADuration &gt; itemBDuration) {
			result = -1;
		} else if (itemADuration &lt; itemBDuration) {
			result = 1;
		}
		return result;
	},

 

Зверев Александр,

Спасибо за ответ. Возможно ли запланировать в базовой функциональности фиксацию шапки календаря и возможность настройки сортировки?

Завёл идеи о шапке многодневных и способе сортировки.

Показать все комментарии

Добрый день, уважаемые коллеги!

Есть задача в мобильном приложении (уже давно функционирующем) изменить на карточке Контрагента логику поля Подразделение . В справочнике Подразделений добавлено булево поле UsrProhibitSelectionAccount, если true, то запрещать подразделение к выбору. 

Рассматриваю применение 2-х вариантов:

Бизнес-правило 1: Фильтр записей Подразделений по признаку UsrProhibitSelectionAccount (см. ниже 1-й метод). Метод отрабатывает и при выборе отображает только то, что можно

Бизнес-правило 2: Выдавать сообщение пользователю, если выбранная ранее запись некорректна (см. ниже 2-й метод).  И вот тут почему-то в консоли видно, что не считывается корректно значение поля UsrProhibitSelectionAccount. Оно всегда false. Правда не во всех записях оно заполнено (см. скрин).

Помогите, пожалуйста, определить в чем некорректность 2-го метода. Благодарю за помощь

// Правило 1
/*Terrasoft.sdk.Model.addBusinessRule("Account", {
	ruleType: Terrasoft.RuleTypes.Filtration,
	position: 1,
	triggeredByColumns: ["UsrDepartment"],
	events: [Terrasoft.BusinessRuleEvents.ValueChanged,Terrasoft.BusinessRuleEvents.Load],
	 message: Terrasoft.LocalizableStrings.DepartmentMustBeAllowed,
	 filters: Ext.create("Terrasoft.Filter", {
        modelName: "UsrAccountDepartments",
        property: "UsrProhibitSelectionAccount",
        value: false
    })
});*/
 
// Правило 2
Terrasoft.sdk.Model.addBusinessRule("Account", {
	name: "AccountDepartmentMustBeAllowedRule",
	ruleType: Terrasoft.RuleTypes.Custom,
	triggeredByColumns: ["UsrDepartment"],
	events: [Terrasoft.BusinessRuleEvents.Load,
		Terrasoft.BusinessRuleEvents.ValueChanged, Terrasoft.BusinessRuleEvents.Save],
	executeFn: function(record, rule, column, customData, callbackConfig) {
		var AccountDepartment = record.get("UsrDepartment");
	var isValid = AccountDepartment.get("UsrProhibitSelectionAccount");
 
		record.changeProperty("UsrDepartment", {
			isValid: {
				value: isValid,
				message: Terrasoft.LocalizableStrings.DepartmentMustBeAllowed
			}
		});
		Ext.callback(callbackConfig.success, callbackConfig.scope, [isValid]);
	}
});

Изображение удалено.

Изображение удалено.

Нравится

2 комментария
Лучший ответ

Марина, а оно точно так должно синхронно читать из объекта справочника так, как у Вас?

В схеме MobileActivityOpportunityModelConfig значение Opportunity.Account читают с асинхронным вызовом так:

Terrasoft.sdk.Model.addBusinessRule("Activity", {
	name: "ActivityAccountOpportunityFiltrationRule",
	ruleType: Terrasoft.RuleTypes.Filtration,
	events: [Terrasoft.BusinessRuleEvents.Load, Terrasoft.BusinessRuleEvents.ValueChanged],
	triggeredByColumns: ["Account"],
	filteredColumn: "Opportunity",
	filters: Ext.create("Terrasoft.Filter", {
		property: "Account"
	})
});
 
Terrasoft.sdk.Model.addBusinessRule("Activity", {
	name: "ActivityAccountByOpportunityRule",
	ruleType: Terrasoft.RuleTypes.Custom,
	triggeredByColumns: ["Opportunity"],
	events: [Terrasoft.BusinessRuleEvents.ValueChanged, Terrasoft.BusinessRuleEvents.Save,
		Terrasoft.BusinessRuleEvents.Load],
	executeFn: function(record, rule, column, customData, callbackConfig) {
		var opportunityRecord = record.get("Opportunity");
		if (opportunityRecord) {
			var accountRecord = record.get("Account");
			if (!accountRecord) {
				var opportunityModel = Ext.ModelManager.getModel("Opportunity");
				opportunityModel.load(opportunityRecord.getId(), {
					isCancelable: false,
					queryConfig: Ext.create("Terrasoft.QueryConfig", {
						columns: ["Account"],
						modelName: "Opportunity"
					}),
					success: function(loadedRecord) {
						if (loadedRecord) {
							record.set("Account", loadedRecord.get("Account"), true);
						}
						Ext.callback(callbackConfig.success, callbackConfig.scope);
					},
					failure: function(record, operation) {
						var exception = operation.getError();
						Ext.callback(callbackConfig.failure, callbackConfig.scope, [exception]);
					},
					scope: this
				});
				return;
			}
		}
		Ext.callback(callbackConfig.success, callbackConfig.scope);
	},
	position: 1
});

Также на скриншоте видно, что колонка UsrProhibitSelectionAccount  в локальной БД пустая. Из-за этого логика может отрабатывать некорректно. Нужно правильно настроить фильтрацию синхронизации в манифесте и пересинхронизировать с нуля. 

Марина, а оно точно так должно синхронно читать из объекта справочника так, как у Вас?

В схеме MobileActivityOpportunityModelConfig значение Opportunity.Account читают с асинхронным вызовом так:

Terrasoft.sdk.Model.addBusinessRule("Activity", {
	name: "ActivityAccountOpportunityFiltrationRule",
	ruleType: Terrasoft.RuleTypes.Filtration,
	events: [Terrasoft.BusinessRuleEvents.Load, Terrasoft.BusinessRuleEvents.ValueChanged],
	triggeredByColumns: ["Account"],
	filteredColumn: "Opportunity",
	filters: Ext.create("Terrasoft.Filter", {
		property: "Account"
	})
});
 
Terrasoft.sdk.Model.addBusinessRule("Activity", {
	name: "ActivityAccountByOpportunityRule",
	ruleType: Terrasoft.RuleTypes.Custom,
	triggeredByColumns: ["Opportunity"],
	events: [Terrasoft.BusinessRuleEvents.ValueChanged, Terrasoft.BusinessRuleEvents.Save,
		Terrasoft.BusinessRuleEvents.Load],
	executeFn: function(record, rule, column, customData, callbackConfig) {
		var opportunityRecord = record.get("Opportunity");
		if (opportunityRecord) {
			var accountRecord = record.get("Account");
			if (!accountRecord) {
				var opportunityModel = Ext.ModelManager.getModel("Opportunity");
				opportunityModel.load(opportunityRecord.getId(), {
					isCancelable: false,
					queryConfig: Ext.create("Terrasoft.QueryConfig", {
						columns: ["Account"],
						modelName: "Opportunity"
					}),
					success: function(loadedRecord) {
						if (loadedRecord) {
							record.set("Account", loadedRecord.get("Account"), true);
						}
						Ext.callback(callbackConfig.success, callbackConfig.scope);
					},
					failure: function(record, operation) {
						var exception = operation.getError();
						Ext.callback(callbackConfig.failure, callbackConfig.scope, [exception]);
					},
					scope: this
				});
				return;
			}
		}
		Ext.callback(callbackConfig.success, callbackConfig.scope);
	},
	position: 1
});

Также на скриншоте видно, что колонка UsrProhibitSelectionAccount  в локальной БД пустая. Из-за этого логика может отрабатывать некорректно. Нужно правильно настроить фильтрацию синхронизации в манифесте и пересинхронизировать с нуля. 

Зверев Александр,

 

Александр, большое спасибо за рекомендации. Да, видимо, я неверно читаю объект справочника. Надо попробовать разобраться с асинхронным вызовом. Спасибо за приведенный пример.

Заполнение колонки UsrProhibitSelectionAccount в локальной базе пока не помогло.

Показать все комментарии

Добрый день, уважаемые коллеги!

Есть следующий кейс: 1. На странице Контрагента указывается наше ответственное подразделение, выбирается из справочника.

2. В записях справочника Подразделений есть признак (булево) можно выбирать это подразделение для указания в Контрагентах или нет - UsrProhibitSelectionAccount

3. Нужно запретить выбор записи Подразделения, если признак UsrProhibitSelectionAccount = true

4. Настраиваю правило валидации, как описано https://academy.terrasoft.ru/documents/technic-sdk/7-16/dobavlenie-vali…

5. При выборе значения из справочника правило отрабатывает. Но если вводить запрещенное подразделение вручную, то оно спокойно остается в поле и позволяет сохранить значение.

Подскажите, пожалуйста, что нужно сделать в данном случае? Благодарю за помощьИзображение удалено.

Изображение удалено.

Добавленные методы в карточку Контрагента

methods: {
			// Метод-валидатор значения колонки [UsrDepartment] // semmrn
            DepartmentValidator: function() {
                // Переменная для хранения сообщения об ошибке валидации.
                var invalidMessage = "";
                // Проверка значения колонки [UsrDepartment]
                var AccountDepartments = this.get("UsrDepartment");
                if (AccountDepartments.UsrProhibitSelectionAccount) {
                    // Если значение колонки [UsrAccountDepartments.UsrProhibitSelectionAccount] 
                    // запрещено к выбору = да, то
                    // в переменную invalidMessage помещается значение локализуемой строки с сообщением
                    // об ошибке валидации.
                    invalidMessage = this.get("Resources.Strings.DepartmentMustBeAllowed");
                }
                // Объект, свойство которого содержит сообщение об ошибке валидации.
                // Если валидация прошла успешна, в объекте возвращается пустая строка.
                return {
                    // Сообщение об ошибке валидации.
                    invalidMessage: invalidMessage
                };
            },
            // Переопределение базового метода, инициализирующего пользовательские валидаторы.
            setValidationConfig: function() {
                // Вызывает инициализацию валидаторов родительской модели представления.
                this.callParent(arguments);
                // Для колонки [UsrDepartment] добавляется метод-валидатор DepartmentValidator().
                this.addColumnValidator("UsrDepartment", this.DepartmentValidator);
                }
		},

 

Нравится

5 комментариев
Лучший ответ

Для того, что Вы хотите есть как раз валидация, которую Вы пробовали в первом сообщении. Она должна предотвращать и от ручного ввода. Не смотрели в отладке, функция-валидатор выполняется, что в поле AccountDepartments и как срабатывает условие?

Доброго дня, Марина. Почему Вы не хотите сделать настройку посредством бизнес-правила (фильтрацией)? 

Марина, ещё можно деактивировать при помощи стандартного механизма:

Если одно или несколько значений справочника устарели и больше не используются, то такие значения можно деактивировать (Рис. 2). Деактивированное значение не будет отображаться при выборе значений в справочных полях. При этом пользователи продолжат видеть это значение в тех записях, где оно было указано ранее, и смогут использовать его для фильтрации. По умолчанию возможность деактивировать значения справочника выключена. Разрешить деактивацию записей для нужного справочника можно в разделе [Конфигурация]. Подробнее о настройке читайте в статье “Деактивация записей объектов”.

Рис. 2 — Деактивированное значение справочника [Типы статей базы знаний]

section_lookups_deactivated_record_example.png 

Но так будет запрещён выбор не в конкретной карточке, а везде. 

Уважаемые коллеги, спасибо за Ваши предложения.

Стандартные фильтры и деактивация не подходят, так как в справочнике настроена иерархия, которая не совмещается с данными инструментами.

Нет ли метода, который будет проверять значение при вводе вручную? а также при открытии страницы (так как неправильные записи у нас пока появляются в результате их создания в мобильном приложении)? Благодарю за помощь

Для того, что Вы хотите есть как раз валидация, которую Вы пробовали в первом сообщении. Она должна предотвращать и от ручного ввода. Не смотрели в отладке, функция-валидатор выполняется, что в поле AccountDepartments и как срабатывает условие?

Зверев Александр,

 

благодарю), ситуация, действительно, прояснилась через отладчик

Показать все комментарии

Добрый день.

 

Есть раздел "Контакты", который может содержит записи с типом "Сотрудник".

Если необходимо иметь возможность отличать сотрудников от клиентов, есть возможность применять фильтры "Типы контакта" и т.д.

 

Вопрос: В каких случаях необходимо/эффективнее задействовать еще и раздел "Сотрудники"?

//Чтобы его заполнить все равно приходится обращаться к записям раздела "Контакты", а для создания записи в разделе "Пользователи" достаточно предварительно создать запись в "Контакты" .

Нравится

1 комментарий

Это два разных объекта, Contact и Employee. Во втором есть справочное поле — ссылка на контакт и дополнительная информация, связанная с должностью и руководителем, началом, концом карьеры, испытательным сроком и увольнением.

Вероятно, выделили в отдельный раздел как для удобства, так и для разграничения прав только для кадровиков.

Показать все комментарии

Добрый день.

 

Есть 3 потребности в работе с аналитикой:

  1. возможность делать скриншот отдельных виджетов
  2. при нажатии на действия "скриншот" Система должна делать скриншот виджета, сохранять изображение, на котором отсутствуют вспомогательные иконки (шестеренка и т.д.")
  3. расширить контейнер, чтобы влезал текст на диаграмме целиком?



Изображение удалено.

1) Подскажите возможна ли такая реализация? Куда необходимо смотреть?

2) Планируется ли реализация этих требований в  базовой функциональности платформы в последующих версиях?

Нравится

5 комментариев

Добрый день.

С версии  7.15.2 изменились схемы отрисовки графиков, был выполнен переход к Chart.js. На текущей версии, к сожалению, ещё нет возможности настроить полное отображение текстов подписей графиков через пользовательский интерфейс. 

По информации от авторов функциональности:

1. Такой возможности не планировали давать. В Windows и macOs есть штатные средства для создания скриншотов, а также существуют утилиты, которые помогают редактировать скриншот по усмотрению пользователя, например, ShareX.

2. Аналогично.

3. Подобные изменения запланированы на второй квартал следующего года.

 

Зверев Александр,

 

Согласен, что по п.1 и п.2 можно использовать штатные/сторонние средства.

Вопрос заключается в удобстве использования функциональности платформы, зачастую Заказчик хочет избежать дополнительных шагов (установить ShareX, вырезать необходимый виджет, открыть его в программе редактирования, обрезать его, замазать (отсечь не получится) иконки в правом верхнем углу , сохранить в png) для достижения своих целей, и предполагает, что платформа предоставит полноценное решения в рамках работы с аналитикой.

 

Если Вы подскажите, где в системе нужно копать для настройки видимости иконок виджетов и кнопки "скриншот", я буду благодарен.

 

Но лучшим вариантом было бы конечно включение требований из п.1. и п.2 в базовую функциональность платформы, т.к. пользователям для включения виджета платформы, например, в презентацию обычно не нужен весь дашборд, как и иконки, которые представляют ценность при работе с платформой, а не на скриншоте.

 

\\п.3  Подобные изменения запланированы на второй квартал следующего года.

Если необходимо расширить контейнер с текстом в рамках текущей версии (а не спустя 8 месяцев), то каким образом Вы рекомендовали бы действовать?

По Вашим вопросам и пожеланиям зарегистрированы идеи: «Итоги. Надо сохранять скриншот только одного графика (виджета), а не только всей вкладки итогов.» и «Итоги. Работа с графиками. Текст (подпись/заголовок) в графике не отображается полностью, обрезаны подписи».

Зверев Александр,

Спасибо за помощь.

Остался один только вопрос: Если необходимо расширить контейнер с текстом в рамках текущей версии (а не спустя 8 месяцев), то каким образом Вы рекомендовали бы действовать?

Вероятно, делать свой аналог на основе доработанных схем этого типа итогов и использовать его, пока не добавится в «коробке».

Кстати, на демо-сайте не вижу таких примеров как у Вас, там легенда слева написана вертикально (но если делать длинной, на другую строку не перенеслась):

Показать все комментарии

Скажите пожалуйста, будет ли приложение отображать push активностей/email активностей, созданные из бизнес процессов?

Нравится

2 комментария

К сожалению push уведомление не работает, в системе от  другого пользователя добавил активность, указав себя ответственным по задаче, но в телефоне pusha нет. система ios

Олег, добрый день!

 

push-уведомление создается ответственному сотруднику, если установлено напоминание ответственному (признак Ответственному в группе полей Напоминания на странице активности).

Показать все комментарии

На сборку Sales enterprise (on-site) установлено приложение "Расчет рабочих дней в бизнес-процессах" https://marketplace.terrasoft.ru/template/raschet-rabochih-dney-v-biznes-processah

Пакет отображается в конфигурации, но действия процесса не попадают в список действий процесса при добавлении в пользовательский бизнес-процесс.

Каким образом можно это исправить?

 

Нравится

2 комментария
Лучший ответ

Скорее всего проблема в иерархии пакетов. Сделайте зависимость своего пакета от их пакета

Скорее всего проблема в иерархии пакетов. Сделайте зависимость своего пакета от их пакета

Добрый день, Мария!

 

Алексей прав, по умолчанию действия процесса будут доступны только для бизнес-процессов пакета Custom. Если вы настраиваете бизнес-процессы в своем пользовательском пакете, добавьте зависимость в свой пакет от пакета приложения GlbCalendarService.

Показать все комментарии