Вопрос

Как посмотреть, что записано в планировщике?

Ответ

Вы можете проверить существующие тригера выполнив запрос в БД:

select * from QRTZ_TRIGGERS
select * from QRTZ_SIMPLE_TRIGGERS

 

Нравится

Поделиться

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

Вопрос

Как создать свой элемент управления?

Ответ

Для создания пользовательского элемента управления необходимо:

1) Создать модуль - класс элемента управления:

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();
		}
	}
});

2) Создать модуль - генератор элемента управления:

define("UsrMyControlGenerator", ["UsrMyControlGeneratorResources", "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);
});

3) Добавить пользовательский элемент управления в схему в секцию diff:

define("CasePage", ["CasePageResources", "terrasoft", "UsrMyControlGenerator", "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: {}
	};
});

В зависимостях схемы обязательно указать модули UsrMyControl и UsrMyControlGenerator.

Нравится

Поделиться

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

Задача

При изменении структуры каталогов, они перестают открываться.

Решение

В схеме FolderManagerViewModel пакета ProductOmnichannel необходимо внести изменения в метод getCatalogueLevelItemsSelect():

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

select.addColumn("Id", "ColumnPathValueId");
var column = select.addColumn("Name", "ColumnPathValueName");

на

select.addMacrosColumn(Terrasoft.QueryMacrosType.PRIMARY_COLUMN, "ColumnPathValueId");
var column = select.addMacrosColumn(Terrasoft.QueryMacrosType.PRIMARY_DISPLAY_COLUMN"ColumnPathValueName");

Данное изменение необходимо по причине того, что у клиента в объекте может отсутствовать такое поле как [Name].

Необходимые условия

Версия 7.6.

 

Нравится

Поделиться

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

Симптомы

Bpm'online mobile bug report:

Тип: Terrasoft.ServerException

Сообщение: Запрос на сервер вернул ошибку Дополнительная информация:

{"request":{"id":4,"headers":{"X-Terrasoft-Mobile":"true","Accept":"application/json","Content-Type":"application/json","Authorization":"Cookie","X-Requested-With":"XMLHttpRequest"},"options":{"url":"http://xxxxx.xxxxxxx.xxxxx/0/Services/ProfileService.asmx/Logout","method":"POST","jsonData":{"customData":"","doLogout":"true"},"headers":{"X-Terrasoft-Mobile":"true","Accept":"application/json","Content-Type":"application/json","Authorization":"Cookie"},"disableCaching":false,"scope":{"initialConfig":{"url":"http://xxxxx.xxxxxxx.xxxxx/0/Services/ProfileService.asmx/Logout","method":"POST","jsonData":{"customData":"","doLogout":"true"},"headers":{"X-Terrasoft-Mobile":"true","Accept":"application/json","Content-Type":"application/json","Authorization":"Cookie"},"disableCaching":false},"performanceCounterKey":"86e101ce-533b-4802-941b-56bf73c7b609"}},"async":true},"requestId":4,"status":500,"statusText":"Internal Server Error","responseText":"{\r\n  \"Code\": -1,\r\n  \"Exception\": \"Terrasoft.Core.LicException: Срок действия лицензии истек либо лицензия неактивна\\r\\n  at Terrasoft.Core.UserConnection.CheckLicense()\\r\\n  at Terrasoft.Core.UserConnection.InitializeCurrentUser(String userName, TimeZoneInfo timeZone, String clientIP, String agent, Boolean logSessionStart)\\r\\n  at Terrasoft.Core.UserConnectionFactory.CreateUserConnection(AppConnection appConnection, AuthData authData, String sessionId, String clientIP, String agent, Boolean checkPasswordExpiry)\\r\\n  at Terrasoft.Web.Common.SessionHelper.RecreateUserConnection(AuthData authData)\"\r\n}","responseXML":null,"responseBytes":null}

Причина

У пользователя отсутствует активная лицензия.

Решение

Для корректной авторизации и работы в мобильном приложении необходимо, чтобы лицензия была роздана пользователю.

Нравится

Поделиться

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

Вопрос

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

Ответ

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

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

