автоматическая нумерация
Технические вопросы
7.x

Добрый день!

Подскажите, пожалуйста, вариант решения для автоматической нумерации позиций в документе (нумерация записей в детали карточки).

Есть такой "Базовый объект с позицией", но поддержка сказала его не использовать в 7.х, и надо писать свою логику на клиенте. Может, кто сталкивался?

Спасибо!

Нравится

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

Владимир, добрый день!

Для решения задачи нужно создать колонку (int) (к примеру «Position») на объекте, после этого на клиентском модуле написать функцию подсчета записей и обновления счетчика позиции для записи.

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

Владимир, добрый день!

Для решения задачи нужно создать колонку (int) (к примеру «Position») на объекте, после этого на клиентском модуле написать функцию подсчета записей и обновления счетчика позиции для записи.

Возникает еще вопрос с удалением и пересчётом позиций. Это уже явно лучше делать на сервере

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

Владимир, Вы можете использовать метод в объекте «Базовый объект с позицией», данное решение будет функционировать без каких либо проблем.
С нашей стороны рекомендуем делать кастомизации на клиентском модуле так как данный подход больше соответствует нынешнему ходу разработки кастомизаций для приложения

Показать все комментарии
добавление действия
замещающий раздел
права доступа
Технические вопросы
7.x

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

1) Добавила новое действие.
Требуется реализовать переход на другую страницу при нажатии на данное действие.
Не могли бы вы подсказать как это сделать?

2) Не могу найти какая страница отвечает стандартному действию "Права доступа"

Спасибо большое!

Нравится

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

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

1) Правильно ли я понимаю, что Вы используете версию 5.X?
Примеры есть в темах:
http://www.community.terrasoft.ru/forum/topic/9471
http://www.community.terrasoft.ru/forum/topic/8691
2) Деталь "Права доступа" в разделе (если речь идет о 5.X) отображается в том случае, если объект раздела администрируется по записям.

1) Спасибо большое, Андрей!!
2) Нет, деталь я вижу , я не могу найти его исходный код.

Сабина, посмотрите BaseObjectRecordRightsGridPage.

Другой вопрос, для чего Вам это нужно:)

Мы не рекомендуем вносить изменения в данную деталь, т.к. это может повлиять на работу системы.

Спасибо!

Хочу создать аналогичную страницу для редактирования прав доступа для выделенных активностей.

"Безродный Андрей" написал:

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

1) Правильно ли я понимаю, что Вы используете версию 5.X?

Примеры есть в темах:

http://www.community.terrasoft.ru/forum/topic/9471

http://www.community.terrasoft.ru/forum/topic/8691

2) Деталь "Права доступа" в разделе (если речь идет о 5.X) отображается в том случае, если объект раздела администрируется по записям.


Я очень извиняюсь, здесь как я понимаю редактируется карточка. При редактировании раздела надо поступать аналогично? Просто при изменении раздела открывается окно для написания javascript кода, а для карточки c#.

Спасибо.

1. Как сделать по кнопке переход в другой раздел — описано здесь.
2. В линейке 5.Х код всех окнон: карточек, реестров, разделов написан на C#. На JS есть только отдельные вкрапления внутри C#. Пожалуйста, покажите скриншот, чтобы понять, что Вы хотите изменить.

"Смена прав" в разделе "Мои активности". (1я картинка)
Исходный код на 2й картинке.

Мне надо чтобы при нажатии открывалась некая страница .

Сабина, для добавления действия в раздел достаточно переопределить метод getSectionActions. Например, как это сделано в разделе «Контакты» (ContactSectionV2).

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

Сабина, для добавления действия в раздел достаточно переопределить метод getSectionActions. Например, как это сделано в разделе «Контакты» (ContactSectionV2).


Спасибо, но вопрос ведь не в добавлении, а в реализации перехода на другую страницу.
Или там что-то подобное реализовано?

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

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

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


