Использование ES6 в JavaScript модулях и клиентских схемах?
Собственно пробовал ли кто ? или может у оф.представителей есть рекомендации/предостережения на этот счет.
Уж больно много возможностей предоставляет новый стандарт, и фактически полностью поддерживается мажорными версиями всех популярных браузеров - ну как минимум в части:
promise, yield, именованных аргументов функций и значений по умолчанию, деструктуризация массивов и объектов и т.д.

Нравится

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

Здравствуйте, в системных требованиях все еще значится IE11:
https://academy.terrasoft.ua/documents/marketing/7-8/sistemnye-trebovan…
А следовательно в рамках ядра и схем системы не может быть ни промисов, ни большинства других фишек ES6.
Вы конечно можете начать использовать в своих доработках любые вещи из ES6, но тем самым ограничите доступ к системе соответствующими браузерами.
Таблица поддержки ES6 браузерами:
https://kangax.github.io/compat-table/es6/

IE11... будь он неладен :)
Ну гипотетически с версии 7.10 - появляется полноценная выгрузка модулей в ФС и их комбэк (еще не пробовали но в release notes - есть)
Это позволяет подключить к делу Babel, не известно как он дружит с AMD-нотацией конечно, надо попробовать "бабельнуть"
Вот собственно пример:
Исходный код схемы использующей ES6

define("KmProjects1Page", ["TestMixin"], function(TestMixin) {
	return {
		entitySchemaName: "KmProjects",
		details: /**SCHEMA_DETAILS*/{}/**SCHEMA_DETAILS*/,
		diff: /**SCHEMA_DIFF*/[]/**SCHEMA_DIFF*/,
		attributes: {
			"TestAttrDefinition": {
				"dataValueType": Terrasoft.DataValueType.BOOLEAN,
				"value": false,
				"dependencies": [
					{
						"columns": ["Test"],
						"methodName": "TestMethod"
					}
				]
			}
		},
		methods: {
			"testES6Usage": function() {
			  // Expression bodies
        var odds = evens.map(v => v + 1);
        var nums = evens.map((v, i) => v + i);
        var pairs = evens.map(v => ({even: v, odd: v + 1}));
 
        // Statement bodies
        nums.forEach(v => {
          if (v % 5 === 0)
            fives.push(v);
        });
 
        // Lexical this
        var bob = {
          _name: "Bob",
          _friends: [],
          printFriends() {
            this._friends.forEach(f =>
              console.log(this._name + " knows " + f));
          }
        }
 
        //classes
        class SkinnedMesh extends THREE.Mesh {
          constructor(geometry, materials) {
            super(geometry, materials);
 
            this.idMatrix = SkinnedMesh.defaultMatrix();
            this.bones = [];
            this.boneMatrices = [];
            //...
          }
          update(camera) {
            //...
            super.update();
          }
          get boneCount() {
            return this.bones.length;
          }
          set matrixType(matrixType) {
            this.idMatrix = SkinnedMesh[matrixType]();
          }
          static defaultMatrix() {
            return new THREE.Matrix4();
          }
        }
 
        //promise
        let promise = new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve("result");
          }, 1000);
        });
        promise.then(
            result => {
              alert("Fulfilled: " + result);
            },
            error => {
              alert("Rejected: " + error);
            }
          )
			}
		},
		mixins: {
			TestMixin: "Terrasoft.TestMixin"
		},
		rules: {}
	};
});

После обработки Babel

"use strict";
 
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
 
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
 
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
 
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
 
