контакт
Лид
создание
Технические вопросы
7.x

Добрый день!
На странице редактирования лида при вводе в поле контакт имени контакта система предлагает создать новый. И если нажать на "Создать", то сразу же создастся контакт с введенным именем в поле.
Стоит задача, что бы после нажатия "создать" открывалась страница контакта, и можно было дополнять данные уже на странице контакта.

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

Нравится

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

Здравствуйте!

Функциональность создания новой записи из справочника реализована в LookupQuickAddMixin.

В последней релизной версии уже встроена функциональность открытия карточки перед созданием объекта, в методах tryCreateEntityOrOpenCard->openPageForNewEntity.

За эту функциональность отвечает фича UseSilentCreation.

"Demchenko Olha" написал:

Здравствуйте!

Функциональность создания новой записи из справочника реализована в LookupQuickAddMixin.

В последней релизной версии уже встроена функциональность открытия карточки перед созданием объекта, в методах tryCreateEntityOrOpenCard->openPageForNewEntity.

За эту функциональность отвечает фича UseSilentCreation.

Ольга, спасибо!

Я, как раз перед Вашим ответом нашел нужный модуль LookupQuickAddMixin :smile:
Заместил его, и добавил необходимое условие, чтобы срабатывал метод openPageForNewEntity.
Все работает корректно!

Я попробовал в 7.16 менять эту фичу, но она ни на что не повлияла. Как была возможность создать новую запись прямо из поля, так и осталась

В 7.16 карточка лида выглядит уже не так, как в первом сообщении темы. Возможно, и логика менялась. Где именно вводите и создаётся запись?

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

В таблице SysAdminUnitType (Типы административных юнитов, на данный момент мне все понятно со всеми типами кроме одного - "Team" (3)

Что это за тип ? Пока что на практике не встретил его ни разу, да и пока что даже не могу предположить где такой тип админ.юнитов создается.

Нравится

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

Здравствуйте,
на карточке “группы” есть поле “гид” и деталь “контакты группы”. Необходимо при изменении поля “гид” пройтись по всем контактам в детали и используя значение поля “сделка” из каждого контакта, изменить поле “гид” для всех соответсвующих сделок этих контактов в разделе “сделки”.
Реализовали данную задача с помощью БП, но меняется только одна запись, подскажите пожалуйста, как можно читать всю выборку записей из детали и для множества соответствующих записей в другом разделе менять поле.

Нравится

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

Здравствуйте!

Судя по описанию необходимо работать одновременно с коллекцией данных, а не первой выбранной записью.
Пока с коллекцией может работать только элемент "Добавить данные" в режиме выборки.
Для решения вашей задачи можно использовать [Задание-сценарий], написать в нем интересующий запрос.
Или создать циклический процесс, который будет обрабатывать 1 контакт за 1 итерацию в цикле.

Спасибо, с помощью сценария сделали, чтобы процесс повторялся столько раз, сколько контактов в детали

int count = Get<int>("count");
int count2 = count - 1;
Set<int>("count", count2);
return true;

но изменение срабатывает несколько раз для одной и той же записи, не подскажите, пожалуйста, как в цикле перебрать все контакты?

Здравствуйте!

Алгоритм следующий:
А. Добавить в объект Контакты служебное поле с типом Логическое, например ForCycle.
Б. Первый процесс запускается по нужному событию и устанавливает для всех контактов ForCycle = true.
В. Второй процесс циклический:
1) читать кол-во контактов, у которых ForCycle = true
2) поток по умолчанию ведет к завершению процесса
3) условный поток: [Читать кол-во записей] > 0 - ведет к элементу Читать первый контакт, где ForCycle = true. На этом этапе мы получим Id первого интересующего контакта - > обрабатываем контакт - > устанавливаем ForCycle = false -> в конце поток перехода ведет к Пункт 1

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

Спасибо, получилось.

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

