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

Система версии 7.10.1.1161
Есть карточка заказа и есть кастомная страница подборщика продуктов, которая открывается из карточки заказа по нажатию на специально созданную кнопку. Кастомная страница унаследована от BasePageV2 и привязана к entitySchema Order (то есть к тому же объекту, что и карточка заказа). На ней используется стандартная кнопка Закрыть. Все прекрасно работает, кроме случая когда подборщик открываем из карточки ново создаваемого заказа(add). В этом случае при нажатии Закрыть на кастомной странице выбрасывает обратно в раздел, а должно бы в карточку заказа.

Обратил внимание, что при добавлении нового заказа когда открывается карточка редактирования, то хеш все равно остается от раздела - SectionModuleV2/OrderSection (аналогично и во всех остальных разделах). Вероятно, в этом и есть причина, так как при нажатии Закрыть публикуется BackHistoryState. Ниже код, который используем для открытия карточки подборщика. Подскажите как добиться нужного эффекта, так как кейс с открытием подборщика из нового заказа и есть самый основной.

                                openProductSelector: function() {
                                        var orderId = this.get("PrimaryColumnValue") || this.get("Id");
                                       
                                        var defaultValues = [
                                                        {
                                                                name: "PrimaryColumnValue",
                                                                value: orderId
                                                        },
                                                        {
                                                                name: "UseSeparatedPageHeader",
                                                                value: true
                                                        }

                                                ];
                                       
                                        stateObj = {
                                                                        isSeparateMode: true,
                                                                        schemaName: "SelectProductInOrderPage",
                                                                        moduleId: "CardModuleV2_SelectProductInOrderPage",
                                                                        valuePairs: defaultValues,
                                                                        operation: "open",
                                                                        isInChain: false
                                                                };
                                        requestUrl = "CardModuleV2/SelectProductInOrderPage/";
                                       
                                        this.sandbox.publish("PushHistoryState", {
                                                hash: requestUrl,
                                                stateObj: stateObj
                                        });
                                },     

                                onProductSelectionButtonClick: function() {
                                        this.set("OpenselectProductPage", true);
                                        this.save({isSilent: true});
                                },

                onSaved: function (response, config) {
                    this.hideBodyMask();
                    if (!this.get("NextPrcElReady")) {
                        this.set("NextPrcElReady", response.nextPrcElReady);
                    }
                    if (config && config.isSilent) {
                        this.onSilentSaved(response, config);
                    } else {
                        var updateConfig = this.getUpdateDetailOnSavedConfig();
                        this.sandbox.publish("UpdateDetail", updateConfig, [this.sandbox.id]);
                        this.sendSaveCardModuleResponse(response.success);
                        if (this.get("IsInChain")) {
                            this.onProcessCardSaved();
                            return;
                        }
                        if (this.isNewMode()) {
                            this.onCloseCardButtonClick();
                        } else {
                            this.onProcessCardSaved(true);
                        }
                    }
                    this.set("Operation", Terrasoft.ConfigurationEnums.CardOperation.EDIT);
                    if (!this.destroyed) {
                        this.updateButtonsVisibility(false, { force: true });
                    }
                    this.set("IsChanged", this.isChanged());
                    this.subscribeOwner(config);
                    if (config && config.isSilent) {
                                                if(this.get("OpenselectProductPage")) {
                                                        this.set("OpenselectProductPage", false);
                                                        this.openProductSelector();
                                                }
                        return;
                    }
                    this.updateAmountAfterSave("ProductInProductsTab",
                        function () {
                            this.updateDetail({ detail: "ProductInResultsTab" });
                            this.updateOrderProductSummary();
                        },
                        this
                    );
                },

Нравится

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

пока заказ не сохранен - некуда возвращаться у заказа нет Id а стало быть и хеша не собрать.
В том числе и по этой причине при попытке что либо добавить в деталь на странице, ее карточка карточка предварительно принудительно сохраняется, т.к. если открыть в Chain карточку детали, то куда потом возвращаться :)

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

Илья, но я ведь и делаю save перед открытием. И делаю его silent - как раз как в случае с сохранением из детали. И this.set("Operation", Terrasoft.ConfigurationEnums.CardOperation.EDIT) тоже происходит в методе onSaved до открытия кастомной страницы (про этот "режим" вы говорите? или про silent save все же ?)

В более старых версиях помнится при добавлении новой записи был хеш вида CardModuleV2/OrderPageV2/add/recordId Почему от этого отказались - неясно.

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

this.isAddMode() 

там аккурат считывается значение этого атрибута.