define("KmProjects1Page", ["TestMixin"], function (TestMixin) {
  return {
    entitySchemaName: "KmProjects",
    details: /**SCHEMA_DETAILS*/{} /**SCHEMA_DETAILS*/
    , diff: /**SCHEMA_DIFF*/[] /**SCHEMA_DIFF*/
    , attributes: {
      "TestAttrDefinition": {
        "dataValueType": Terrasoft.DataValueType.BOOLEAN,
        "value": false,
        "dependencies": [{
          "columns": ["Test"],
          "methodName": "TestMethod"
        }]
      }
    },
    methods: {
      "testES6Usage": function testES6Usage() {
        // Expression bodies
        var odds = evens.map(function (v) {
          return v + 1;
        });
        var nums = evens.map(function (v, i) {
          return v + i;
        });
        var pairs = evens.map(function (v) {
          return { even: v, odd: v + 1 };
        });
 
        // Statement bodies
        nums.forEach(function (v) {
          if (v % 5 === 0) fives.push(v);
        });
 
        // Lexical this
        var bob = {
          _name: "Bob",
          _friends: [],
          printFriends: function printFriends() {
            var _this = this;
 
            this._friends.forEach(function (f) {
              return console.log(_this._name + " knows " + f);
            });
          }
        };
 
        //classes
 
        var SkinnedMesh = function (_THREE$Mesh) {
          _inherits(SkinnedMesh, _THREE$Mesh);
 
          function SkinnedMesh(geometry, materials) {
            _classCallCheck(this, SkinnedMesh);
 
            var _this2 = _possibleConstructorReturn(this, _THREE$Mesh.call(this, geometry, materials));
 
            _this2.idMatrix = SkinnedMesh.defaultMatrix();
            _this2.bones = [];
            _this2.boneMatrices = [];
            //...
            return _this2;
          }
 
          SkinnedMesh.prototype.update = function update(camera) {
            //...
            _THREE$Mesh.prototype.update.call(this);
          };
 
          SkinnedMesh.defaultMatrix = function defaultMatrix() {
            return new THREE.Matrix4();
          };
 
          _createClass(SkinnedMesh, [{
            key: "boneCount",
            get: function get() {
              return this.bones.length;
            }
          }, {
            key: "matrixType",
            set: function set(matrixType) {
              this.idMatrix = SkinnedMesh[matrixType]();
            }
          }]);
 
          return SkinnedMesh;
        }(THREE.Mesh);
 
        //promise
 
 
        var promise = new Promise(function (resolve, reject) {
          setTimeout(function () {
            resolve("result");
          }, 1000);
        });
        promise.then(function (result) {
          alert("Fulfilled: " + result);
        }, function (error) {
          alert("Rejected: " + error);
        });
      }
    },
    mixins: {
      TestMixin: "Terrasoft.TestMixin"
    },
    rules: {}
  };
});

Вполне себе "съедобный код, если только внутрях какой-либо сборщик не обрезает содержимое за рамками define конструкции, но вроде - нет такого.

PS: Единственное, что для полного счастья надо бы подключить полифил от Babel, и вот кстати вопрос - как в рамках идеологии системы корректно подключать JS-либы ?

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

Это было и раньше, но для on-site.
"Севостьянов Илья Сергеевич" написал:корректно подключать JS-либы