Если Вам необходимо управлять доступностью (enabled) или видимостью (visible) вы биндите их на атрибут (который предварительно создаете) и в последующем меняя значение атрибута - управляете соответствующим значением.
И все таки... (ради спортивного интереса, и для более углубленно понимания системы и применяемых в ней механизмов)
Каким образом в коде можно подступиться до элемента/поля и воздействовать на его свойства ?
(Это же компоненты Ext.JS насколько я понимаю)
Ну естественно я говорю не о том чтобы получить DOM-объект по имени компонента или по CSS-селектору (Ext.get() / Ext.getCmp()) и применить к нему стили инлайново.
Я про то как доступиться до конфига, изменить и инициализировать изменения ?

PS: Есть где ни будь кстати материал по разработке и внедрению кастомных элементов ? (быстрое гугление по форуму не помогло, хотя возможно я некорректно формулировал поисковый запрос :))

Нравится

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

Здравствуйте, Илья.

Помимо ExtJs в bpm'online также используется библиотека BackboneJs. Если на момент построение модели в ViewModel генераторе не были определены биндинги - изменить значение какого-либо свойства в рантайме Вы уже не сможете. Разве что писать свой пользовательский генератор.

Документации по написанию пользовательских контролов на данный момент нет. Только по аналогии с уже существующими или же наследуясь от них.

Вопрос:
Как создать свой контрол?
Ответ:

контрол:

Ext.ns("Terrasoft");
/**
 * @class Terrasoft.controls.UsrMyControl
 * Класс элемента управления для отображения
 */
Ext.define("Terrasoft.controls.UsrMyControl", {
	extend: "Terrasoft.Component",
	alternateClassName: "Terrasoft.UsrMyControl",
	mixins: {
	},
	myParam: "",
	/**
	 * @inheritdoc Terrasoft.Component#tpl
	 * @protected
	 * @overridden
	 */
	tpl: [
		/*jshint white:false */
		"<div id='{id}-usr-my-control' class='{wrapClass}'>",
		"{myParam} test-test-test",
		"</div>"
		/*jshint white:true */
	],
	getTplData: function() {
		var tplData = this.callParent(arguments);
		tplData.myParam = this.myParam;
		this.updateSelectors(tplData);
		return tplData;
	},
	/**
	 * @inheritDoc Terrasoft.Component#init
	 * @protected
	 * @overridden
	 */
	init: function() {
		this.callParent(arguments);
		this.addEvents(
			"myMethod"
		);
	},
	/**
	 * @inheritDoc Terrasoft.Component#constructor
	 * @overridden
	 */
	constructor: function() {
		this.callParent(arguments);
	},
 
	updateSelectors: function(tplData) {
		var id = tplData.id;
		var baseIdSuffix = "-usr-my-control";
		var selectors = this.selectors = {};
		selectors.wrapEl = "#" + id + baseIdSuffix;
		return selectors;
	},
	/**
	 * Подписка на события элементов элемента управления
	 */
	initDomEvents: function() {
		this.callParent(arguments);
		var wrapEl = this.getWrapEl();
		if (wrapEl) {
			wrapEl.on("click", this.onClick, this);
		}
	},
	/**
	 * Обработчик события click на основном элементе
	 * @param event
	 */
	onClick: function(event) {
		event.stopEvent();
		this.fireEvent("myMethod", this);
	},
	/**
	 * Возвращает конфигурацию привязки к модели. Реализует интерфейс миксина {@link Terrasoft.Bindable}.
	 * @overridden
	 */
	getBindConfig: function() {
		var parentBindConfig = this.callParent(arguments);
		var bindConfig = {
			myParam: {
				changeMethod: "setMyParam"
			}
		};
		Ext.apply(bindConfig, parentBindConfig);
		return bindConfig;
	},
 
	setMyParam: function(val) {
		this.myParam = val || this.myParam;
		if (this.allowRerender()) {
			this.reRender();
		}
	},
	/**
	 * @overridden
	 * @inheritdoc Terrasoft.Component#destroy
	 */
	onDestroy: function() {
		var wrapEl = this.getWrapEl();
		if (wrapEl) {
			wrapEl.un("myMethod", this.onClick, this);
		}
		this.callParent(arguments);
	},
	/**
	 * Устанавливает значение флага только для чтения
	 * @param {Boolean} readonly Значение флага только для чтения. Если true - элемент управления устанавливается в
	 * режим только для чтения, false - рабочий режим элемента управления
	 */
	setReadonly: function(readonly) {
		if (this.readonly === readonly) {
			return;
		}
		this.readonly = readonly;
		if (this.allowRerender()) {
			this.reRender();
		}
	}
});