this.methods.onSchedulerView = function() {
var tag = prepareEdit(this);
var recordId = this.get('activeRow');
this.view(tag, recordId);

это оно?

Можно создать для одного раздела две разные страницы вида?(одна уже реализована в базовом)

"Исаева Сабина" написал:
Вильшанский Дмитрий пишет:

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

this.methods.onSchedulerView = function() {

var tag = prepareEdit(this);

var recordId = this.get('activeRow');

this.view(tag, recordId);

это оно?

Можно создать для одного раздела две разные страницы вида?(одна уже реализована в базовом)

Вам нужен метод getSectionActions.
Конечно можно, у раздела может быть несколько страниц редактирования.
Посмотрите базовый функционал в разделе «Активности». Например, в разделе есть страницы с типом «Задача», «E-mail».

"Исаева Сабина" написал:
Вильшанский Дмитрий пишет:

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

this.methods.onSchedulerView = function() {

var tag = prepareEdit(this);

var recordId = this.get('activeRow');

this.view(tag, recordId);

это оно?

Можно создать для одного раздела две разные страницы вида?(одна уже реализована в базовом)

Вам нужен метод getSectionActions.
Конечно можно, у раздела может быть несколько страниц редактирования.
Посмотрите базовый функционал в разделе «Активности». Например, в разделе есть страницы с типом «Задача», «E-mail».

"Александр Зубков" написал:Задача

this.methods.onSchedulerCopy = function() {
var tag = prepareEdit(this);
var recordId = this.get('activeRow');
this.copy(tag, recordId);
};
this.methods.onSchedulerDelete = function() {
prepareEdit(this);
this.onDelete();
};

вот они они как я понимаю.
А как они связываются?
т.е как система понимает что вызывать при данной операции? (в каком месте эта привязка осуществляется? )

Сабина, добрый день!

Конкретно эти методы связываются с соответствующими кнопками в функции generateEditButtons схемы ActivitySection (обработчик события click):

buttonsConfig.push({
	className: 'Terrasoft.Button',
	enabled: {
		bindTo: 'getEditButtonsEnabled'
	},
	click: {
		bindTo: 'onSchedulerCopy'
	},
	styles: {
		textStyle: {
			margin: '0px 10px 0px 0px'
		}
	},
	style: Terrasoft.controls.ButtonEnums.style.DEFAULT,
	caption: resources.localizableStrings.CopyButtonCaption,
	tag: ConfigurationEnums.CardState.Copy
});

"Лабьяк Олег Игоревич" написал:

Сабина, добрый день!

Конкретно эти методы связываются с соответствующими кнопками в функции generateEditButtons схемы ActivitySection (обработчик события click):

buttonsConfig.push({

        className: 'Terrasoft.Button',

        enabled: {

                bindTo: 'getEditButtonsEnabled'

        },

        click: {

                bindTo: 'onSchedulerCopy'

        },

        styles: {

                textStyle: {

                        margin: '0px 10px 0px 0px'

                }

        },

        style: Terrasoft.controls.ButtonEnums.style.DEFAULT,

        caption: resources.localizableStrings.CopyButtonCaption,

        tag: ConfigurationEnums.CardState.Copy

});

С уважением,

Олег Лабьяк,

инженер-программист,

группа компаний Terrasoft.

Добрый день, Олег!
Нет , вопрос в последующем действии, как программа понимает какую страницу открывать при вызове this.copy?

Страница определяется в результате вызова функции prepareEdit(this). Внутри этой функции происходит поиск карточки редактирования, соответствующей текущему модулю. Если страница найдена, она сохраняется в переменную tag, которая используется в вызове this.copy.

this.methods.onSchedulerView = function() {
var tag = prepareEdit(this);
var recordId = this.get('activeRow');
this.view(tag, recordId);
};
this.methods.onSchedulerEdit = function() {
var tag = prepareEdit(this);
var recordId = this.get('activeRow');
this.edit(tag, recordId);
};
this.methods.onSchedulerCopy = function() {
var tag = prepareEdit(this);
var recordId = this.get('activeRow');
this.copy(tag, recordId);
};

this.methods.onSchedulerDelete = function() {
prepareEdit(this);
this.onDelete();
};

Что бы ни передавали в эти edit, copy view , для каждого открываются одинаковые страницы. Может они как то по умолчанию прикреплены?

Сабина, посмотрите, пожалуйста, реализацию функции prepareEdit. В ней происходит поиск карточек редактирования, соответствующих текущему разделу. Сами карточки регистрируются в системной таблице SysModuleEdit, но при старте системы информация о них сохраняется в отдельную коллекцию для каждого раздела. Сами разделы сохраняются в Terrasoft.configuration.ModuleStructure, а получить карточки редактирования, соответствующие модулю, можно следующим образом (пример для раздела "Активности"):

var pages = Terrasoft.configuration.ModuleStructure[Activity.name].pages;

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

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

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

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

Нравится

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

Дмитрий, данная функциональность есть только в продуктах service http://www.terrasoft.ru/service/
Для других продуктов работает базовая логика приложения один пользователь = один электронный ящик.

В маркетинг хотя бы её надо встроить. info@ есть у всех компаний

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

Дмитрий, данная функциональность есть только в продуктах service http://www.terrasoft.ru/service/

Для других продуктов работает базовая логика приложения один пользователь = один электронный ящик.

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

Дмитрий, добрый день!

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

1. Пользователи синхронизируют свою почту (1 пользователь=1 почта).
2. Создается еще один пользователь (общий корпоративный юзер=1 общая корпоративная почта).
3. Внутри самого почтового ящика корпоративной почты настраивается переадресация входящих писем на нужные ящики других пользователей.

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

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

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

Возникла задача:

Необходимо установить признак обязательности поля UsrDeviationReason, если даты UsrDatePlan и UsrDateActual не равны друг другу и UsrDateActual заполнена:

Написали правило:

"UsrDeviationReason":{
        "BindParameterRequiredDeviationReason": {
        "ruleType": BusinessRuleModule.enums.RuleType.BINDPARAMETER,
        "property": BusinessRuleModule.enums.Property.REQUIRED,
        "logical": Terrasoft.LogicalOperatorType.AND,
        "conditions": [
                {
                        "leftExpression": {
                                "type": BusinessRuleModule.enums.ValueType.ATTRIBUTE,
                                "attribute": "UsrDatePlan"
                        },
                        "comparisonType": Terrasoft.ComparisonType.NOT_EQUAL,
                        "rightExpression": {
                                "type": BusinessRuleModule.enums.ValueType.ATTRIBUTE,
                                "attribute": "UsrDateActual"
                        }
                },
                {
                        "leftExpression": {
                                "type": BusinessRuleModule.enums.ValueType.ATTRIBUTE,
                                "attribute": "UsrDateActual"
                        },
                        "comparisonType": Terrasoft.ComparisonType.IS_NOT_NULL
                }
        ]
}

В итоге два вопроса:
1) По UsrDateActual IS_NOT_NULL работает (т.е., если поле UsrDateActual не заполнено, то признак обязательности не появляется).
Но при равенстве UsrDatePlan и UsrDateActual признак обязательности всё равно остаётся. Что указано не так?
2) Даже если признак обязательности исчезает, всё равно остаётся подсказка заполнить это поле и никак не исчезает.

Спасибо за помощь!

Нравится

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

Может, конечно, я тупость скажу, но условие "не равно" вроде бы без нижнего подчеркивания, просто NotEqual.
Информацию брал отсюда https://www.bpmonline.com/bpmonlinesdken/Terrasoft.Core~Terrasoft.Core…

Хотя и IsNotNull там тоже без нижних подчеркиваний написано, но у вас работает.

Владимир, добрый день!

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

"Демьяник Алексей Олегович" написал:Проверьте, чтобы даты были абсолютно идентичными

Формат полей date, а не datetime, и в них нет времени.

"Сазанов Александр Владимирович" написал:Хотя и IsNotNull там тоже без нижних подчеркиваний написано, но у вас работает.

В логике карточек вижу, что с подчеркиваниями. и NOT_EQUAL тоже видел. А в процессах, кажется, без подчеркивания.

Владимир, добрый день!

Типы сравнений EQUAL и NOT_EQUAL не совсем корректно работают в случае сравнения дат, поскольку в JavaScript даты являются объектами, а два объекта никогда не будут равными друг другу, если это не один и тот же объект.

Рекомендую для изменения обязательности поля использовать не бизнес-правило, а создать отдельное свойство модели, значение которого связать с атрибутом isRequired нужного поля в блоке diff:

{
	"operation": "insert",
	"parentName": "Header",
	"propertyName": "items",
	"name": "UsrDeviationReason",
	"values": {
		"layout": {"column": 12, "row": 0},
		"isRequired": {"bindTo": "IsUsrDeviationReasonRequired"}
	}
}

Инициализировать это свойство можно при инициализации страницы, а также при изменении полей плановой и фактической даты.

Поделюсь результатом:

IsUsrDeviationReasonRequired: function(columnName) {
	var endDate = this.get("EndDate");
	var deadline = this.get("Deadline");
	var isRequired = false;
	if ((endDate) && (deadline)) {
		isRequired = (endDate.toDateString() !== deadline.toDateString());
	}
	var column = this.columns[columnName];
	if (column) {
		column.isRequired = isRequired;
		this.validateColumn(columnName);
	}
	return isRequired;
}
Показать все комментарии
7.x
мастер
страница
тип

Добрый день!

Очень не удобно накидывать заново все "базовые" данные при настройке страниц Мастера (в зависимости от Типа записи). Почему нельзя было (не только колонку "Название", а ) сразу набор "базовых" данных закинуть на страницу нового Типа?

Нравится

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

И еще неудобнее copy-paste логику, программируя фильтрацию и прочие вещи для каждого типа

Добрый вечер!

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

"Демьяник Алексей Олегович" написал:и было передано в отдел разработки.

Сказано красиво, но не ясно будет ли какой-то результат?

Мы делаем так:
1) редактируем базовую страницу в Мастере (получаем replacing page).
2) создаем пустые страницы с типами
3) меняем у созданных страниц родительский объект на страницу объекта
4) общие вещи меняем у страницы, созданной в 1-м пункте
5) уникальные вещи меняем (а вся функциональность уже есть) у страниц, созданных во 2-3 пунктах.