Через define, к примеру схема: jQuery, там в теле дефайна просто функция которая сама себя и вызывает, обогащая глобальный контекст либой JQuery, в глобальную переменную $
Так что полифилы можно подключить примерно таким же способом, т.к. они обогащают глобальный контекст.
В схеме где нужна будет данная логика, схему либы необходимо будет подключать в блок зависимостей:
define("ContactPageV2", ["JQuery"], function() { ... Либо в ините, через реквайр:
require(["jQuery"], function() {});
Так же через define можно делать либы что возвращают объект, но с этим думаю понятно.
define("ContactPageV2", ["MyLib"], function(myLib) { ...
require(["MyLib"], function(myLib) {});

"Максим Шевченко" написал:Это было и раньше, но для on-site.

В каком формате ?

Мы изначально сильно болели этой темой начиная работать еще с 7.8
Задавали вопрос в саппорт, вот нам как раз про "весенний релиз" - обещали, ну вот в 7.10 появилось в релиз-нотах.

Если это можно сделать для 7.8, 7.9 - прошу поделиться HowTo ? (Т.к. проекты есть и вряд ли они прям быстро сейчас все пообновляются)

PS: Можно было чекаутить/чекинить из SVN в ФС - но это то еще удовольствие особенно в части работы со схемами объектов и Бизнес процессами, да и метаданных тьма, абсолютно не ясно что там от чего зависит, т.к. судя по истории комитов в SVN из приложения просто работа с кодом схем иногда приводит к изменению каких-то метаданных и т.д.

"Севостьянов Илья Сергеевич" написал: каком формате ?

Я имел в виду useFileContent, для он-сайт, выгружает схемы в ФС, и позволяет разрабатывать прямо там, вот инструкция: https://academy.terrasoft.ru/documents/technic-sdk/7-9/rabota-s-klients…

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

"Севостьянов Илья Сергеевич" написал:ну это только схемы, причем "скопом" без иерархического какого либо деления, что не есть удобно - это раз, во-вторых это никоим образом не позволяет в виде файлов опять же деплоить на прод... вообщем годится только для выгрузки сорцов по сути.

Скоро будет документация по ФС в 7.10:
http://www.community.terrasoft.ru/forum/topic/25319

Будет с иерархией, но тоже только для on-site.

Ну я так понимаю будет возможность пакеты заворачивать в zip/gz и сетапить.
На демочке уже засекли занятные новые опции:

Я так понимаю речь как раз идет про пакеты выгружаемые в ФС.
Но засетапить их можно будет я так понимаю и в cloud версии ?

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

Да, для сайтов on-demand также доступна данная функция. В версии 7.10 пользователи могут самостоятельно устанавливать пакеты.

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

Здравствуйте, необходимо, чтобы после изменения данных контакта менялся ответственный на пользователя, который совершил изменения,
создали бизнес-процесс:

Сигнал – изменение записи любого поля контакта
Изменить данные контакта, где id = сигнал.Id записи, установить ответственный = контакт текущего пользователя

Однако БП уходит в бесконечный цикл, если добавить условие  |ответственный не равно контакт текущего пользователя| в блок “Изменить данные”, то БП в цикл не уходит, но и ответственный не меняется.
Подскажите, пожалуйста в чем ошибка?

Нравится

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

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

В процессе добавлен сигнал по изменению любого поля в объекте, при этом в рамках этого же процесса происходит изменение поля. В результате процесс зацикливается.
Для решения задачи измените режим в стартовом сигнале Ожидать изменения = Любого поля из выбранных (вместо любого поля). Выберите все поля, кроме Ответственный.

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

Добрый день, подскажите, пожалуйста, как можно реализовать sql запрос на удаление данных в элементе "Задание- сценарий" БП или же в схеме карточки:

DELETE FROM [dbo].[Lead] WHERE [Id] = 'c6deb935-c86e-44f6-9101-eec17473c1df'

В БП в сценарии таким способом не выходит подключить userConnection:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using Terrasoft.Common;
using Terrasoft.Core;
using Terrasoft.Core.DB;
using Terrasoft.Core.Entities;
var delete = new Delete(userConnection)
.From("Lead")
.Where("Id").IsEqual(Column.Parameter("{c6deb935-c86e-44f6-9101-eec17473c1df}"));
return true;

Нравится

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

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

Вы можете получить экземпляр UserConnection следующим образом:

UserConnection UserConnection = Get("UserConnection");

Спасибо, теперь возникает ошибка:

The type arguments for method 'Terrasoft.Core.Process.ProcessModel.Get(string)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

Если убрать галочку с "Для интерпретируемого процесса", то процесс выполняется успешно, но лид не удаляется, хотя напрямую sql запросом удаление происходит, в чем может быть проблема?

"Maria H" написал:пасибо, теперь возникает ошибка:

Илья чуть-чуть ошибся, метод гет, это дженерик метод, ему нужно передавать тип, следовательно получение коннекшена будет выглядеть следующим образом:

var userConnection = Get<UserConnection>("UserConnection");
Показать все комментарии

Необходимо расширить логику для некоей карточки использующей базовый save метод, но расширить логику необходимо уже после встроенной валидации обязательных полей.
Как необходимый кандидат подходит функция saveEntityInChain из BasePageV2
Она вызывается до непосредственного сохранения, но после проверки обязательных полей, и все замечательно пока логика - синхронная.
А вот как быть если расширение логики подразумевает вызов асинхронный ?
Сам вызов saveEntityInChain в рамках цепочки вызовов, т.е. заворачивая в коллбек this.callParent() мы фактически выходим из чейна в связи с чем имеем ряд предсказуемых ошибок.

this.Terrasoft.chain(
        this.saveCheckCanEditRight,
        this.saveAsyncValidate,
        this.saveEntityInChain,
        function() {
                this.Ext.callback(callback, scope || this);
        }, this);

Каким образом можно управлять текущим состоянием chain - и расширить его необходимым вызовом, или приостановить его исполнение до выполнения собственной асинхронной логики ?

Нравится

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

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

Либо, вот еще обходной путь которым решали подобные задачи:

1. Создаем атрибут булевного типа, который будет описывать выполнена ли esq\асинхронная функция или еще нет. К примеру: IsEsqCompleted, с значением по умолчанию false.
2. Замещаем метод save, в нем проверяем если this.get(“IsEsqCompleted”) === false то вызываем наш esq\асинхронную функцию, если истина, то this.callParent(arguments);
3. В самом esq\асинхронной функции, в колбеке, устанавливаем IsEsqCompleted в true, и еще раз вызываем scope.save(); где scope, это this сохраненный в переменную scope вне колбека, что бы с помощью замыкания в колбеке был доступ к скоупу карточки.

Таким образом, по событию сохранения, вначале выполнится ваш метод, установится IsEsqCompleted в истину, сработает callParent и будет произведено сохранение карточки.

Спасибо, описанный Вами метод, довольно "старый прием" :) И в данном случае он наверное единственный наиболее лаконичный.
PS: К моменту написания вопроса, я забыл что в JS SDK документации есть ссылка на исходник функции, к счастью метод chain, хоть и не удостоился какого либо исчерпывающего описания, но его исходный код все таки представлен, что поставило все на свои места.

PPS: По началу я думал что это некая имплементация Ext.Promise или обертка над его использованием.
Собственно говоря, конструктив идентичный:

var promise = new Ext.Promise(function(resolve, reject){
    if(expression fulfilled){
         resolve(data);
     }
     else{
         reject(error reason);
     }
 });
 promise.then(
     function(value){ ... }, //success callback
     function(error){ ... }, //reject callback
     function(value){ ... }, //progress callback
     Scope
 );

Если бы это было так, то внутри любой функции chain присутствовал бы self.then / this.then метод, который позволил бы расширить цепочку вызова своей функцией, или даже своим чейном/промисом

 promise.then(
     function(value){ ... }, 
     function(error){
         self.then(function(){
                //добавляемый в chain код
          })}, 
     function(value){ ... }, 
 );

Но попытки найти что либо подобное среди доступных методов в отладчике - ничего не дала.

Держите пример использования террасофт чейн:

obj = {a: 2, b: 3}
 
var func1 = function(next) {
	console.log(this.a);
	next();
}
 
var func2 = function(next) {
	console.log(this.b);
	next();
}
 
var func3 = function(next) {
	console.log("end");
}
 
Terrasoft.chain(
	func1,
	func2,
	func3,
	obj
);

Да уже понятно все по исходному коду самой функции.
В случае если это замещающая схема - то там в любом случае использовать callParent,
что по сути тоже chain - но реализован он иначе, и вот где-то в изначальном объектв происходит вызов next()
Хотя в приведенном выше примере, я только что посмотрел в исходном коде - прямой вызов next() все равно не используется везде идет вызов this.Ext.callback (предполагаю что это или альтернатива или туда в callback и подставляется следующий аргумент чейна автоматически).

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

Здравствуйте последняя функция в базовой реализации в BaseEntityPage,

function() {
this.Ext.callback(callback, scope || this);
}, this);

Сделана для того, чтобы на уровень выше, можно было вызвать callParent передав свой метод\цепочку, которая(й) выполнится после базовой.
К сожалению, на уровень выше, в BasePageV2 уже такой возможности не предоставили, так что на уровне вашей карточки, отвечая на ваш вопрос, вмешаться в центр цепочки, до сохранения, но после валидации, можно полным переопределением, и вызывать свою функцию с асинхронной частью, в колбеке которой продолжать цепочку. Пример:

save: function(config) {
				this.showBodyMask({
					selector: this.get("ContainerSelector") || null,
					timeout: 0
				});
				this.Terrasoft.chain(
					this.saveCheckCanEditRight,
					this.saveAsyncValidate,
					this.myMethod,
					this.saveEntityInChain,
					this.saveDetailsInChain,
					function() {
						this.onSaved(this.cardSaveResponse, config);
						this.cardSaveResponse = null;
						delete this.cardSaveResponse;
					},
				this);
			},
 
			myMethod: function(callback, scope) {
				window.console.log("myMethod.");
 
				var esq = this.Ext.create("Terrasoft.EntitySchemaQuery", {
					rootSchemaName: "Contact"
				});
				esq.addColumn("Name");
				var paramDataType = this.Terrasoft.DataValueType.GUID;
				var lookupFilter = this.Terrasoft.createColumnFilterWithParameter(Terrasoft.ComparisonType.EQUAL,
					"Id", "c4ed336c-3e9b-40fe-8b82-5632476472b4", paramDataType);
				esq.filters.addItem(lookupFilter);
 
				// вызов асинхронной части
				esq.getEntityCollection(function(result) {
					// ... любые синхронные действия
					// продолжение цепочки
					this.Ext.callback(callback, scope || this);
				}, this);
			}
Показать все комментарии

7.9.2
По умолчанию в системе насколько я понимаю для объекта SysSettingsValue нет прав даже у Supervisor пользователя, т.к. ESQ-запросом получить ничего невозможно.

Почему такое ограничение введено на эту таблицу ?

Нравится

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

Илья, здравствуйте!

Это системная таблица. Также через ESQ нет возможности получить данные из объекта SysAdminOperationGrantee. Пример получения значения системной настройки с кодом DataServiceQueryTimeout:

Terrasoft.SysSettings.querySysSettings(["DataServiceQueryTimeout"], function(sysSettings) {
	if (!Ext.isEmpty(sysSettings.DataServiceQueryTimeout)) {
		this.dataServiceQueryTimeout = sysSettings.DataServiceQueryTimeout;
	}
}, this);

Причина: значения системных настроек могут отличаться для разных ролей пользователей. По этой причине использовать ESQ не корректно.

Как выяснилось в число "особенных таблиц" так же входит "SysAdminUnitInRole"

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

Добрый день, коллеги!

Каким образом можно решить подобную задачу по ограничению прав доступа - если у пользователя есть права на редактирование записи (например, контрагента), то ему доступны все поля (где нет ограничений по колонкам), если есть доступ на чтение, то эти же поля доступны только для чтения.
Но если на запись нужно дать ограниченный доступ на чтение, то доступен другой набор полей (например, Название, Код и Ответственный).

В каком VIEW ограничить набор колонок и каким признаком лучше выдавать "ограниченный" набор?

Спасибо!

Нравится

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

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

Что вы имеете ввиду под "ограниченным" доступом?

"Мотков Илья" написал:Что вы имеете ввиду под "ограниченным" доступом?

Приведу пример. Я как ответственный менеджер за контрагента "X" могу видеть и редактировать его карточку (и все поля в ней). А в других контрагентах (где ответственный не я) я могу видеть только 3 поля - название, код и ответственного.

Стандартным функционалом (доступом по записям и колонкам) это не решить

Уже обсуждалось месяц назад, стандартного механизма нет. Только разработкой.
Возможно, в 7.10 можно будет нащёлкать мышкой такое бизнес-правило.

"Зверев Александр" написал:стандартного механизма нет.

Но существуют же VIEW, которые фильтруют доступ по записям? Если в этих VIEW уменьшить количество колонок при определенных условиях, то можно решить этот вопрос

Владимир, ну это же тоже будет доработка.:wink:

Кроме того, не представляю, как можно ограничить во view для каждого, а не одного конкретного пользователя. Это в 3.Х доступ по записям был сделан при помощи view с фильтрацией по SYSTEM_USER, а в 7.Х все пользователи системы работают с БД через одну учётку, прописанную в ConnectionStrings. Фильтры накладываются программно где-то внутри EntitySchemaQuery.

"Зверев Александр" написал:Владимир, ну это же тоже будет доработка

Да, всё тут доработка :D

"Зверев Александр" написал:Фильтры накладываются программно где-то внутри EntitySchemaQuery

Это уже сложнее, конечно.. Но, возможно, реализуемо. По крайней мере, это критично важно.

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

"Зверев Александр" написал:Нужно всего лишь наряду с механизмами в ядре по выдаче прав на объекты, на поля, на записи и на записи по умолчанию сделать ещё один.

Думаю, что в банковских проектах такое реализовывали. Осталось найти, кто :)

Думаю, нет. В ходе проекта такое реализуют скриптами под конкретное поле, согласно ТЗ.

Реализовывали программной доработкой. При этом советуем базовый механизмы не трогать. В основном реестре оставлять видимыми только те записи, где доступ полный. И делать отдельную вью с доступом на все записи. Обычно такое делают, если хотят избежать дублей для поиска. Ну вот по нажатию на кнопку "Поиск во всей базе" - открывать отдельную вью со всеми записями, но только тремя колонками. Но такое нельзя реализовать в самом разделе "Контрагенты"

 

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

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

Нравится

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

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

define("ContactSectionV2", ["ContactSectionV2Resources"],
	function(resources) {
		return {
			entitySchemaName: "Contact",
			diff: /**SCHEMA_DIFF*/[
			]/**SCHEMA_DIFF*/,
			methods: {
				loadGridData: function() {
 
					var filters = this.getFilters();
					if (filters.collection.items.length === 0) {
						return;
					}
 
					this.beforeLoadGridData();
					var esq = this.getGridDataESQ();
					this.initQueryColumns(esq);
					this.initQuerySorting(esq);
					this.initQueryFilters(esq);
					this.initQueryOptions(esq);
					this.initQueryEvents(esq);
					esq.getEntityCollection(function(response) {
						this.destroyQueryEvents(esq);
						this.updateLoadedGridData(response, this.onGridDataLoaded, this);
						this.checkNotFoundColumns(response);
					}, this);
				}
			}
		};
	}
);

Можно так же, очищать реестр в блоке if, на случай если кто-то снимет фильтр обратно.

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

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

Нравится

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

Михаил, здравствуйте!

Вам необходимо смотреть в схему GridUtilitiesV2 (миксин loadGridData, beforeLoadGridData и afterLoadGridData).
Примечание. Изменения будут работать не только для раздела, но и для детали. Вам необходимо реализовывать конкретно в схеме раздела.

"Вильшанский Дмитрий" написал:

Михаил, здравствуйте!

Вам необходимо смотреть в схему GridUtilitiesV2 (миксин loadGridData, beforeLoadGridData и afterLoadGridData).

Примечание. Изменения будут работать не только для раздела, но и для детали. Вам необходимо реализовывать конкретно в схеме раздела.


Спасибо, но я уже нашел метод который срабатывает при прокрутке раздела вниз.
Вот пример:

initQueryColumns: function() {
     this.callParent(arguments);
     this.showInformationDialog("!!!");
}
Показать все комментарии

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

Предлагается 2 варианта использования, собственно на стороне сервера через БП и в UI через вызов специализированного метода getIncrementCode()
Остановимся на втором варианте.
Так вот; есть тут небольшая проблема: если мы хотим показывать собственно сгенерированный номер в поле открывшейся карточки то мы вызываем его при инициализации (собственно аналогично примеру из статьи помещая вызов в onEntityInitialized.
Проблема: Итератор сдвигается при каждом вызове, т.е. открывая карточку в режиме добавления новой записи или копирования номер сдвинется, но это не гарантирует нам того что эта запись в конечном итоге вообще произойдет, т.е. пользователь может просто нажать "Отмена" или уйти с карточки - а номерок уже прирос на один.
Мы конечно можем привязаться к методу save и осуществлять вызов getIncrementCode() там, но номерок-то хочется получить на этапе открытия карточки.

Можно ли как-то вызвать его без сдвига итератора ?
PS: я безусловно понимаю что очевидный ответ - не мороч голову делай сервер-сайд, а для показа сделай ESQ запрос в системную настройку напрямую и добавь еденичку :)
Или получай номерок при открытии, ESQ Update запросом в сист.настройку делай роллбек ее на 1 назад, и повторно вызывай метод уже при сохранении.
Но может есть какие-то более лаконичные способы ?

Нравится

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

Добрый день, Илья!

К сожалению, изменить это логику возможности нет. Также, менять ее, как и использовать Ваш вариант с запросом в системную настройку - не очень хорошая идея, так как есть ненулевая верояность того, что несколько пользователей одновременно начнут создавать, допустим, счет, в результате получим несколько счетов с одинаковым номером. Если все-таки есть желание получать данные номера строго по порядку, можно вызвать данный метод в save.

Понятно,
Хотя кстати для тех же счетов законодательство РФ требует соблюдения строгой очередности, так что надо как-то этот момент контролировать.
Идеальный вариант:
Выдавать очередной номер для демонстрации.
Но в процессе сохранения снова проверять его доступность, и в случае если за время редактирования карточки другим пользователем уже был создан документ (получил текущий номер), то получать следующий и извещать об этом пользователя, мол при сохранении номер документа был изменен т.к. текущий уже занят.
т.е. разделить на метод получения без итерирования:
showIncrementCode()
И собственно метод запроса, установки и итерирования
getIncrementCode()
незначительно расширив логи последнего проверкой на факт предоставления номера для другой записи.

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

BPMOnline: 7.9.2.2410
По какой-то причине в мастере редактирования страниц, при добавлении, у полей с типами: "Целое число", "Дробное число" и "Логическое" отсутствует чекбокс определяющий обязательность для заполнения:



А вот у типов "Справочник", "Строка" и "Дата" - есть ?


Это баг или фича :) ?

Нравится

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

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

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

Логическое поле всегда заполнено значением false (если пользователь явно не указал true), дробное/целое число - значением 0 (если пользователь не указал другое).

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