PS:
зачастую просто надо предотвратить поведение при котором новая карточка закрывается при сейве, этот кейс решается именно так.

Илья, режим хранится как раз в атрибуте Operation. То есть он сменяется на EDIT до того как я открываю кастомную страницу. Проблема состоит именно в том, что при нажатии Закрыть на кастомной странице идет запрос BackHistoryState, а там - хеш раздела. Вот и идет вылет в раздел.

В общем, пока проблему обходим следующим путем:

1) На странице заказа перенесли вызов метода, отвечающего за открытие кастомной страницы подборщика, из onSaved в onSilentSaved, так как он вызывается до смены режима в атрибуте Operation
2) сам режим передаем в массиве defaultValues на кастомную страницу. Код выглядит теперь так

openProductSelector: function() {
	var orderId = this.get("PrimaryColumnValue") || this.get("Id");
	var operation = this.get("Operation");
 
	var defaultValues = [
			{
				name: "PrimaryColumnValue",
				value: orderId
			}, 
			{
				name: "UseSeparatedPageHeader",
				value: true
			},
			{
				name: "Operation",
				value: operation
			} 							
		];
 
	stateObj = {
					isSeparateMode: true,
					schemaName: "SelectProductInOrderPage",
					moduleId: "CardModuleV2_SelectProductInOrderPage",
					valuePairs: defaultValues,
					operation: "open",
					isInChain: false
				};
	requestUrl = "CardModuleV2/SelectProductInOrderPage/";
 
	this.sandbox.publish("PushHistoryState", {
		hash: requestUrl,
		stateObj: stateObj
	});
},	
 
onProductSelectionButtonClick: function() {
	this.set("OpenselectProductPage", true);
	this.save({isSilent: true});
},
 
 
onSilentSaved: function() {
	if(this.get("OpenselectProductPage")) {
		this.openProductSelector();
	this.set("OpenselectProductPage", false);
	} else {
		this.callParent(arguments);
	}
}  

3) на кастомной странице создали кнопку по виду идентичную кнопке Закрыть. Видимость этой кастомной кнопки прибиндили к isNewMode, а кнопки Закрыть к противоположному к isNewMode значению.
4) Метод обработчик нажатия на кастомную кнопку открывает наш заказа в режиме редактирования.

onBacktToOrderButtonClick: function() {
		var orderId = this.get("PrimaryColumnValue") || this.get("Id");
		var defaultValues = [
				{
					name: "PrimaryColumnValue",
					value: orderId
				}					
		];
 
		stateObj = {
						isSeparateMode: true,
						schemaName: "OrderPageV2",
						moduleId: "CardModuleV2_" + orderId + "_OrderPageV2",
						valuePairs: defaultValues,
						operation: "edit",
						isInChain: false
					};
		requestUrl = "CardModuleV2/OrderPageV2/edit/" + orderId;
 
		this.sandbox.publish("PushHistoryState", {
			hash: requestUrl,
			stateObj: stateObj
		});				
},

Но это решение все равно несколько "корявое". Так как при создании нового заказа: карточка заказа. Открыть подборщик => Подборщик. Закрыть => Карточка Заказа. Закрыть => попдаем обратно в подборщик вместо раздела.

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

Когда пользователь кликает по гиперссылкам в окне приложения страницы по которым он перемещаются образуют очередь (стек LIFO), типовые действия "Закрыть" / "Сохранить" и т.д. приводят к тому что пользователь "возвращается на карточку", зачастую работая с другими карточками пользователь инициирует БП или иначе изменяет данные, которые ранее были загружены в модели карточек к которым предстоит "вернуться", но когда пользователь "возвращается на них" то данные там не актуализированы.
И это представляет прям насущную проблему.
Её не сложно решить самостоятельно, имея какой-то способ определения факта события "пользователь вернулся на карточку".
Стоит признаться, "ковырялся" я довольно много... но пока что "зацепиться" ни за что не удалось.
Но мне кажется что, способ всё такие есть.

Есть ли у коллективного разума какие-то идеи, знания на этот счет ?

Нравится

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

Сам спросил, сам отвечу
за это отвечает BROADCAST сообщение BackHistoryState
на него есть всего один подписчик в ConfigurationBootstrap.js
его обрабатывает метод onBackHistoryState
так что можно "расширять" его при необходимости.

PS: по факту этот метод не делает ничего кроме вызова

router.back();

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

Ладно, вы справились быстрее, поздравляю :smile:

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

Добрый день!
Возникла проблема при работе с SVN.
Не удается обновить или зафиксировать пакеты.
В конфигурации ошибка выглядит так: "The XML response contains invalid XML"

