Доброго дня!

Підкажіть будь ласка, як передати дані з доданих полів деталі "Адреса контакту" в поля нового об'єкту використовуючи бізнес-правило?

При заповненні клієнта хочеться щоб дані з деталі заповнювались у відповідні поля. З типовими полями зрозуміло, налаштував.

Чи власні поля треба обов'язково додавати в об'єкт контакт? Оскільки при додаванні значення з іншого поля я їх просто не бачу. Типи полів: довідник, ціле число.

Нравится

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

Добрий день.

Для того, щоб заповнювати дані з іншого об'єкта за допомогою бізнес правил, Вам потрібно додати довідкове поле, яке посилатиметься на цей об'єкт.

Детальніше про бізнес правила:

https://academy.terrasoft.ua/docs/user/kastomizacija_no_code/nastrojka_…



Також ви можете використовувати бізнес-процеси для того, щоб автоматично заповнювати потрібні вам поля.

Детальніше про бізнес-процес:

https://academy.terrasoft.ua/docs/user/biznes_processy/nastrojka_proces…

Дякую, зробив через поля.

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

Пытаюсь вынести страницу Email из раздел Активности в новый раздел EmailAct.

 

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

 

Для этого создал новый раздел Email и объект EmailAct. После этого в таблица "SysModuleEntity" у нового раздел (значение "Id" взят из таблицы "SysModule" поле "SysModuleEntityId" для EmailAct) указал в поле "SysEntitySchemaUId" как для Активности.

-------

Далее попытался сменить "Code" в "SysModule" с 'EmailAct' на 'Activity'. Но вместо привязки записей из Активности в Email -> получаю два раздел Email без записей.

 

Не подскажите - что я сделал не то?

И как правильно выполнить поставленную задача?

 

Планировалось, что объект активности так и останется активности, просто раздел Активности будет иметь фильтр по тип не равно Email, а раздел Email будет копией Активности, но с фильтру по тип равно Email

 

Спасибо!

 

P.S. Извините за русский, не мой родной язык, но английский ещё хуже знаю (использую переводчик).

Если кто знает армянский - можете на нем ответить - мне будет более просто.

Прикрепленные файлы

Нравится

13 комментариев
Лучший ответ

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



getFilters: function() {

                var filters = this.callParent(arguments);

                filters.add("EmailFilter", this.Terrasoft.createColumnFilterWithParameter(

                    this.Terrasoft.ComparisonType.EQUAL, "Type", ConfigurationConstants.Activity.Type.Email

                ));

                return filters;

            }



Следующий шаг это сделать что бы открывалась нужна страница, я бы создал новый пейдж и унаследовался от EmailPageV2, и перебиндил бы её в таблице SysModueEdit

Точная версия ПО: 7.17.3.1.377

Переносить емейл, уже звучит как не очень хорошая идея, так как "хвостов" среди бд/системной и бизнес логики, может быть очень много, те же самые темплейты. Если бы описали саму задачу, ту что шире, что-то можно было бы подсказать.



Можно ли это сделать? Да. 

Есть ли лучший способ решить задачу? Думаю, что тоже да.

Стас Гаврилюк,

Сейчас email находится в Активности.

Т.к. email активностей может быть много - нужно сделать раздел вноль как раздел Активности, только где была бы одна единственная страница Email и соответственно записи из объекта Активности с типом Email . А в активностях её или убрать или скрыть и сделать так, чтоб записи с типом Email не отображались.

Не подскажите - как это лучше сделать?

Стас Гаврилюк,

Идея была в том, чтобы создать новый раздел ссылающийся на объект активности и имеющий фильтрацию по тип = email. А у Активности добавить фильтрацию по тип не равно email.

 

Стас Гаврилюк,

Хотелось бы прочитать - как лучше это сделать?

Max Froggy,

Т.к. email активностей может быть много - нужно сделать раздел вноль как раздел Активности,

 

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

Стас Гаврилюк,

я сейчас планирую устроить в компанию, которая работает с Creatio, мне дали это задание, как тест знаний.

Я хоть и часто работал с C#/JS и Creatio не совсем понимаю, как это сделать...

Стас Гаврилюк,

Скажите хоть - как бы вы решали такую задачу?

Стас Гаврилюк,

Очень прошу помочь...

Да сами ответили Да на оба вопроса, неужели нет идей, как это сделать?

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



getFilters: function() {

                var filters = this.callParent(arguments);

                filters.add("EmailFilter", this.Terrasoft.createColumnFilterWithParameter(

                    this.Terrasoft.ComparisonType.EQUAL, "Type", ConfigurationConstants.Activity.Type.Email

                ));

                return filters;

            }



Следующий шаг это сделать что бы открывалась нужна страница, я бы создал новый пейдж и унаследовался от EmailPageV2, и перебиндил бы её в таблице SysModueEdit

