Публикация
//Добавляем в методы
 
methods: {
            setValidationConfig: function() {
                this.callParent(arguments);
                this.addColumnValidator("UsrLastName", this.lastNameValidator); // Только кириллица и тире. Количество символов не может превышать 100 UsrLastName, UsrFirstName и т.д  - названия колонок
                this.addColumnValidator("UsrFirstName", this.firstNameValidator);// Только кириллица и тире. Количество символов не может превышать 100
                this.addColumnValidator("UsrMiddleName", this.middleNameValidator);// Только кириллица и тире. Количество символов не может превышать 100
                this.addColumnValidator("UsrPhone", this.usrPhoneValidator);// Номер телефона не соответствует маске +Ц(ЦЦЦ)ЦЦЦ-ЦЦ-ЦЦ
                this.addColumnValidator("UsrOGRN", this.usrOGRNValidator);//ОГРН должен состоять из 13 цифр
                this.addColumnValidator("UsrINN", this.usrINNValidator);// ИНН ЮЛ должен состоять из 10 цифр
                this.addColumnValidator("UsrAULFullName", this.usrAULFullNameValidator);// Полное наименование ЮЛ не может превышать 512 символов
                this.addColumnValidator("UsrAULShortName", this.usrAULShortNameValidator);// Сокращенное наименование ЮЛ не может превышать 255 символов
                this.addColumnValidator("UsrEmail", this.usrEmailValidator);// Некорректное значение адреса электронной почты
                this.addColumnValidator("UsrLicPer", this.usrLicPerValidator);// Значение должно быть в диапазоне от 1 до 5
            },
            usrLicPerValidator: function() {
                var invalidMessage = "";
                var value = this.get("UsrLicPer");
                invalidMessage = this.usrLicPerFunctionNG(value);
                return {
                    fullInvalidMessage: invalidMessage,
                    invalidMessage: invalidMessage
                };
            },
            usrLicPerFunctionNG: function(value) {
                return value < 1 || value > 5 ? "Значение должно быть в диапазоне от 1 до 5" : "";
            },
            usrEmailValidator: function() {
                var invalidMessage = "";
                var value = this.get("UsrEmail");
                invalidMessage = this.usrEmailFunctionNG(value);
                return {
                    fullInvalidMessage: invalidMessage,
                    invalidMessage: invalidMessage
                };
            },
            usrEmailFunctionNG: function(value) {
                if (!Ext.isEmpty(value)) {
                    if (!((/^[-._a-z0-9]+@(?:[a-z0-9][-a-z0-9]+\.)+[a-z]{2,6}$/.test(value)))) {
                        return "Некорректное значение адреса электронной почты";
                    }
                }
                return "";
            },
            usrAULFullNameValidator: function() {
                var invalidMessage = "";
                var value = this.get("UsrAULFullName");
                invalidMessage = this.usrAULFullNameFunctionNG(value);
                return {
                    fullInvalidMessage: invalidMessage,
                    invalidMessage: invalidMessage
                };
            },
            usrAULFullNameFunctionNG: function(value) {
                return value && value.length > 512 ? "Полное наименование ЮЛ не может превышать 512 символов" : "";
            },
            usrAULShortNameValidator: function() {
                var invalidMessage = "";
                var value = this.get("UsrAULShortName");
                invalidMessage = this.usrAULShortNameFunctionNG(value);
                return {
                    fullInvalidMessage: invalidMessage,
                    invalidMessage: invalidMessage
                };
            },
            usrAULShortNameFunctionNG: function(value) {
                return value && value.length > 254 ? "Сокращенное наименование ЮЛ не может превышать 255 символов" : "";
            },
            usrINNValidator: function() {
                var invalidMessage = "";
                var value = this.get("UsrINN");
                invalidMessage = this.usrINNFunctionNG(value);
                return {
                    fullInvalidMessage: invalidMessage,
                    invalidMessage: invalidMessage
                };
            },
            usrINNFunctionNG: function(value) {
                if (!Ext.isEmpty(value)) {
                    if (!((/^\d{10}$/.test(value)))) {
                        return "ИНН ЮЛ должен состоять из 10 цифр";
                    }
                }
                return "";
            },
            usrOGRNValidator: function() {
                var invalidMessage = "";
                var value = this.get("UsrOGRN");
                invalidMessage = this.usrOGRFunctionNG(value);
                return {
                    fullInvalidMessage: invalidMessage,
                    invalidMessage: invalidMessage
                };
            },
            usrOGRFunctionNG: function(value) {
                if (!Ext.isEmpty(value)) {
                    if (!((/^\d{13}$/.test(value)))) {
                        return "ОГРН должен состоять из 13 цифр";
                    }
                }
                return "";
            },
            usrPhoneValidator: function() {
                var invalidMessage = "";
                var value = this.get("UsrPhone");
                invalidMessage = this.mobileFunctionNG(value);
                return {
                    fullInvalidMessage: invalidMessage,
                    invalidMessage: invalidMessage
                };
            },
            mobileFunctionNG: function(value) {
                if (!Ext.isEmpty(value)) {
                    if (!((/^\+\d\(\d{3}\)\d{3}\-\d{2}\-\d{2}$/.test(value)))) {
                        return "Номер телефона не соответствует маске +Ц(ЦЦЦ)ЦЦЦ-ЦЦ-ЦЦ";
                    }
                }
                return "";
            },
            lastNameValidator: function() {
                var invalidMessage = "";
                var value = this.get("UsrLastName");
                invalidMessage = this.fioFunctionNG(value);
                return {
                    fullInvalidMessage: invalidMessage,
                    invalidMessage: invalidMessage
                };
            },
            firstNameValidator: function() {
                var value = this.get("UsrFirstName");
                var invalidMessage = "";
                invalidMessage = this.fioFunctionNG(value);
                return {
                    fullInvalidMessage: invalidMessage,
                    invalidMessage: invalidMessage
                };
            },
            middleNameValidator: function() {
                var value = this.get("UsrMiddleName");
                var invalidMessage = "";
                invalidMessage = this.fioFunctionNG(value);
                return {
                    fullInvalidMessage: invalidMessage,
                    invalidMessage: invalidMessage
                };
            },
            fioFunctionNG: function(value) {
                if (!Ext.isEmpty(value)) {
                    if (!((/^[а-яА-ЯёЁ][а-яА-ЯёЁ\-]{1,99}$/.test(value)))) {
                        return "Только кириллица и тире. Количество символов не может превышать 100.";
                    }
                }
                return "";
            }
        },

 