Лог ошибки:
2017-07-21 13:40:39,965 [86] ERROR NT AUTHORITY\SYSTEM Svn ThrowException - The XML response contains invalid XML
RootCause: The XML response contains invalid XML
SvnErrorCode: SVN_ERR_XML_MALFORMED
SvnErrorCategory: 26
Workspace Number: 0
Workspace Name: Default

В чем может быть проблема?

Нравится

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

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

Похоже на внутреннюю ошибку svn, начать можно с проверки url svn сервера, также можно попробовать выкачать репозиторий какой-то сторонней утилитой (например tortoise svn) и попробовать зафиксировать какие-то изменения.

Также можно открыть рабочую копию в файловой системе, если это on-site, и попробовать выполнить svn cleanup, но осторожно, там есть опция по отмене изменений, она должна быть выключена

"Мотков Илья" написал:

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

Похоже на внутреннюю ошибку svn, начать можно с проверки url svn сервера, также можно попробовать выкачать репозиторий какой-то сторонней утилитой (например tortoise svn) и попробовать зафиксировать какие-то изменения.

Также можно открыть рабочую копию в файловой системе, если это on-site, и попробовать выполнить svn cleanup, но осторожно, там есть опция по отмене изменений, она должна быть выключена


Добрый день, дело в том, что подобные on-site приложения уже ранее были развернуты и на них не возникало таких проблем, так как производилась, так сказать, установка с 0 (подключение svn, установка пакетов из svn и так далее). Проблема возникает только в тот момент, когда пытаемся развернуть новое приложение из готового бэкапа старого. Т.е. получается что конфигурации +- между собой схожи, так что кажется навряд-ли url svn сервера как то менялся.

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

Добрый день!
Столкнулся с проблемой вывода в дашборде продаж, в которых ВСЕ заказы находятся в состоянии Отменен. В моем случае выводятся продажи, в которых есть заказы в состоянии Отменен.

Нравится

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

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

В построителе выборки Вы можете использовать does not exists (или не существует) фильтр, в котором перечислить все остальные состояния отличные от 'Отменен'.
Более подробно о построении таких фильтров (пункт "Установка агрегирующего фильтра" - вместо количества нужно указать опцию не существует):
https://academy.terrasoft.ru/documents/sales-team/7-10/rasshirennyy-fil…

P.S. прикрепила скриншот для наглядности

Алла, спасибо за решение)

Добавьте еще условие, что заказы существуют. И при это не существуют заказы, в которых состояние отличное от "Отменен"

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

Доброго времени суток.
Версия 7.9.

На открытой CTI панели звонков есть функция поиска по контактам (см. скриншот).
Там есть кнопочка с телефонной трубкой. Однако кликнуть по ней, чтобы позвонить, не получается.

Можно ли где-нибудь добавить обработку на этот клик, в какой схеме определён этот блок?
Или убрать кнопку, как вариант.

Нравится

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

обработчик - callPhoneNumber
находится в CtiPanelModelUtilities

добавление строки-элемента в CTIPanel

{
	"operation": "insert",
	"parentName": "ctiPanelMainContainer",
	"index": 4,
	"propertyName": "items",
	"name": "PhoneNumber",
............................................и т.д.
},

Данила, спасибо!

Но есть ещё один момент. Это звонок по кнопке 1 (на прикреплённой картинке).
Можно ли вызвать звонок по кнопке 2?

Также есть аналогичная кнопка в ленте звонков.

там обработчик onNumberClick,
из, скорее всего, CommunicationHistoryItem

Данила,

CommunicationHistoryItem - это для истории звонков. Туда добавил обработчик кликов.

Есть ещё результаты поиска, они на первом скрине были..

Пока нашёл в SubscriberSearchResultItem вот такое:

{
					"operation": "insert",
					"name": "CommunicationItemsListContainer",
					"parentName": "SubscriberSearchResultItemContainer",
					"propertyName": "items",
					"values": {
						"id": "CommunicationItemsListContainer",
						"itemType": Terrasoft.ViewItemType.GRID,
						"markerValue": "CommunicationItemsListContainer",
						"selectors": {"wrapEl": "#CommunicationItemsListContainer"},
						"idProperty": "Id",
						"collection": {"bindTo": "SubscriberCommunications"},
						"onGetItemConfig": {"bindTo": "getCommunicationPanelViewConfig"},
						"classes": {"wrapClassName": ["communications-control-group"]},
						"generator": "CtiContainerListGenerator.generatePartial"
					}
				}