Стас Гаврилюк, огромное спасибо

как минимум поменять - поменять что?

Стас Гаврилюк,

Проблемка единственное, при клике на Email выделяется почему-тоАктивности

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

 

В приложении существует три основные таблицы в которых хранится информация о разделе:  SysModule, SysModuleEntity, SysModuleEdit.



SysModule - таблица, где хранятся сама информация про раздел.

SysModuleEntity - таблица связи раздела и SysModuleEdit. В ней хранится информация про типизацию страниц.

SysModuleEdit - информация про страницы редактирования раздела.

 

Более детально о таблицах SysModuleEdit и SysModuleEntity можно прочитать здесь:

https://community.terrasoft.ua/articles/tablicy-sysmoduleedit-i-sysmodu…

 

Что касается SysModule:

 [Caption] - Название раздела (можно брать caption объекта раздела.

[SysModuleEntityId]  - колонка Id записи таблицы SysModuleEntity из первого этапа (проверка Select Id from SysModuleEntity Where SysEntitySchemaUId='AF6DD4AD-1398-4FCE-8837-78DC65AE0F9E')

[Code] - Название объекта раздела;

[SectionModuleSchemaUId] - UId модуля SectionModuleV2 с помощью которой открывается схему раздела;

[SectionSchemaUId] - колонка Uid страницы раздела объекта (например, "UsrRK1Section") в таблице SysSchema (проверка Select Uid,Name,Caption from SysSchema where name like '%UsrRK%'). 

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

 

Вот некоторые моменты с которыми пользователи часто сталкиваются при создании этого раздела:

- Вы можете использовать в таблице SysModuleEdit ( колонка CardSchemaUId) схему базовой странице редактирования EmailPageV2.

- Проверьте установлена ли связать этой записи в таблице SysModuleEdit с таблицей SysModuleEntity Вашего раздела.

- Также проверьте что в таблице SysModule Вашего раздела указана корректная запись из таблице  SysModuleEntity.

 

С уважением,

Катя

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

Добрый день. я хочу показать более подробную ошибку пользователю, который запускает бизнесс процесс из кастомной кнопки. Бизнесс процесс запускает C# код, который бросает исключение. Но я не могу найти информацию по результатам выполнения бизнесс процесса.

Я получаю вот такой JSON в коллбеке ProcessModuleUtilities.executeProcess

 

'"{\\"message\\":\\"Произошла ошибка во время выполнения процесса. Пожалуйста, проверьте журнал процессов для получения детальной информации\\",\\"success\\":false,\\"errorInfo\\":{\\"initialConfig\\":{\\"errorCode\\":null,\\"message\\":\\"Произошла ошибка во время выполнения процесса. Пожалуйста, проверьте журнал процессов для получения детальной информации\\",\\"stackTrace\\":null},\\"errorCode\\":null,\\"message\\":\\"Произошла ошибка во время выполнения процесса. Пожалуйста, проверьте журнал процессов для получения детальной информации\\",\\"stackTrace\\":null,\\"instanceId\\":\\"8a078ebd-540c-476a-aab0-98fa4c42ce83\\",\\"hasListeners\\":{},\\"events\\":{\\"destroyed\\":true}}}"'

 

Здесь есть instanceId (8a078ebd-540c-476a-aab0-98fa4c42ce83), но когда я пробую сделать поиск по нему в базе данных, то не нахожу никакой ошибки. 

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

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

 

Если же я выполню бизнес процесс успешно, тогда существует поле processId, по которому я могу найти текст ошибки.

 

Как другим способом можно найти ошибку выполнения бизнесс процесса который запускает C# код?

 

Спасибо

Нравится

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

Или может быть я делаю неправильно когда кидаю исключение в бизнес процессе. Как правильно создать исключение, если бизнес процесс должен быть остановлен?

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

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

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

Добрый день. Возможно ли данный функционал добавить? Это выглядит очень удобно когда надо при нажатии на кнопку запустить бизнесс процесс и показать ошибку если она появилась.

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

Добрый день!

Вопрос по конвертации печатных форм в ПДФ, в Studio 7.18.5.

На маркете есть "бесплатное"  приложение "Aspose.PDF connector for Creatio", которое разрешает 150 конвертаций в месяц, а дальше "по тарифу". Пытаемся понять, это единственный вариант для нашего продукта? Т.е. нету полностью бесплатного варианта?

 

спасибо 

Нравится

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

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

 

Спасибо за вопрос!



На текущий момент могу порекомендовать использовать конвертацию в PDF средствами MS Word через "сохранить как". 

Как обходное решение, если необходимо чтобы после выгрузки PDF файл не меняли пользователи:

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

Также, могу порекомендовать вариант:

1. Формирование отчетов через FastReport - https://academy.terrasoft.ua/docs/developer/komponenty_prilozheniya/otchety/fastreport/obzor

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

 

С уважением,

Анастасия

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

Коллеги, добрый день! Можете, пожалуйста, подсказать, с помощью какого инструмента можно реализовать, например массовое согласование кандидатов с отправкой резюме в письме во вложении направить руководителю на согласование нескольких кандидатов). Или направить нескольким руководителям несколько кандидатов.