Нравится

Поделиться

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

Симптомы

Сделал скрипт автонумерации в InvoicePageV2, работает только для контакта Zherlygin, в остальных случаях запрос присылает null. 

Данные с профайлера. 

Object {value: "b7f3b127-51be-494e-b184-58fe18c0e781", displayValue: "Кривошеева Екатерина", primaryImageValue: "00000000-0000-0000-0000-000000000000"}
 
exec sp_executesql N'
SELECT
NULL [Id],
NULL [UsrInvCounter],
NULL [PhotoId],
[Photo].[Name] [Photo.Name]
FROM
[dbo].[SysEmpty] [Contact] WITH(NOLOCK)
LEFT OUTER JOIN [dbo].[SysImage] [Photo] WITH(NOLOCK) ON ([Photo].[Id] = [Contact].[Id])
WHERE
NULL = @P1',N'@P1 uniqueidentifier',@P1='B7F3B127-51BE-494E-B184-58FE18C0E781'
 
 
 
Object {value: "410006e1-ca4e-4502-a9ec-e54d922d2c00", displayValue: "Жерлыгин Дмитрий", primaryImageValue: "00000000-0000-0000-0000-000000000000"}
 
exec sp_executesql N'
SELECT
[Contact].[Id] [Id],
[Contact].[UsrInvCounter] [UsrInvCounter],
[Contact].[PhotoId] [PhotoId],
[Photo].[Name] [Photo.Name]
FROM
[dbo].[Contact] [Contact] WITH(NOLOCK)
LEFT OUTER JOIN [dbo].[SysImage] [Photo] WITH(NOLOCK) ON ([Photo].[Id] = [Contact].[PhotoId])
WHERE
[Contact].[Id] = @P1',N'@P1 uniqueidentifier',@P1='410006E1-CA4E-4502-A9EC-E54D922D2C00'

Причина

Не правильно розданы права доступа для объекта Contact.

Решение

Перераздать права.

Нравится

Поделиться

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

Вопрос

Как создать произвольный html виджет (widget) для Итогов.

Ответ

1. Создать модуль с кодом:

define("UsrMySimpleWidget", ["ext-base", "terrasoft", "sandbox", "BaseFiltersGenerateModule",
"UsrMySimpleWidgetResources"], function(Ext, Terrasoft, sandbox, BaseFiltersGenerateModule, resources) {
 
           function getViewModel() {
                return Ext.create("Terrasoft.BaseViewModel", {
                     entitySchema: "Activity",
                     methods: {
                           getChart: function(key) {
                                sandbox.publish("GenerateChart", key);
                           },
                           load: function() {
                           }
                     }
                });
           }
 
           function generateMainView(renderTo) {
 
                var resultConfig = Ext.create("Terrasoft.Container", {
                     id: "tableOtchetMetkiParamContainer",
                     selectors: {
                           wrapEl: "#tableOtchetMetkiParamContainer"
                     },
                     renderTo: renderTo
                });
                return resultConfig;
           }
 
           function fillDom() {
                var htmlAdded = "<div>hello world!</div>";
                Ext.get("tableOtchetMetkiParamContainer").setHTML(htmlAdded);
           }
 
           var render = function(renderTo) {
 
                var viewConfig = generateMainView(renderTo);
                var viewModel = getViewModel();
 
                fillDom();
 
                viewConfig.bind(viewModel);
                viewConfig.render(renderTo);
           };
 
           return {
                schema: "Activity",
                methods: {
 
                },
                userCode: function() {
 
                },
                init: function() {
 
                },
                filterChanged: function(filter, eOpts) {
 
                },
                render: render
           };
     }
);

2. Добавить виджет на панель Итогов и выбрать вышесозданную схему.

Нравится

Поделиться

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

подскажите пожалуйста - вы ничего не упустили? или это скелет в котором половина пропущена? При попытке сохранить модуль JS выдает кучу ошибок чуть ли не в каждой строке

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

Вопрос

В 7.7 при разработке с "useFileContent = true" изменения js-ника не подхватываются при очистке кеша. Вместо этого необходимо постоянно выполнять операцию "Зафиксировать клиентские модули в БД".

Если это не критично, можна ли настроить поведение как в 7.6?

Ответ

В версии 7.7 появился модуль «SectionBundleModule», в котором лежат коды всех схем разделов и карточек (базовых). А также «ConfigurationBootstrap» в него входят базовые схемы, необходимые для работы системы.

Если Вы введете разработку функционала в базовых и замещенных схем, то необходимо в Web.config (...\Terrasoft.WebApp\Web.config) изменить в ключе «UseIncludeDependenciesSource» изменить признак на «false» (). Это связано с тем, что в версии 7.7 разработаны «SectionBundleModule» (для работы разделов/карточек/деталей) и «ConfigurationBootstrap» (минимальный набор для отображения интерфейса) для кэширования данных и тем самым увеличить быстродействие быстродействия.

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

Нравится

Поделиться

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

Вопрос

Есть ли какой-нибудь способ сделать карточку доступной только для редактирования?

Ответ

В BasePageV2 определен метод updateButtonsVisibility().

1 способ. Переопределить его, таким образом, чтобы ShowSaveButton, было false:

this.set("ShowSaveButton", false);

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

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

Нравится

Поделиться

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

Вопрос

По какому принципу работают представления в мобильном приложении? При импорте данных в оффлайн-режиме представления сохраняются в базу как обычные таблицы. В рамках задачи необходимо фильтровать поле, у которого справочником является представление. В представлении используются записи из двух разделов (Контрагенты и кастомное Строительные объекты). Выходит, что при добавлении нового контрагента в десктоп-версии он появляется в представлении, и по нему можно фильтровать. При создании нового контрагента в оффлайн-режиме мобильного приложения в представлении он появляется, очевидно, только после синхронизации, что затрудняет работу пользователя. Насколько я понимаю, в SQL-Lite есть возможность работы с представлениями. Есть ли возможность реализовать функционал представлений? Код представления в файле. Версия приложения 7.9.2 2410. Версия мобильного приложения 7.11.7.

Ответ

 

В SQLite есть возможность создавать представление. Для это нужно выполнить скрипт создания представления в конфигурационном модуле.

Для этого нужно создать сам модуль и добавить его в манифест в блок CustomSchemas

 

Пример кода:

 