Пока работает, но немного стрёмно :)

"Владимир Соколов" написал:

Мы делаем так:

1) редактируем базовую страницу в Мастере (получаем replacing page).

2) создаем пустые страницы с типами

3) меняем у созданных страниц родительский объект на страницу объекта

4) общие вещи меняем у страницы, созданной в 1-м пункте

5) уникальные вещи меняем (а вся функциональность уже есть) у страниц, созданных во 2-3 пунктах.

Пока работает, но немного стрёмно :)

Один из возможных вариантов решения.

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

Добрый день.
Переношу пакет с доработками 7.4 на 7.5 средствами SVN и MD файлами.
В процессе переноса выявлена, что для замещающих клиентских модулей ActPageV2,
CorrespondencePageV2 отсутствует родитель.
Как быть в этой ситуации? Как переносить доработки с версии на версию, если от версии к версии изменяются схемы ядра?

Нравится

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

Здравствуйте, Игорь.

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

В утилите и пакетах обновления на версию 7.5 предусмотрен учет данного изменения.
Прошу детализировать проблему и описать шаги, которые выполнялись Вами в письме на адрес support@tscrm.com.

Постараемся Вам помочь.
Заранее спасибо.

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

Игорь, здравствуйте!

Вариантом решения Вашего вопроса будет выгрузить пакеты с доработками через WorkspaceConsole и накатить их на другую базу. Для этого:

1) Сначала важно указать параметры подключения к БД, которые будет использовать WsC. WsC использует строку подключения из конфигурационного файла Terrasoft.Tools.WorkspaceConsole.exe.config (должен находиться в каталоге с WsC).
В секции указывается используемая строка подключения
В секции указываются значения строк подключения к БД.
...............................................................

2) Выполнить 1, 3 и 4 пункты из прикреплённой инструкции.
3) Сделать выгрузку пакетов из первой базы с помощью WorkspaceConsole, запустив файл update.bat со следующими параметрами:
Terrasoft.Tools.WorkspaceConsole.exe -operation=SaveDBContent -workspaceName=Default -destinationPath=D:\Temp\Repository\ -contentTypes=Repository
4) Выполняем первый пункт, указав теперь путь к базе, на которую накатываются пакеты.
5) Выполняем полностью прикрепленную инструкцию. Накатятся пакеты с доработками на другую базу.

Инструкцию по накатке пакетов прикрепляю.

Спасибо!

Не будет ли конфликта при обновлении, т.к. необходимый пакет будет выгружен со всеми наследуемыми им пакетами, а эти пакеты в целевой BPM 7.5 уже есть?

Игорь, добрый день!

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

Спасибо!

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

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

Нравится

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

Олег, добрый день!

Есть два варианта: добавить бизнес-правило с типом FILTRATION или добавить фильтр в свойство lookupListConfig соответствующего атрибута.

Пример первого варианта - схема ActivityPageV2, пакет Opportunity, фильтрация значений поля "Продажа" в зависимости от поля "Контрагент":