Нравится

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

Коллеги, добрый день.



Реализовать данную задачу можно с помощью элемента бизнес-процесса "Отправить Email".

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

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



С уважением, Алексей.

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

Коллеги, добрый день! Подскажите пожалуйста. возможно ли реализовать отправление почтового уведомления кандидату/клиенту, в котором приложен ISC файл. Например в системе в календаре назначается встреча и затем автоматом уходит письмо с вложением ISC, при открытии которого становится встреча в календарь?

Нравится

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

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

 

Подобный функционал не является частью базового функционала Creatio в принципе, так как он связан именно с приложением календаря, которое стоит у пользователя. Основная задача здесь — создать ссылку на собрание как что-то, что создаст взаимодйствие с приложением календаря конечного пользователя и создаст встречу.

 

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

 

С уважением,

Мирослава

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

Добрый день!

Подскажите, пожалуйста, может у кого-то был подобный кейс. Можно ли реализовать визирование объекта непосредственно из e-mail уведомления? При настройке уведомления к элементу визирования, такой возможности не вижу. Предполагаю, что можно в самом шаблоне использовать элемент [кнопка], чтобы там разместить ссылки, содержащие параметры согласовать/отклонить? Но как инициализировать объект  (передать в ссылку GUID объекта), который надо визировать? 

Буду признательна за любой совет!!!

Нравится

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

Хочу добавить в сотрудники в Карьеру фильтруемое справочное поле, не получается составить запрос

"DerivedClients": {

                                "dataValueType": Terrasoft.DataValueType.LOOKUP,

                                "lookupListConfig": {

                                     /* Массив фильтров, которые применяются к запросу для формирования данных поля-справочника. */

                                    "filters": [

                        function() {

                            var filterGroup = Ext.create("Terrasoft.FilterGroup");

                            filterGroup.add("IsUser", Terrasoft.createColumnIsNotNullFilter("[SysAdminUnit:Account].UsrLookup3"));

                            filterGroup.add("IsActive",

                                Terrasoft.createColumnFilterWithParameter(

                                    Terrasoft.ComparisonType.EQUAL,

                                    "[SysAdminUnit:Account].UsrLookup3",

                                    true));

                            return filterGroup;

                        }

                    ]

                              }

                        },

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

Нравится

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

Александр, добрый день!

Привожу пример построения фильтра по справочному полю.

Есть объект StTrip и необходимо отфильтровать справочное поле StTransport по определённому параметру:

"StTransport": {
                "dataValueType": Terrasoft.DataValueType.LOOKUP,
                "lookupListConfig": {
                    "filters": [
                        function() {
                            var filterGroup = Ext.create("Terrasoft.FilterGroup");
                            filterGroup.add("IsActive",
                                Terrasoft.createColumnFilterWithParameter(
                                    Terrasoft.ComparisonType.GREATER,
                                    "[StTransport:Id].StMaxWeight",
                                    this.get("StWeight")));
                            return filterGroup;
                        }
                    ]
                }
            }

Обратите внимание на формулировку StTransport:Id. Надеюсь данный пример поможет вам с вашей задачей.

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

В итоге я сделал как в документации, переопределил модуль EmployeeCareerPag

схему  

define("EmployeeCareerPage", [], function() {
    return {
        /* Название схемы объекта страницы записи. */
        "entitySchemaName": "Employee",
        /* Атрибуты модели представления. */
        "attributes": {
            /* Колонка модели представления. */
            "DerivedClients": {
                /* Тип данных колонки модели представления. */
                "dataValueType": Terrasoft.DataValueType.LOOKUP,
                /* Конфигурационный объект атрибута типа LOOKUP. */
                "lookupListConfig": {
                    /* Массив фильтров, которые применяются к запросу для формирования данных поля-справочника. */
                    "filters": [
                        function() {
                            var filterGroup = Ext.create("Terrasoft.FilterGroup");
                            //filterGroup.add("IsUser", Terrasoft.createColumnIsNotNullFilter("[Account:UsrLookup3].Id"));
                            filterGroup.add("IsUser", Terrasoft.createColumnIsNotNullFilter("[Account:AccountType].Name"));
                            filterGroup.add("IsUser",
                                Terrasoft.createColumnFilterWithParameter(
                                    Terrasoft.ComparisonType.EQUAL,
                                   "[Account:AccountType].Name" === "наш сотрудник",
                                    true));
                            return filterGroup;
                        }
                    ]
                }
            }
        }
    };
});