Похоже, что кнопка где-то рядом.. пока не нахожу что-то.

Нашёл - схема SubscriberCommunicationItem.

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

BPM 7.10
При создании нового элемента, при выборе определенного значения категории (например - К1), изменить уровень доступа к полям этого элемента. Т.е. при открытии поле под замком, после выбора категории и установки параметра в значение "К1" - замок убрать.

Предполагаю, выполняться это должно на клиенте, т.е. использование JS.

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

Нравится

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

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

Для этого можно использовать бизнес-правила. Подробнее по ссылке:
https://academy.terrasoft.ru/documents/sales-enterprise/7-10/nastroyka-…

"Терещенко Алексей" написал:Подскажите есть ли библиотека с примерами, которые можно было бы самостоятельно проанализировать для выполнения собственного функционала?

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

"Севостьянов Илья Сергеевич" написал:ну кое что есть на академии в специальном разделе

Спасибо. Реализовал с помощью бизнес-правил.

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

Хочу поделиться интересной 'фичей', обнаруженной при настройке графиков в дешбордах.

Если у Вас в графике есть несколько серий и для первой серии установить тип графика 'Bar' (скрин 1), а для другой 'Column' (скрин 2) (возможно, что в русскоязычной локализации они называются по-другому), то в итоге Вы получите эффект 'вложения' одной серии в другую (для понимания этого эффекта лучше посмотреть прикрепленный скриншот график).

P.S. Не исключаю, что такой эффект после внесения изменений и исправлений в графики после очередного обновления на новую версию может пропасть, так как отображаться в итоге должно нечто другое, соответствующее указанному типу графика, но пока такой эффект присутствует вы можете использовать его в настройке ваших дешбордов, тем более, что нашим пользователям он очень понравился :wink:

Сейчас у нас версия 7.10.2.

Нравится

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

Это именно что фича а не баг, и тянется она давно, и не планирует исчезать :smile:

Максим,

хорошо, если так.

Мы этому будем только рады!

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

В статье Добавление автонумерации к полю страницы описан пример из которого не совсем понятна следующая строка UserTask1.EntitySchema = Entity.Schema;.
"Установка схемы для генерации номера." - т.е. логично предположить, что Entity.Schema - схема автонумерации? Как-то не понятно...
...тогда вопрос: какие существуют еще схемы?
А если возникнет необходимость в одном разделе, в одной таблице еще одни номер сгенерировать (т.е. в двух разных полях разные номера) тогда как?

Нравится

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

"Терещенко Алексей" написал:.е. логично предположить, что Entity.Schema - схема автонумерации? Как-то не понятно...

Ноуп.
UserTask1 - таск генерации номера (пользовательское действие вроде называется, которое вы тащите с левой панели).
.EntitySchema - входящий параметр этого генератора

Entity - экземпляр класса Entity под текущий объект
.Schema - поле со схемой объекта (таблицы)

"Варфоломеев Данила" написал:UserTask1 - таск генерации номера
- нет. генерация номера в данном ЮзерТаск1 - это значение, выбранное в поле "Действие". Как таково сам по себе ЮзерТаск1 - не производит никакой генерации.

На самом деле если Вам необходим просто итерируемый номер, логика описанная в статье - избыточна.
Он имеет смысл к реализации когда номер строится с участием "маски".
Самый простой способ - это создать свою системную переменную под каждую нумеруемую сущность.
И в своей реализации получать значение и итерировать его самостоятельно. (Или на бэкенде в БП или на фронте в JS)
на фронте

//получить значение системной переменной
//https://academy.terrasoft.ru/jscoresdk/#!/api/Terrasoft.core.SysSettings-method-querySysSettings
Terrasoft.SysSettings.querySysSettings(SysSettingNamesArray, callback, scope)
//установить значение системной переменной
//https://academy.terrasoft.ru/jscoresdk/#!/api/Terrasoft.core.SysSettings-method-updateSysSettingsValue
Terrasoft.SysSettings.updateSysSettingsValue(configurationObject, callback, scope)
Показать все комментарии
дистрибутив
Технические вопросы
7.x

Доброго времени суток. Где можно взять установщик BPM Online? Разработка on-cloud недостаточно хороша, хотелось бы вести разработку со своей машины.

Нравится

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

Добрый день,

Для предоставления дистрибутива у Вас на CustomerId должны быть лицензии.
Подскажите, пожалуйста, Ваш CustomerId, а также название Вашей компании.

С уважением,
Наталия

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