var sqls = [“CREATE VIEW IF NOT EXISTS AccountView (Id, Name) AS  SELECT Id, Name FROM Account”];
Terrasoft.Sql.DBExecutor.executeSql({
   isCancelable: false,
   sqls: sqls,
   success: function() {},
   failure: function() {}
});



 

Реализовать в карточке фильтрацию по вьюхе будет довольно сложно, придется все равно писать кастомное бизнес-правило, которое будет делать запрос к view.

 

По умолчанию с представлениями на уровне SQLite мы не работаем. В этом нет смысла, т.к. представление в MSSQL или Oracle может вообще не совпадать по реализации с представленим в SQLite. Представление в мобильном приложении – это обычная таблица, следовательно с ним нужно работать соответственно.

 

Это значит, что если нужно, чтобы значение там появилось, его следует туда добавить. Для этого можно реализовать бизнес-правила на объекты «Контрагент» и «Строительные объекты», в которых при добавлении записи или ее обновлении будет делаться копия этой записи в нужное представление.

Нравится

Поделиться

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

Вопрос

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

Пример:

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

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

Ответ

В реестре может отображаться не только колонка для отображения, но можно выводить и значения, сформированные на основании значений нескольких колонок. Для такого сложного форматирования, или отображения разных значений в зависимости от условий, значения колонок можно задавать в виде функций (используя методы Terrasoft.sdk.GridPage.setPrimaryColumn() и Terrasoft.sdk.GridPage.setSecondaryColumn()):

Terrasoft.sdk.GridPage.setPrimaryColumn('Account', {
	columns: ['Name', 'PrimaryContact'],
	convertFunction: function(values) {
		if (!Ext.isEmpty(values.PrimaryContact)) {
			return values.Name + ' (' + values.PrimaryContact + ')';
		} else {
			return values.Name;
		}
	}
});

Кроме того, можно указывать дополнительные колонки и при выборе значения поля-справочника. Делается это по аналогии с гридом, но только используется класс Terrasoft.sdk.LookupGridPage:

Terrasoft.sdk.LookupGridPage.setSecondaryColumn("Account", "PrimaryContact");

А вот альтернативный вариант расширения возможностей грида - изменение шаблона элементов грида:

Terrasoft.util.writeStyles(
	".div-table {",
		"display:table;",
		"width:100%;",
	"}",
	".div-table-row {",
		"display:table-row;",
		"width:100%;",
		"clear:both;",
	"}",
	".div-table-col {",
		"float:left;",
		"display:table-column;",
		"min-width:50%;",
	"}",
	".div-table-col-button {",
		"float:right;",
		"display:table-column;",
	"}"
);
Ext.define("MyCustomList", {
	override: "Ext.Terrasoft.List",
 
	initializeItemTpl: function() {
		this.callParent(arguments);
		var store = this.getStore();
		var model = store.getModel();
		var modelName = model.getName();
		if (modelName === "Account") {
			var tpl = this.getItemTpl();
			tpl.html =
			"<div class=\"x-list-item-tpl div-table\">" +
				"<div class=\"div-table-row\">" +
					"<div class=\"div-table-col\">{[this.applyPrimaryColumn(values)]}</div>" +
					"<div class=\"div-table-col-button\">{Phone}</div>" +
				"</div>" +
				"<div class=\"div-table-row\">" +
					"<div class=\"div-table-col\">{[this.applySecondaryColumn(values)]}</div>" +
					"<div class=\"div-table-col-button\">{Web}</div>" +
				"</div>" +
			"</div>";
		}
	}
 
});

 

Нравится

Поделиться

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

Задача

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

Решение

Поиск можно доработать в «строке поиска по базе знаний» страницы портального пользователя. Это отдельный элемент, похожий на командную строку, использующий ее вид, но реализация его логики содержится в схеме KnowledgeBaseSearchModule.

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

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

http://ваш-сайт/0/Nui/ViewModule.aspx#PortalMainPageModule/

Для реализации сложного фильтра вместо фильтра только по названию статьи, необходимо изменить следующий код, путем замещения базовой схемы KnowledgeBaseSearchModule:

/*sessionFilters.CustomFilters = {
    value: value,
    displayValue: value,
    primaryDisplayColumn: true
};*/
 
var filters = Ext.create("Terrasoft.FilterGroup");
filters.addItem(Terrasoft.createColumnFilterWithParameter(
    Terrasoft.ComparisonType.CONTAIN, "Notes", value));