и сделал такой запрос, но все еще не работает, что я мог тут сделать не так?

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

Добрый день!

 

Подскажите варианты добаления маски номера телефона детали ContactCommunicationDetail.

 

На страничке раздела Контакты, при добавлении средства связи = моб телефон = была преднатсроена маска ввода номера телефона. 

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

Нравится

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

Добрый вечер, Андрий!

 

На данный момент возможности настроить маску пользовательскими средствами нет.

 

Возможно, решить Вашу задачу поможет информация в этом посте https://community.terrasoft.ua/questions/maska-telefona-na-detali-sredstva-svyazi

Добрый день!

Как и сказала Алла, необходимы навыки разработки для добавления маски. Привожу пример как это сделать с колонкой контакта.

Для начала создаем модуль MultiMaskEdit

 define("MultiMaskEdit", ["ext-base", "terrasoft"], function(Ext, Terrasoft) {
    /**
     * @class Terrasoft.controls.MultiMaskEdit
     */
    Ext.define("Terrasoft.controls.MultiMaskEdit", {
        extend: "Terrasoft.TextEdit",
        alternateClassName: "Terrasoft.MultiMaskEdit",
 
        /**
         * @public
         * @type {Terrasoft.Align}
         */
        textAlign: Terrasoft.Align.LEFT,
        /**
         * @protected
         * @overridden
         */
        onFocus: function() {
            this.callParent(arguments);
            var value = this.getTypedValue();
            this.setDomValue(value);
        },
        /**
         * @protected
         * @virtual
         * @return {String}
         */
        getInitValue: function() {
            var value = this.callParent(arguments);
            if (!Ext.isEmpty(value) && !Ext.isEmpty(this.masks)) {
                var formatValue = this.formatValue(value);
                this.value = formatValue.value;
                var validationInfo = this.getValidationInfo(formatValue);
                this.setValidationInfo(validationInfo);
            }
            return this.value;
        },
        getValidationInfo: function(value) {
            var formatValue = (!Ext.isEmpty(value) && Ext.isBoolean(value.isComplete)) ? value : this.formatValue(value);
            var validationInfo = {
                isValid: true,
                invalidMessage: ""
            };
            if (!formatValue.isComplete) {
                validationInfo = {
                    isValid: false,
                    invalidMessage: this.getIncorrectNumberMessage()
                };
            }
            return validationInfo;
        },
        getIncorrectNumberMessage: function() {
            return "Invalid number";
        },
        /**
         * @protected
         * @overridden
         * @param  {Event} e DOM keypress
         * @param  {String} type (optional)
         * значение Terrasoft.DataValueType.FLOAT
         */
        onKeyPress: function(e) {
            this.callParent(arguments);
            if (this.readonly || Ext.isEmpty(this.masks)) {
                return;
            }
            var isSpecialKey = Ext.isGecko && e.isSpecialKey();
            if (isSpecialKey) {
                return;
            }
            e.preventDefault();
            var keyUnicode = e.getKey();
            var key = String.fromCharCode(keyUnicode);
            if (this.baseCharsRe && !this.baseCharsRe.test(key)) {
                return;
            }
            var domEl = e.getTarget();
            var info = this.getInputInfo(domEl);
            var formatValue = this.formatValueByInsChar(key, info.caretPosition, info.valueBeforeCaret, info.valueSelected,
                info.valueAfterCaret);
            if (!formatValue) {
                return;
            }
            domEl.value = formatValue.value;
            Terrasoft.utils.dom.setCaretPosition(domEl, formatValue.pos);
            if (!this.validationInfo.isValid && formatValue.isComplete) {
                var validationInfo = {
                    isValid: true,
                    invalidMessage: ""
                };
                this.setValidationInfo(validationInfo);
            }
        },
        getInputInfo: function(domEl) {
            var selectedTextLength = Terrasoft.utils.dom.getSelectedTextLength(domEl);
            var caretPosition = Terrasoft.utils.dom.getCaretPosition(domEl);
            var value = domEl.value;
            var valueBeforeCaret = "";
            var valueAfterCaret = "";
            var valueSelected = "";
            if (Ext.isIE) {
                valueBeforeCaret = value.slice(0, caretPosition - selectedTextLength);
                valueAfterCaret = value.slice(caretPosition);
                caretPosition -= selectedTextLength;
            } else {
                valueBeforeCaret = value.slice(0, caretPosition);
                valueAfterCaret = value.slice(caretPosition + selectedTextLength);
            }
            if (selectedTextLength > 0) {
                valueSelected = value.slice(caretPosition, selectedTextLength + caretPosition);
            }
            return {
                selectedTextLength: selectedTextLength,
                caretPosition: caretPosition,
                value: value,
                valueBeforeCaret: valueBeforeCaret,
                valueAfterCaret: valueAfterCaret,
                valueSelected: valueSelected
            };
        },
        /**
         * @protected
         */
        onKeyDown: function(e) {
            this.callParent(arguments);
            if (Ext.isEmpty(this.masks)) {
                return;
            }
            var key = e.getKey();
            var matches, masks, placeHolders, commonStr;
            if (key === e.BACKSPACE || key === e.DELETE) {
                e.preventDefault();
                var domEl = e.getTarget();
                var info = this.getInputInfo(domEl);
 
                if (info.valueBeforeCaret === "" && info.valueAfterCaret === "") {
                    this.setDomValue("");
                }
 
                else if (info.selectedTextLength > 0) {
 
                    if (Ext.isEmpty(info.valueAfterCaret)) {
                        this.setDomValue(this.removeEndPlaceHolders(info.valueBeforeCaret));
                    } else {
 
                        matches = this.getMatchesByValue(info.valueBeforeCaret);
                        if (matches.matches.length === 0) {
                            return;
                        }
 
                        masks = this.getPropertyValuesFromArray(matches.matches, "mask");
 
                        placeHolders = this.getPropertyValuesFromArray(masks, "placeHolder");
                        var replaceText = this.getCommonStartString(placeHolders).substr(info.caretPosition, info.selectedTextLength);
                        if (replaceText.length === info.selectedTextLength) {
                            this.setDomValue(info.valueBeforeCaret + replaceText + info.valueAfterCaret);
                        }
                    }
                } else if (key === e.BACKSPACE && !Ext.isEmpty(info.valueBeforeCaret)) {
                    matches = this.getMatchesByValue(info.valueBeforeCaret);
                    if (matches.matches.length === 0) {
                        return;
                    }
 
                    masks = this.getPropertyValuesFromArray(matches.matches, "mask");
 
                    placeHolders = this.getPropertyValuesFromArray(masks, "placeHolder");
                    commonStr = this.getCommonStartString(placeHolders);
                    if (commonStr.length >= info.caretPosition) {
                        this.setDomValueAndCaret(info.valueBeforeCaret.slice(0, -1) + commonStr.substr(info.caretPosition - 1, 1) +
                            info.valueAfterCaret, info.caretPosition - 1);
                    }
                } else if (key === e.DELETE && !Ext.isEmpty(info.valueAfterCaret)) {
 
                    matches = this.getMatchesByValue(info.valueBeforeCaret);
                    if (matches.matches.length === 0) {
                        return;
                    }
 
                    masks = this.getPropertyValuesFromArray(matches.matches, "mask");
 
                    placeHolders = this.getPropertyValuesFromArray(masks, "placeHolder");
                    commonStr = this.getCommonStartString(placeHolders);
                    if (commonStr.length > info.caretPosition) {
                        this.setDomValueAndCaret(info.valueBeforeCaret + commonStr.substr(info.caretPosition, 1) +
                            info.valueAfterCaret.slice(1), info.caretPosition + 1);
                    }
                }
            }
        },
        maskConfig: {
            definitions: {
                //numbers
                "9": {
                    re: "[0-9]"
                },
                //cyryllic
                "к": {
                    re: "[а-яА-ЯёЁ]"
                },
                //латинские
                "l": {
                    re: "[a-zA-Z]"
                },
                //any letter
                "c": {
                    re: "[а-яА-ЯёЁa-zA-Z]"
                },
                //any letter or number
                "#": {
                    re: "[а-яА-ЯёЁA-Za-z0-9]"
                }
            },
            placeHolderChar: "_"
        },
        mask: [],
        /**
         * @protected
         * @overridden
         */
        init: function() {
            this.callParent(arguments);
            this.reSpecChars = [
                "\\", "(", ")", "+"
            ];
            this.addEvents(
                "paste");
            this.on("paste", this.onPaste, this);
            this.setMasks(this.mask);
        },
        /**
         * @protected
         * */
        setMasks: function(value) {
            this.masks = [];
            if (Ext.isEmpty(value)) {
                value = {
                    formats: []
                };
            }
            Terrasoft.each(value.formats, function(format, i) {
                this.masks[i] = this.getMaskByFormat(format);
            }, this);
            this.changeValue(this.value);
        },
        /**
         * {@link Terrasoft.Bindable}.
         * @overridden
         */
        getBindConfig: function() {
            var bindConfig = this.callParent(arguments);
            var multiMaskEditBindConfig = {
                mask: {
                    changeMethod: "setMasks"
                }
            };
            Ext.apply(bindConfig, multiMaskEditBindConfig);
            return bindConfig;
        },
        getMaskByFormat: function(format) {
            var mask = {};
            var matches = [];
            var placeHolderChar;
            var placeHolder = "";
            var def;
            var allRe = "";
 
            if (format) {
                Terrasoft.each(format.split(""), function(c) {
                    def = this.maskConfig.definitions[c];
                    if (def) {
                        allRe += def.re;
                        placeHolderChar = def.placeHolderChar || this.maskConfig.placeHolderChar || "_";
                        matches.push({
                            re: new RegExp(def.re),
                            placeHolderChar: placeHolderChar
                        });
 
                        placeHolder += placeHolderChar;
                    } else {
                        placeHolder += c;
                        matches.push(c);
                        if (this.reSpecChars.indexOf(c) > 0) {
                            allRe += "\\" + c;
                        } else {
                            allRe += c;
                        }
                    }
                }, this);
                mask.placeHolder = placeHolder;
                mask.matches = matches;
                if (allRe !== "") {
                    mask.re = {};
                    mask.re.full = new RegExp("^" + allRe + "$");
                }
            }
            return mask;
        },
        /**
         * @param value
         */
        removeEndPlaceHolders: function(value) {
            if (!Ext.isEmpty(value)) {
                var pos;
 
                for (var i = (value.length - 1); i >= 0; i--) {
 
                    if (value[i] !== this.maskConfig.placeHolderChar) {
                        break;
                    }
                    pos = i;
                }
                if (pos === 0) {
                    value = "";
                } else if (pos) {
                    value = value.slice(0, pos);
                }
            }
            return value;
        },
        /**
         * @param mask
         * @param c
         * @param pos
         * @param textBefore
         * @param textReplaced
         * @param textAfter
         * @param allowAutoFill
         * @returns {*}
         */
        maskValidateByInsChar: function(mask, c, pos, textBefore, textReplaced, textAfter, allowAutoFill) {
            var replacedLength = textReplaced.length;
            if (replacedLength > 0) {
                textAfter = mask.placeHolder.slice(pos, pos + textReplaced.length) + textAfter;
            }
            var value = textBefore + textAfter;
            var maskValidate;
            var match;
            if (!Ext.isEmpty(textAfter)) {
                match = mask.matches[pos];
                if (match && !match.re && mask.matches[pos] === c) {
                    return {
                        value: value,
                        pos: pos,
                        result: this.maskValidateValue(mask, value)
                    };
                }
            }
            value = textBefore + c + textAfter;
            match = mask.matches[pos];
            if (match) {
                if (match.re) {
                    if (textAfter[0] === mask.placeHolder.substr(pos, 1)) {
                        value = textBefore + c + textAfter.substring(1);
                        maskValidate = this.maskValidateValue(mask, value);
                        if (maskValidate.isValid) {
                            return {
                                value: value,
                                pos: pos,
                                result: maskValidate
                            };
                        }
                    }
                }
            }
            maskValidate = this.maskValidateValue(mask, value);
            if (maskValidate.isValid) {
                return {
                    value: value,
                    pos: pos,
                    result: maskValidate
                };
            }
            if (match && !match.re && allowAutoFill && pos < mask.placeHolder.length) {
                var result = this.maskValidateByInsChar(mask, c, pos + 1, textBefore + match,
                    textReplaced, textAfter.substring(1), allowAutoFill);
                if (result) {
                    return result;
                }
                return this.maskValidateByInsChar(mask, c, pos + 1, textBefore + match,
                    textReplaced, textAfter.substring(2), allowAutoFill);
            }
            //}
        },
        /**
         * @param c
         * @param pos
         * @param textBefore
         * @param textAfter
         * @param allowAutoFill
         * @returns {*}
         */
        formatValueByInsChar: function(c, pos, textBefore, textReplaced, textAfter, allowAutoFill) {
            var maskValidation;
            var bestResults = [];
            Terrasoft.each(this.masks, function(item) {
                maskValidation = this.maskValidateByInsChar(item, c, pos, textBefore, textReplaced, textAfter, allowAutoFill);
                if (maskValidation) {
                    maskValidation.mask = item;
                    if (bestResults.length === 0) {
                        bestResults.push(maskValidation);
                    } else if (maskValidation.pos < bestResults[0].pos) {
                        bestResults = [maskValidation];
                    }
                }
            }, this);
            if (bestResults.length > 0) {
                maskValidation = bestResults[0];
 
                var formatValue = this.formatValue(maskValidation.value);
                formatValue.insPos = maskValidation.pos;
                return formatValue;
            }
        },
        /**
         * @param mask
         * @param value
         * @returns {*}
         */
        maskValidateValue: function(mask, value) {
            var match;
            var matchPos = 0;
            if (mask.re.full.test(value)) {
                return {
                    matchPos: value.length,
                    inputPos: value.length,
                    isValid: true,
                    isComplete: true
                };
            }
            var result = {
                matchPos: 0,
                inputPos: 0,
                isValid: true,
                isComplete: false
            };
            value = this.removeEndPlaceHolders(value);
            if (Ext.isEmpty(value)) {
                return result;
            } else {
                Terrasoft.each(value.split(""), function(c, i) {
                    match = mask.matches[i];
                    if (!match) {
                        result = {
                            isValid: false,
                            isComplete: false
                        };
                        return false;
                    } else if (match.re) {
                        if (!match.re.test(c)) {
                            if (match.placeHolderChar === c) {
                                if (!result.inputPos) {
                                    result.inputPos = i;
                                }
                            } else {
                                result = {
                                    isValid: false,
                                    isComplete: false
                                };
                                return false;
                            }
                        }
                    } else if (c !== match) {
                        result = {
                            isValid: false,
                            isComplete: false
                        };
                        return false;
                    }
                    matchPos = i;
                }, this);
                result.matchPos = matchPos;
                if (!result.inputPos) {
                    if (result.isValid) {
                        result.inputPos = result.matchPos + 1;
                    } else {
                        result.inputPos = value.length;
                    }
                }
            }
            return result;
        },
        /**
         * @param value
         * @returns {{matches: Array, matchPos: number, inputPos: number}}
         */
        getMatchesByValue: function(value) {
            var matches = [];
            var minPositions = [];
            var matchPos = 0;
            var inputPos = 0;
            var maskValidation;
            var isComplete = false;
            Terrasoft.each(this.masks, function(mask) {
                maskValidation = this.maskValidateValue(mask, value);
                if (maskValidation.isValid) {
                    var maskMinPos = mask.matches.length;
                    Terrasoft.each(mask.matches, function(match, pos) {
                        if (typeof match === "object" && maskMinPos > pos) {
                            maskMinPos = pos;
                        }
                    });
                    minPositions.push(maskMinPos);
                    if (maskValidation.isComplete) {
                        isComplete = true;
                    }
                    matches.push({
                        mask: mask,
                        validation: maskValidation
                    });
                    if (matchPos < maskValidation.matchPos) {
                        matchPos = maskValidation.matchPos;
                    }
                    if (inputPos < maskValidation.inputPos) {
                        inputPos = maskValidation.inputPos;
                    }
                }
            }, this);
            var minPos = Math.min.apply(0, minPositions);
            return {
                matches: matches,
                matchPos: matchPos,
                inputPos: inputPos,
                minPos: minPos,
                isComplete: isComplete
            };
        },
        /**
         * @param values
         * @returns {*}
         */
        getCommonStartString: function(values) {
            var valuesCount = values.length;
            if (valuesCount === 0) {
                return "";
            } else if (valuesCount === 1) {
                return values[0];
            }
            var commonStr = "";
            var minLen = values[0].length;
            Terrasoft.each(values, function(value) {
                minLen = Math.min(minLen, value.length);
            });
            var isMatch;
            var c;
            for (var i = 0; i < minLen; i++) {
                isMatch = true;
                for (var j = 1; j < valuesCount; j++) {
                    isMatch = values[j][i] === values[j - 1][i];
                    if (!isMatch) {
                        break;
                    }
                    c = values[j][i];
                }
                if (isMatch) {
                    commonStr += c;
                } else {
                    return commonStr;
                }
            }
            return commonStr;
        },
        /**
         * @param a
         * @param name
         * @returns {Array}
         */
        getPropertyValuesFromArray: function(a, name) {
            var result = [];
            Terrasoft.each(a, function(e) {
                result.push(e[name]);
            }, this);
            return result;
        },
        /**
         * @overridden
         * @protected
         */
        initDomEvents: function() {
            this.callParent(arguments);
            var el = this.getEl();
            el.on({
                "paste": {
                    fn: this.onPaste,
                    scope: this
                }
            });
        },
        onBeforePasteFormatValue: Ext.emptyFn,
        onPaste: function(e) {
            if (Ext.isEmpty(this.masks)) {
                return;
            }
            var getSplitText = function(v, p) {
                return {
                    pos: p,
                    before: v.substr(0, p),
                    after: v.substr(p)
                };
            };
            e.preventDefault();
            if (e.browserEvent.clipboardData && e.browserEvent.clipboardData.getData) {
                if (/text\/plain/.test(e.browserEvent.clipboardData.types)) {
                    var clipBoardValue = e.browserEvent.clipboardData.getData("text/plain");
                    clipBoardValue = this.onBeforePasteFormatValue(clipBoardValue) || clipBoardValue;
                    if (Ext.isEmpty(clipBoardValue)) {
                        return;
                    }
                    var domEl = e.getTarget();
                    var info = this.getInputInfo(domEl);
                    var pos = info.caretPosition;
                    var splitText = {
                        before: info.valueBeforeCaret,
                        after: info.valueAfterCaret
                    };
                    var newValue = splitText.before + splitText.after;
                    Terrasoft.each(clipBoardValue.split(""), function(c) {
                        var formatValue = this.formatValueByInsChar(c, pos, splitText.before, "", splitText.after, true);
                        if (formatValue) {
                            newValue = formatValue.value;
                            pos = formatValue.insPos + 1;
                            splitText = getSplitText(newValue, pos);
                        }
                    }, this);
                    domEl.value = newValue;
                }
            }
            return;
        },
        /**
         * @param value
         * @returns {*}
         */
        formatValue: function(value) {
            var newValue = value;
            var placeHolders;
            if (Ext.isEmpty(value)) {
                placeHolders = this.getPropertyValuesFromArray(this.masks, "placeHolder");
                newValue = this.getCommonStartString(placeHolders);
                if (!Ext.isEmpty(newValue)) {
                    return this.formatValue(newValue);
                }
                return {
                    pos: 0,
                    value: "",
                    isComplete: false
                };
            }
            var matches = this.getMatchesByValue(value);
            if (matches.matches.length === 0) {
                if (matches.matchPos > 0) {
                    newValue = value.substr(0, matches.matchPos + 1);
                } else {
 
                    return {
                        pos: 0,
                        value: value,
                        isComplete: false
                    };
                }
                return this.formatValue(newValue);
            }
            var masks = this.getPropertyValuesFromArray(matches.matches, "mask");
            placeHolders = this.getPropertyValuesFromArray(masks, "placeHolder");
            var afterText = this.getCommonStartString(placeHolders).substr(matches.matchPos + 1);
            newValue = value.substr(0, matches.matchPos + 1) + afterText;
            matches = this.getMatchesByValue(newValue);
            return {
                pos: matches.inputPos,
                min: matches.minPos,
                value: newValue,
                isComplete: matches.isComplete
            };
        },
        /**
         * @param value
         * @param caretPosition
         */
        setDomValueAndCaret: function(value, caretPosition) {
            var formatValue = this.formatValue(value);
            if (!this.validationInfo.isValid && formatValue.isComplete) {
                var validationInfo = {
                    isValid: true,
                    invalidMessage: ""
                };
                this.setValidationInfo(validationInfo);
            }
            var el = this.getEl();
            if (el) {
                el.dom.value = formatValue.value;
                caretPosition = caretPosition > formatValue.min ? caretPosition : formatValue.min;
                Terrasoft.utils.dom.setCaretPosition(el.dom, caretPosition);
            }
        },
        /**
         * @param {String} value
         * @protected
         * @virtual
         */
        setDomValue: function(value) {
            this.setDomValueAndCaret(value);
        },
        /**
         * @protected
         * @param  {String} value
         * @return {Boolean}
         */
        changeValue: function(value) {
            if (!Ext.isEmpty(this.masks)) {
                var formatValue = this.formatValue(value);
                if (formatValue.value === this.formatValue(null).value) {
                    value = null;
                    formatValue.isComplete = true;
                    var el = this.getEl();
                    if (el) {
                        el.dom.value = "";
                    }
                }
                var validationInfo = this.getValidationInfo(formatValue);
                this.setValidationInfo(validationInfo);
            }
            return this.callParent(arguments);
        }
    });
});