Добрый день.
Необходимо открыть карточку из детали без возможности редактирования.
Создал деталь истории и теперь при открытии записи из неё, поля должны быть заблокированы для редактирования. Подскажите как можно реализовать подобную логику?

Нравится

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

Первое что напрашивается это конечно же настройка прав доступа:
https://www.youtube.com/watch?v=x5C6VcOhKj4
Колонки не будут серыми и недоступными на изменение, но запись сохранить у пользователя не получится, будет соответствующее сообщение об отсутствии прав.

Другой способ же, бизнес правила по условиям на "только чтение":
https://academy.terrasoft.ru/documents/technic-sdk/7-8/pravilo-bindpara…
Работы тут больше, т.к. на каждое поле прийдется писать свое правило.

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

А можно как-то скриптом перебрать все поля страницы и всем поставить "только чтение", как это делалось в 3.х?

Добрый день!
Можно сделать с помощью CSS. Т.е. при открытии карточки вы устанавливаете стиль.
К примеру, чтобы все читалось, но не редактировалось (правда и не кликалось), то можно добавить стиль в основной контейнер карточки: pointer-events: none;
Если же вам нужно чтобы только нужные элементы, то подход тот же, но CSS другой.
Пример:
div input,a,span,div[id*='Container_Control'],div[class='base-edit-clear-icon'] {
pointer-events: none;
}
Можно добавить opacity для визуализации :)

Просто другие варианты трудозатратны, а это простой способ

"Владимир Соколов" написал:А можно как-то скриптом перебрать все поля страницы и всем поставить "только чтение", как это делалось в 3.х?

Ну при помощи Ext.JS можно проделать вот такой "хак"

Ext.ComponentMgr.all.each(function(c){
	var cmp = Ext.ComponentMgr.all.map[c];
	if(cmp.className){
		if(cmp.className.indexOf("Edit") !== -1){
			if(cmp.setEnabled){
				cmp.setEnabled(false)
			}
                }
	}
})

Выполнение этого скрипта сделает все поля карточки недоступными для редактирования.

Есть несколько НО:
1) ререндер карточки - это состояние сбросит.
(в основном это создает проблемы, при переходе из карточки в карточку по Chain, что кстати было бы возможно предусмотреть и исправить, если бы на мой вопрос вот здесь, кто ни будь нашел/дал ответ)
2) это не касается каких либо элементов управления кнопок, пунктов меню и т.д. только поля.

"Владимир Соколов" написал:
Максим Шевченко пишет:

Работы тут больше, т.к. на каждое поле прийдется писать свое правило.

А можно как-то скриптом перебрать все поля страницы и всем поставить "только чтение", как это делалось в 3.х?

Собрал замещающий модуль для BasePageV2. Перебирает все поля и проставляет атрибут Enabled = False. Управляется всё дефолтным параметром "CardState" который передаётся в схему.

define("BasePageV2", ["ConfigurationEnums"], function (enums) {
return {
messages: {

},
methods: {
disableControls: function (item, callback) {
if (item.item) {
this.disableControls(item.item)
} else
if (item.className === "Terrasoft.Container" || item.className === "Terrasoft.GridLayout" || item.className === "Terrasoft.ControlGroup") {
for (item of item.items) {
this.disableControls(item)
}
} else
if (this.isEditClassname(item.className)) {
item.enabled = false;
}
else
console.log(item.className);

if (callback)
callback.call(this);
},
isEditClassname: function (name) {
return this.getEditControlsClassNames().indexOf(name) != -1;
},
getEditControlsClassNames: function () {
var EditControlsClassNames = ["Terrasoft.Label","Terrasoft.DateEdit", "Terrasoft.LookupEdit", "Terrasoft.TextEdit",
"Terrasoft.ListView", "Terrasoft.RadioButton", "Terrasoft.TimeEdit", "Terrasoft.FloatEdit",
"Terrasoft.CheckBoxEdit", "Terrasoft.ComboBoxEdit", "Terrasoft.InlineTextEdit", "Terrasoft.ImageEdit", "Terrasoft.IntegerEdit", "Terrasoft.MemoEdit"]
return EditControlsClassNames;
},
getCardState: function () {
return this.getDefaultValueByName("CardState");
},
init: function () {
var cardState = this.getCardState();
if (cardState && cardState === enums.CardState.View) {
var parentMethod = this.getParentMethod();
var arg = arguments;
this.disableControls(arguments[1].viewConfig[0], function () {
parentMethod.apply(this, arg);
});
}
else {
this.callParent(arguments);
}
}
},
diff: /**SCHEMA_DIFF*/[

]/**SCHEMA_DIFF*/
};
});

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