rules: {
	"Opportunity": {
		"FiltrationOpportunityByAccount": {
			"ruleType": BusinessRuleModule.enums.RuleType.FILTRATION,
			"autocomplete": true,
			"autoClean": true,
			"baseAttributePatch": "Account",
			"comparisonType": Terrasoft.ComparisonType.EQUAL,
			"type": BusinessRuleModule.enums.ValueType.ATTRIBUTE,
			"attribute": "Account"
		}
	}
}

Пример второго варианта - схема ActivityPageV2 пакета UIv2, фильтрация значений поля "Результат" в зависимости от категории активности:

attributes: {
	"Result": {
		lookupListConfig: {
			filters: [
				function() {
					var type = this.get("ActivityCategory");
					var filterGroup = Ext.create("Terrasoft.FilterGroup");
					filterGroup.add("ActivityCategory",
						Terrasoft.createColumnFilterWithParameter(
							Terrasoft.ComparisonType.EQUAL,
							"[ActivityCategoryResultEntry:ActivityResult].ActivityCategory",
							type.value));
					filterGroup.add("BusinessProcessOnly",
						Terrasoft.createColumnFilterWithParameter(
							Terrasoft.ComparisonType.EQUAL,
							"BusinessProcessOnly",
							0));
					return filterGroup;
				}
			]
		}
	}
}

С помощью второго варианта можно реализовать фильтрацию любой сложности.

Спасибо, использовал второй вариант

Показать все комментарии
Контакт текущего пользователя
Технические вопросы
7.x

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

Нравится

4 комментария
Guid currentUserContactId = UserConnection.CurrentUser.ContactId;

спасибо

а где указать, что такое UserConnection?
При сохранении скрипта - ошибка UserConnection is not defined

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

Здраствуйте! подскажите пожалуйста как можно вручную освободить ресурсы объекта CustomQuery. Я не могу вызвать мотод Dispose() так как этот класс не реализует интерфейс IDisposable.

Нравится

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

Добрый день!

            public static bool IsSchemaExistsInDB(UserConnection userConnection, EntitySchema entitySchema) {
                    string sqlText = userConnection.DBEngine.GetIsEntitySchemaExistSqlText();
                    var checkSchemaExistCustomQuery = new CustomQuery(userConnection, sqlText)
                           .WithParameter("EntitySchemaName", entitySchema.Name);
                    if (sqlText.Contains("@DBSchemaName") || sqlText.Contains(":DBSchemaName")) {
                           checkSchemaExistCustomQuery.WithParameter("DBSchemaName", userConnection.DBEngine.CurrentSchemaName);
                    }
                    using (DBExecutor dbExecutor = userConnection.EnsureDBConnection()) {
                           using (IDataReader dataReader = checkSchemaExistCustomQuery.ExecuteReader(dbExecutor)) {   // (!)
                                  return dataReader.Read();
                           }
                    }
             }

CustomQuery не использует Disponse, так как он не хранит данные.

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

А если делать insert в базу, using тогда не используется. Возможно ли в этом случае вручную освободить ресурсы после команды Execute() ?

Владимир, вышлите пример кода с insert. Стало не совсем понятно, какие ресурсы вы хотите освободить.

Для примера

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

Вся магия происходит в методе Execute. Внутри этого метода в using-е создастся DBExecuter, который выполнит ваш SQL и автоматически освободит подключение к БД.

Понятно. Спасибо!

Показать все комментарии
JSON
десериализация
преобразование в ассоциативный массив
Технические вопросы
7.x

Добрый день!

Не подскажет ли кто-нибудь ответ на такой вопрос: можно ли "перехватить" json до его скармливания системе на десериализацию с тем, чтобы преобразовать его в массив и исключить ряд элементов?

суть проблемы:
сайт отдает json с информацией о заказе услуги. В этом json есть довольно много данных, которые не нужны в CRM, но они нужны в случае, если через CRM будет производиться редактирование заказа (в этой ситуации срмка должна будет вернуть сайту тот же самый json с частью обновленных данных).

Отсюда и возник вопрос - можно ли, до десериализации, "ненужные" данные вычленить из json (сохранив оригинал отдельно) и скормить системе только то, что нужно для работы?

Кроме преобразования в массив я ничего не могу придумать, а bpm не умеет работать с json как с текстом (только как с объектом... или нет?)

Спасибо и сорри, если немного невнятно :)

Нравится

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