Пример на основе файлов базы знаний:

В манифесте в секции ModelDataImportConfig для KnowledgeBaseFile колонки Data установить признак "ImportBinaryData": false

 

{
    "Name": "KnowledgeBaseFile",
    "SyncColumns": [
        {
            "Name": "Data",
            "UseRecordIdAsFileName": false,
            "ImportBinaryData": false
        } 
    ]
}

 

Нравится

Поделиться

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

Вопрос

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

Ответ

Примеров реализации сообщений в нашей системе довольно много. Самым ярким примером могут служить модули BasePageV2 и BaseSectionV2. В этих модулях при инициализации вызывается метод subscribeSandboxEvents(), который выполняет подписку на сообщения sandbox.

Например, в модуле BaseSectionV2 выполняется подписка на сообщение [CardChanged], и когда модуль получает это сообщение, он устанавливает изменённое значение соответствующему атрибуту. Само же сообщение отправляет (публикует) модуль BasePageV2 при вызове метода publishPropertyValueToSection(). В свою очередь, метод publishPropertyValueToSection() вызывается при изменении некоторых атрибутов модели карточки редактирования.

Вы можете поступить похожим образом. Например, в методе init() Вашей карточки редактирования (CasePage) подписаться на изменение поля [ServiceCategory]:

init: function() {
    this.callParent(arguments);
    this.on("change:ServiceCategory", function(model, value) {
        this.publishPropertyValueToSection("CurrentServiceCategory",value);
    }this);
}

Таким образом, при изменении поля [ServiceCategory] карточки новое значение будет записываться в атрибут [CurrentServiceCategory] раздела CaseSection.

После этого в разделе Вы сможете получить значение текущей категории, обратившись к атрибуту [CurrentServiceCategory]:

isEnableButtonColumbus: function() {
    var serviceCategory = this.get("CurrentServiceCategory");
    if (!serviceCategory) {
        // Ваш код
    } else {
        return (serviceCategory.value ===UsrConsts.ServiceCategory.Dynamix);
    }
}

 

Нравится

Поделиться

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

Симптомы

Кейс воспроизведения:

1) открыть задачу в расписании, на детали "Связи" которой заполнен лид, счет, проект и т.д.;

2) перейти по гиперссылке в карточку связанного объекта;

3) открыть вкладку "История" и деталь "Активности";

4) добавить на деталь новую задачу.

В результате получаем ошибку в консоли с сообщением: 

message: Cannot read property 'replaceCls' of null.

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

Причина

Происходит это из-за того, что в ActivitySectionV2 перекрыт метод openCardInChain следующим образом:

openCardInChain: function(config) {
   if (this.isSchedulerDataView() && (config.operation === ConfigurationEnums.CardStateV2.ADD)) {
      var historyStateInfo = this.getHistoryStateInfo();
      if (historyStateInfo.workAreaMode === ConfigurationEnums.WorkAreaMode.COMBINED) {
         this.closeCard();
      }
   }
   return this.callParent(arguments);
},

Этот код присутствует начиная с версии 7.3.0.

Решение

В версии 7.7.0 данное поведение должно быть исправлено.

Для быстрого исправления в конфигурации необходимо заместить схему ActivitySectionV2 и переопределить в ней метод openCardInChain так:

openCardInChain: function(config) {
   if (config.isLinkClick) {
      return false;
   }
   this.saveCardScroll();
   this.scrollCardTop();
   this.showBodyMask();
   var historyState = this.sandbox.publish("GetHistoryState");
   var stateObj = config.stateObj || {
            isSeparateMode: config.isSeparateMode || true,
            schemaName: config.schemaName,
            entitySchemaName: config.entitySchemaName,
            operation: config.action || config.operation,
            primaryColumnValue: config.id,
            valuePairs: config.defaultValues,
            isInChain: true
 };
   this.sandbox.publish("PushHistoryState", {
      hash: historyState.hash.historyState,
      silent: config.silent,
      stateObj: stateObj
   });
   var moduleName = config.moduleName || "CardModuleV2";
   var moduleParams = {
      renderTo: config.renderTo || this.renderTo,
      id: config.moduleId,
      keepAlive: (config.keepAlive !== false)
   };
   var instanceConfig = config.instanceConfig;
   if (instanceConfig) {
      this.Ext.apply(moduleParams, {
         instanceConfig: instanceConfig
 });
   }
   this.sandbox.loadModule(moduleName, moduleParams);
   return true;
}

 