filters.addItem(Terrasoft.createColumnFilterWithParameter(
    Terrasoft.ComparisonType.CONTAIN, "Name", value));
filters.addItem(Terrasoft.createColumnFilterWithParameter(
    Terrasoft.ComparisonType.CONTAIN, "Keywords", value));
filters.logicalOperation = Terrasoft.LogicalOperatorType.OR;
 
var serializationInfo = filters.getDefSerializationInfo();
serializationInfo.serializeFilterManagerInfo = true;
 
sessionFilters.CustomFilters = {
    "null": {
        "displayValue": value,
        "filter": filters.serialize(serializationInfo)
    }
};

 

Также для того, чтобы в колонку "Keywords" записывались публичные теги для последующего поиска по этой колонке, необходимо реализовать события изменения, добавления и удаления тегов базы знаний, путем создания их в замещающем объекте KnowledgeBaseInTagV2.

 

var userConnection = (UserConnection)HttpContext.Current.Session["UserConnection"];
Guid entityId = Entity.GetTypedColumnValue<Guid>("EntityId");
var esq = new EntitySchemaQuery(userConnection.EntitySchemaManager, "KnowledgeBaseInTagV2");
var publicTypeId = "D6FB4DE6-0809-41FE-A84F-6D245CBC5F32";
esq.AddColumn("Tag.Name");
var entityFilter = esq.CreateFilterWithParameters(FilterComparisonType.Equal,
    "Entity.Id", entityId);
var typeFilter = esq.CreateFilterWithParameters(FilterComparisonType.Equal,
    "Tag.Type", publicTypeId);
esq.Filters.Add(entityFilter);
esq.Filters.Add(typeFilter);
var entityCollection = esq.GetEntityCollection(userConnection);
string allTags = string.Empty;
foreach (var entity in entityCollection) {
    var tagName = entity.GetTypedColumnValue<string>("Tag_Name");
    allTags += tagName + ", ";
}
using (DBExecutor executor = userConnection.EnsureDBConnection()) {
    Update updateRelationshipQuery = new Update(userConnection, "KnowledgeBase");
    updateRelationshipQuery.Set("Keywords", Column.Parameter(allTags));
    updateRelationshipQuery.Where("Id").IsEqual(Column.Parameter(entityId));
    updateRelationshipQuery.Execute(executor);
}
return true;

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

UPDATE KnowledgeBase
SET Keywords = ISNULL(res.Tags, '')
FROM (SELECT a.Id, Tags = (SELECT stuff((
                select ', ' + Name from (SELECT t.Name FROM KnowledgeBaseTagV2 t WHERE
                                t.TypeId = 'D6FB4DE6-0809-41FE-A84F-6D245CBC5F32'
                                and t.Id
                                IN (
                                                SELECT e.TagId FROM KnowledgeBaseInTagV2 e WHERE e.EntityId = a.Id
                                )
) tb FOR XML PATH('')), 1, 2, ''))
FROM KnowledgeBase a
GROUP BY a.Id) res
WHERE res.ID = KnowledgeBase.Id

Где 'D6FB4DE6-0809-41FE-A84F-6D245CBC5F32' идентификатор типа "публичный тег".

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

Версия приложения со страницей портального пользователя и базовой схемой KnowledgeBaseSearchModule.

Нравится

Поделиться

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

Вопрос

У нас возникла задача программно запретить редактирование поля "Поставщика" на странице договора (ContractPage) при наступлении определенных условий. Все попытки сделать что-то вроде this.get или find('Supplier') и установить ему enabled или IsEnabled или всё что удалось нагуглить - не работает. 

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

Ответ

Данную возможность можно реализовать следующими способами:

var supp = this.find('Supplier');
supp.customConfig = {
    enabled: { bindTo: 'methodName''}
}

или

var supp = this.find('Supplier');
supp.customConfig = {
    enabled: false
}

Следующий пример кода демонстрирует блокирование поля "Поставщик" если значение поля "Номер" не равняется 2:

var supp = this.find('Supplier');
supp.customConfig = {
    enabled: { bindTo: 'test'}
}
this.methods.test = function(){ return this.get('Number') == '2' };

 

Нравится

Поделиться

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

 Случайное число в процессе. Код элемента "задание сценарий":

var rnd = new Random();
int rndValue = rnd.Next(1,10);
Set("int1",rndValue); // "int1" - код параметра процесса
return true;

Нравится

Поделиться

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