генератор:

 define("UsrMyControlGenerator", ["UsrMyControlGeneratorV2Resources", "terrasoft", "ext-base", "UsrMyControl"],
		function(resources) {
	var UsrMyControlGenerator = Ext.define("Terrasoft.configuration.UsrMyControlGenerator", {
		extend: "Terrasoft.ViewGenerator",
		alternateClassName: "Terrasoft.UsrMyControlGenerator",
		generateUsrMyControl: function(config) {
			var usrMyControl = {
				className: "Terrasoft.UsrMyControl",
				id: config.name + "UsrMyControl",
				selectors: {wrapEl: "#" + config.name + "UsrMyControl"},
				myParam: {bindTo: config.getMyParam},
				myMethod: {bindTo: config.myMethod}
			};
			if (!Ext.isEmpty(config.wrapClassName)) {
				usrMyControl.classes = {
					wrapClassName: config.wrapClassName
				};
			}
			return usrMyControl;
		}
	});
	return Ext.create(UsrMyControlGenerator);
});

схема:

 define("CasePage", ["CasePageResources", "terrasoft", "UsrMyControlGeneratorV2", "UsrMyControl"],
	function(resources, Terrasoft) {
	return {
		entitySchemaName: "Case",
		details: /**SCHEMA_DETAILS*/{}/**SCHEMA_DETAILS*/,
		attributes: {
			"Test": {
				"dataValueType": Terrasoft.DataValueType.TEXT,
				"type": Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
				"value": "123"
			}
		},
		diff: /**SCHEMA_DIFF*/[
			{
				"operation": "insert",
				"parentName": "SolutionTab_gridLayout",
				"name": "UsrTest",
				"propertyName": "items",
				"values": {
					"className": "Terrasoft.UsrMyControl",
					"layout": { "colSpan": 24, "rowSpan": 1, "column": 0, "row": 4 },
					"generator": "UsrMyControlGenerator.generateUsrMyControl",
					"visible": true,
					"getMyParam": "getMyParam",
					"myMethod": "myMethod"
				}
			}
		]/**SCHEMA_DIFF*/,
		methods: {
			onEntityInitialized: function() {
				this.callParent(arguments);
				// just for debug:
				document.scope = this;
			},
			getMyParam: function() {
				return this.get("Test");
			},
			myMethod: function() {
				var oldTest = this.get("Test");
				this.set("Test", oldTest + "!");
			}
		},
		rules: {}
	};
});
Показать все комментарии
печатная форма
Технические вопросы
7.x

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

Нравится

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

Здравствуйте, Александр!

Если в настройках браузера активна функция "Спрашивать куда сохранять файл перед скачиванием", то при скачивании ПФ у Вас будет появлятся окно, где Вы указываете путь сохранения файла и можете изменить его название (см. скриншоты). Также, Вы можете изменить название ПФ в справочнике "Печатные формы".

Показать все комментарии
копирование
раздел
Технические вопросы
7.x

Нужно разработать логику которая бы копировала и данные детали раздела.
Есть такой код isCopyMode

onEntityInitialized: function() {
                                if (this.isAddMode() || this.isCopyMode()) {
//код
}
}

Но как узнать данные источника откуда копируется раздел ?