Используем этот модуль для колонки телефона:

define("ContactPageV2", ["MultiMaskEdit"], function() {
	return {
		entitySchemaName: "Contact",
		attributes: {},
		modules: /**SCHEMA_MODULES*/{}/**SCHEMA_MODULES*/,
		details: /**SCHEMA_DETAILS*/{}/**SCHEMA_DETAILS*/,
		businessRules: /**SCHEMA_BUSINESS_RULES*/{}/**SCHEMA_BUSINESS_RULES*/,
		methods: {},
		dataModels: /**SCHEMA_DATA_MODELS*/{}/**SCHEMA_DATA_MODELS*/,
		diff: /**SCHEMA_DIFF*/[
			{
                "operation": "merge",
                "name": "MobilePhone",
                "values": {
                    "controlConfig": {
                        "className": "Terrasoft.controls.MultiMaskEdit",
                        "mask": {
                            "formats": ["(99)999-99-99"]
                        }
                    }
                }
            }
		]/**SCHEMA_DIFF*/
	};
});

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

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

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

Нравится

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

Добрый день, Никита!

 

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

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



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

На самом деле можно достать идентификатор пользователя Телеграм. Таблица с идентификаторами как раз зарегистрирована как объект (ContactIdentity).



Но писать первым все ровно не получится, так как это политика конфиденциальности самого Телеграма: чтобы не было спама клиенту,  он должен первым написать боту, иначе отправка сообщения будет с ошибкой.




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

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