Нравится

Поделиться

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

Вставляем в элемент "Формула" и передаем в "тело письма"

"<div>Обращение "+[#Получение данных по претензии.Первый элемент результирующей коллекции.Номер#]+", "+[#Получение данных по претензии.Первый элемент результирующей коллекции.Тема#]+" от клиента \""+[#Читать Клиента.Первый элемент результирующей коллекции.Название#]+"\" закрыто.</div><div>Суть обращения: "+[#Получение данных по претензии.Первый элемент результирующей коллекции.Описание#]+"</div><div>Решение: "+[#Получение данных по претензии.Первый элемент результирующей коллекции.Решение#]+"</div><div><a href=https://0349402-sales-enterprise-marketing-service-enterprise-demo.bpmonline.com/0/Nui/ViewModule.aspx#CardModuleV2/CasePage/edit/"+[#Получение данных по претензии.Первый элемент результирующей коллекции.Id#]+">Перейти к претензии</a></div>"

 

Нравится

Поделиться

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

а адрес сайта можно как-то не зашивать, а получить в БП?

Например, завести системную настройку, читать в БП из неё.

Зверев Александр пишет:

Спасибо, даже нашёл такую настройку - SiteUrl 

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

Вопрос

Как включить автоматическое обновление Dashboard-ов?

Ответ

На данный момент (версия 7.6.Х) на пользовательском уровне автоматическое обновление включить нельзя, но можно использовать для решения этой задачи подписку на сообщения ReloadDashboard, которая есть у всех виджетов панели итогов (dashboard).

Для этого можно создать свой модуль без родительского объекта, например UsrDashboardHelper,

в сообщениях добавить ReloadDashboard (Направление: Публикация, Режим: Адресное).

Код возможной реализации генерации событий может быть следующим:

define("UsrDashboardHelper", ["ext-base", "terrasoft", "sandbox"], function(Ext, Terrasoft, sandbox) {
   var getView = function() {
      var config = {
         id: "UsrDashboardHelper",
         selectors: {
            wrapEl: "#UsrDashboardHelper"
         },
         items: []
      };
      return Ext.create("Terrasoft.Container", config);
   };
   return {
      timerId: 0,
      myTimer: function() {
         console.log("tik");
         sandbox.publish("ReloadDashboard", null, ["SectionModuleV2_UsrAutomobileSectionSectionDashboardDashboardModule"]);
      },
      init: function() {
         console.log("UsrDashboardHelper is on in: " + sandbox.id);
         this.timerId = setInterval(this.myTimer, 30000);
      },
      render: function(renderTo) {
         var view = getView();
         view.render(renderTo);
      },
      destroy: function() {
         console.log("UsrDashboardHelper is off!");
         clearInterval(this.timerId);
      }
   };
});

После создания модуля, его можно добавить как виджет, в настройках "dashboard-а", что приведет к выполнению его кода на странице. Данная его реализация будет обновлять все виджеты на странице, имеющие подписку на событие ReloadDashboard, и находящиеся в песочнице, указанной третьим параметром в методе sandbox.publish(...) каждые 300000 миллисекунды (5 минут).



Id песочницы вашей страницы итогов можно узнать посмотрев значение "sandbox.id",

в методе подписки всех "виджетов" на событие "ReloadDashboard", поставив точку остановки в модуле DashboardModule.js на строке:

 

sandbox.subscribe("ReloadDashboard", this.onReloadDashboard, this, [sandbox.id]);

 

Нравится

Поделиться

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

Вопрос

Можно ли сделать так, чтобы срок реакции/разрешения считался от момента назначения ответственного? (сейчас он считается от момента регистрации обращения)

Ответ

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

Нравится

Поделиться

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