Нравится

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

Если это isCopyMode то id от кого скопировались узнать можно так:
this.get("SourceEntityPrimaryColumnValue")
Если isAddMode, то понятно, что нет никакого источника.

Максим спасибо.
Буду делать копирование так

onEntityInitialized: function() {
				if (this.isCopyMode()) {
 
this.get("SourceEntityPrimaryColumnValue"); //id источника
//код копирования
 
				}

вопрос...как я понимаю пока еще нет же созданной записи в БД?

"Юсупов Марат" написал:как я понимаю пока еще нет же созданной записи в БД?

Все верно, данной записи еще нет в бд, но вы можете вызвать:
this.save({silent:true}) что сохранит данную запись в бд, не закрывая карточку. После чего можно будет добавлять в неё и записи деталей.

Показать все комментарии
AdmiUnit
организационная структура
функциональная роль
Технические вопросы
7.x

В системе введено 2 сущности: элементы организационной структуры "Орг.юниты" и "Функциональные роли", в каждую из них по отдельности можно включать пользователей, и друг друга.
Причем включение одной сущности в другую создает реверс-тождественную связь (Добавление Функ.роли в Орг.Юнит равнозначно добавлению Орг.Юнита в Функ.Роль, эти связи вне зависимости от направления даже описываются в одной таблице).
Так вот, логично было бы предположить что данное разделение для обеспечения многопользовательского режима в приложении имеет смысл на уровне предоставления прав доступа, но эксперимент показывает весьма непредсказуемое поведение:
Пользователь А включен в Функц.Роль B которая в свою очередь включена в Орг.Юнит C.
В настройках прав доступа, например на объект "Контакты", установлено запрещающее правило на "Чтение", что фактически скрывает раздел для Орг.Юнита C.
Логическая цепочка: Пользователю A, раздел контакты станет недоступен, т.к. он входит в Функ.Роль B которая в свою очередь входит в Орг.юнит С, которому запрещено чтение по объекту "Контакты".
Фактически: Пользователю A - остается доступен раздел "Контакты", и ограничение он получает только если будет непосредственно включен в Орг.Юнит C
т.е. ограничения распостраняются только на пользователей непосредственно включенных в соответствующую Функ.роль или Орг.Юнит, через включение их друг в друга пользователям не наследуются никакие разрешения целевой группы, так же как и пользователям целевой что либо от включаемой.

А теперь самый главный вопрос: Зачем ?!
Какой смысл в 2-х сущностях, и в возможности их связи друг с другом, если это ни коим образом не связано с наследованием предоставленных разрешений ?!
И зачем вообще тогда нужны Функциональные роли...
В чем вообще "соль" обособленности Орг.Юнитов и Функ.Ролей ?

Нравится

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

Здравствуйте, Илья.

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

Ответы на ваши вопросы вы можете найти в статьях на Академии: https://academy.terrasoft.ru/documents/sales-team/7-9/razdel-upravleniy…
https://academy.terrasoft.ru/documents/sales-team/7-9/stranica-organiza…
https://academy.terrasoft.ru/documents/sales-team/7-9/stranica-funkcion…

Также, рекомендуем вам просмотреть видео:
https://www.youtube.com/watch?v=mSWfz61NKFI
https://www.youtube.com/watch?v=x5C6VcOhKj4

В них поясняются все особенности работы с Организационными и Функциональными ролями и описаны примеры настройки прав доступа для Функциональных и Организационных ролей.

Ну с членством в другой функ.роли которой явно разрешено и разрешающее правило расположено выше - понятно.

а вот тут, прошу пару слов пояснения:

"Мария Ватулина" написал:входить в дргую функциональную роль, которой доступ к данному разделу не запрещен

Т.е. включая пользователя в Орг.юнит или Функ.роль которой явно что-то не запрещено, все связанные явно заданные запрещающие правила всех других включений для пользователя - не применяются ?

Илья,

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

Рекомендуем ознакомиться с видео (ссылки указаны выше), там озвучены все нюансы настройки прав доступа для Организационных и Функциональных ролей.

Показать все комментарии
Автонумерация полей
Технические вопросы
7.x

Здравствуйте, подскажите, пожалуйста, реально ли реализовать автонумерацию строк в детали, к примеру “контакт в группе” с учетом удаленных записей и т.д.?

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

Нравится

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

Вряд ли это надо делать с системной настройкой, так как с деталью обычно в одно время работает 1 пользователь. Достаточно перед сохранением записи брать максимальное значение в группе.
А при удалении - пересчитывать номер у всех последующих записей.
Ну, и дать возможность пользователю менять очередность записей, если необходимо.

Но, согласен - было бы здорово иметь такой функционал в базовом продукте, чтобы не изобретать велосипед

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

Подскажите, по статье ниже добавляли автонумерацию?
https://academy.terrasoft.ua/documents/technic-sdk/7-9/primer-ispolzova…

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

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

"Мотков Илья" написал:то есть даже если зайти в создание счета, выйти, и зайти снова, номер будет другой.

Это не самая корректная реализация. Правильнее присваивать номер счета после сохранения записи

спасибо :smile:

Показать все комментарии
Технические вопросы
7.x

Коллеги, приветствую! Дурацкий вопрос. Вот кусок кода:

statusChanged: function() {
        if (this.get("Status").value === '394d4b84-58e6-df11-971b-001d60e938c6') {
        var esq = this.Ext.create("Terrasoft.EntitySchemaQuery", {rootSchemaName: "VwAvailablePlaces"});
        esq.addColumn("Classroom", "Classroom");
        var freePlacesColumn = esq.addColumn("FreePlaces", "FreePlaces");
        freePlacesColumn.orderDirection = Terrasoft.OrderDirection.ASC;
        freePlacesColumn.orderPosition = 0;
        esq.filters.addItem(
                this.Terrasoft.createColumnFilterWithParameter(
                        this.Terrasoft.ComparisonType.EQUAL, "Branch", this.get("Branch").value));
        esq.getEntityCollection(function(response) {
           if (response.success && response.collection.getCount() > 0) {
                var classRoomValue = response.collection.getByIndex(0).get("Classroom").value;
                var classRoomDisplayValue = response.collection.getByIndex(0).get("Classroom").displayValue;
                 this.set("Classroom", {value: classRoomValue, displayValue: classRoomDisplayValue});
            }
          }, this);
     }                         

Так вот сортировка игнорируется(что ASC, что DESC - один фиг). Вот это место:

        freePlacesColumn.orderDirection = Terrasoft.OrderDirection.ASC;
        freePlacesColumn.orderPosition = 0;

А ну да, самое прикольное, что на сервак-то это передается. В консоли:

orderDirection: 1 (ну или 2 для DESC)
orderPosition: 0

Вообще смахивает на системную ошибку это все. Или нет? Код - точная копия того, как делают ОНИ. В чем проблема? Это 7.9, если что.

Нравится

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

Добрый день, Дмитрий!

Только что проверил на своей 7.9.2 - направление сортировки работает. Попробуйте в это же представление в базе выполнить запрос с ASC и DESC, возможно, там всего одна запись, потому и есть ощущение, что сортировка не отрабатывает.

Илья, спасибо за ответ. Ну да, вьюха показала небольшой недочет. Спс.

Показать все комментарии
7.9
Notes
Примечания
Технические вопросы
7.x

Подскажите, возможно ли вытягивать поле "Примечания" (на вкладках "Файлы и примечания") автоматически по высоте текста?

Нравится

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

Добрый день!

Конечно можно. Для решения Вашего кейса нужно применить собственные css-стили. Как это можно сделать в приложении:
http://www.community.terrasoft.ru/forum/topic/16386

Подробное руководство по css:
https://developer.mozilla.org/en-US/docs/Web/CSS

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