Добрый день, Коллеги!
В Sales в разделе Заказы есть подбор продуктов. Стандартная логика позволяет в карточке контрагента в поле прайс-лист выбрать прайс-лист и тогда, при подборе товаров в заказ будет видна цена согласно этого прайс-листа. Система как-то пробрасывает его туда. Я бы хотел подменить эту логику на передачу туда прайс-листа из карточки самого заказа (я поле создал справочное). Где искать? Какой метод подменить или использовать?

Нравится

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

Прайс-лист из таблицы контрагента используется в С#-схеме AccountPriceListPicker (там обычный Select), её вызывает тоже С#-схема веб-сервиса PriceListService:

/// <summary>
/// Get Price List using account. Took from account, if there is no Price List,
/// then took it from partnership
/// </summary>
/// <param name="accountId">Account identifier.</param>
/// <returns>PriceList identifier</returns>
[OperationContract]
[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped,
	ResponseFormat = WebMessageFormat.Json)]
public Guid GetPriceList(Guid accountId) {
	var priceListPicker = ClassFactory.Get<IPriceListPicker>(new ConstructorArgument("userConnection",
UserConnection));
	var	preSetPriceList = priceListPicker.GetPriceList(accountId);
	return preSetPriceList != default(Guid)
? preSetPriceList
: priceListPicker.GetPriceList(UserConnection.CurrentUser.AccountId);
}

А уже к нему обращаются из JS в странице заказа BaseOrderPage пакета Order:

/**
 * Sets predefined price list.
 * @protected
 * @virtual
 */
initializePredefinedPriceList: function() {
	if (this.isPredefinedPriceListsEnabled()) {
		this.$PredefinedPriceList = this.$Account && this.$Account.PriceList;
		if (this.isEmpty(this.$PredefinedPriceList)) {
			const config = this.getPriceListServiceConfig();
			this.callService(config, this.onPredefinedPriceListInitialized, this);
		}
	}
},
...
/**
 * Sets predefined price list.
 * @protected
 * @virtual
 */
initializePredefinedPriceList: function() {
	if (this.isPredefinedPriceListsEnabled()) {
		this.$PredefinedPriceList = this.$Account && this.$Account.PriceList;
		if (this.isEmpty(this.$PredefinedPriceList)) {
			const config = this.getPriceListServiceConfig();
			this.callService(config, this.onPredefinedPriceListInitialized, this);
		}
	}
},

 

 

Прайс-лист из таблицы контрагента используется в С#-схеме AccountPriceListPicker (там обычный Select), её вызывает тоже С#-схема веб-сервиса PriceListService:

/// <summary>
/// Get Price List using account. Took from account, if there is no Price List,
/// then took it from partnership
/// </summary>
/// <param name="accountId">Account identifier.</param>
/// <returns>PriceList identifier</returns>
[OperationContract]
[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped,
	ResponseFormat = WebMessageFormat.Json)]
public Guid GetPriceList(Guid accountId) {
	var priceListPicker = ClassFactory.Get<IPriceListPicker>(new ConstructorArgument("userConnection",
UserConnection));
	var	preSetPriceList = priceListPicker.GetPriceList(accountId);
	return preSetPriceList != default(Guid)
? preSetPriceList
: priceListPicker.GetPriceList(UserConnection.CurrentUser.AccountId);
}

А уже к нему обращаются из JS в странице заказа BaseOrderPage пакета Order:

/**
 * Sets predefined price list.
 * @protected
 * @virtual
 */
initializePredefinedPriceList: function() {
	if (this.isPredefinedPriceListsEnabled()) {
		this.$PredefinedPriceList = this.$Account && this.$Account.PriceList;
		if (this.isEmpty(this.$PredefinedPriceList)) {
			const config = this.getPriceListServiceConfig();
			this.callService(config, this.onPredefinedPriceListInitialized, this);
		}
	}
},
...
/**
 * Sets predefined price list.
 * @protected
 * @virtual
 */
initializePredefinedPriceList: function() {
	if (this.isPredefinedPriceListsEnabled()) {
		this.$PredefinedPriceList = this.$Account && this.$Account.PriceList;
		if (this.isEmpty(this.$PredefinedPriceList)) {
			const config = this.getPriceListServiceConfig();
			this.callService(config, this.onPredefinedPriceListInitialized, this);
		}
	}
},

 

 

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

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

Но в версии 7,17,0 - не могу определить где находится кнопка Настройки. 

Пример на скрине

Нравится

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

На версии 7.17.0 можно попробовать пройти по адресу yoursite/0/ViewPage.aspx?Id=c2af7f54-07df-4670-9c2b-af2497d3231f

 

На версии 7.17.1 прежде чем эту ссылку использовать надо реанимировать старый конфигуратор

 

Спасибо! Вот только клиенту это уже не объяснишь....

А клиент в старой конфигурации умел это делать сам? в этом случае дайте ему ссылку, а дальше все тоже самое осталось.

Екатерина, что Вы имеете в виду под шаблоном для импорта объектов из Конфигурации? Речь о выгрузке схем или данных в Excel? Приведите ссылку или скриншот, как раньше было.

вот тут ссылка на окошко старого интерфейса /0/ViewPage.aspx?Id=c2af7f54-07df-4670-9c2b-af2497d3231f

добраться из старой конфигурации можно так:

 

Было бы не плохо вставить эту кнопку "Скачать шаблон"  в новый мастер импорта, который по адресу /0/Nui/ViewModule.aspx?vm=FileImportWizard#FileImportModule/FileImportStartPage/f0a9cd02-5a19-4a4b-9e6e-7505bd509247

Спасибо, теперь понял, о чём Вы. В новой конфигурации нет кнопки «настройки», поскольку она была не частью старой конфигурации, а частью старого 5.Х-интерфейса вообще, была видима в любом разделе. А в 7.17 старый интерфейс окончательно убрали, а с ней и кнопку настройки, и окно старого импорта, и кнопку скачивания в нём. В новом интерфейсе свой экран настроек, «Дизайнер системы».

В академию по поводу неактуального абзаца сообщил.

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

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

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

Зверев Александр,

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

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

Зверев Александр,

Не, идея не в этом. Шаблон выгружается согласно доработанной объектное модели.(он не статичен) Т.о. добавляем в объект все нужные поля и справочники, выгружаем шаблон и отдаем для наполнения. А так как у всех модели данных разные, то и выгрузка шаблонов - регулярна.

В таком случае, можно опять выгрузить по действию «Экспорт в Excel».

Зверев Александр,

Да, но для этого нужно опять добавлять колонки в реестр.

А зачем Вы их удаляли?

С выгрузкой по действию «Экспорт в Excel» тоже есть свои нюансы. Так например списочное представление позволяет вывести только 24 колонки. Можно конечно в плиточном вывести все необходимые колонки. Тогда Экспорт в Excel выгрузит все что надо. Но Выгрузка шаблона позволяла выгрузить сразу всю объектную модель данных без настройки реестра записей, да еще и с указанием обязательности полей

Зверев Александр,

а зачем мне в реестре весь список полей из объекта? Алексей в сообщении выше очень точно описал суть проблемы)

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

Или самостоятельно сделать БП с логикой, взятой из DownloadTemplateMenuItemClickScriptTaskExecute схемы ImportSettingsPage и адаптированной под сохранение файла где-то в базе.

Или написать веб-сервис, куда параметром передают название схемы, а возвращают файл.

Нынешняя логика этой кнопки такая:

		public virtual bool DownloadTemplateMenuItemClickScriptTaskExecute(ProcessExecutingContext context) {
			Page.BaseMessagePanel.Clear();
			if (string.IsNullOrEmpty(SchemaName)) {
				Page.BaseMessagePanel.AddMessage(WarrningMessage, UnableSelectObjectMessage, MessageType.Warning, true);
				return true;
			}
			Page.Response.ClearHeaders();
			Page.Response.ClearContent();
			string fileName = SchemaName;
			var instance = UserConnection.EntitySchemaManager.GetInstanceByName(SchemaName);
			if (instance != null) {
				fileName = instance.Caption.ToString();
			}
			Page.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
			if (HttpContext.Current.Request.Browser.Browser == "IE") {
				string fileNameEnc = HttpUtility.UrlPathEncode(fileName);
				Page.Response.AddHeader("Content-Disposition", "attachment; filename=\"" + fileNameEnc + ".xlsx\"");
			} else {
				Page.Response.AddHeader("Content-Disposition", "attachment; filename=" + fileName + ".xlsx");
			}
			List<double> columnWidthsList = new List<double>();
			using (MemoryStream stream = new MemoryStream()) {
				using(SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Create(stream, SpreadsheetDocumentType.Workbook)) {
					// Add a WorkbookPart to the document.
					var workbookpart = spreadsheetDocument.AddWorkbookPart();
					workbookpart.Workbook = new OpenXmlSpreadsheet.Workbook();
 
					// Add a WorksheetPart to the WorkbookPart.
					var worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
					var workbookStylesPart = workbookpart.AddNewPart<WorkbookStylesPart>(); 
					worksheetPart.Worksheet = new OpenXmlSpreadsheet.Worksheet(new OpenXmlSpreadsheet.SheetData());
 
					// Add Sheets to the Workbook.
					OpenXmlSpreadsheet.Sheets sheets = spreadsheetDocument.WorkbookPart.Workbook.AppendChild<OpenXmlSpreadsheet.Sheets>(new OpenXmlSpreadsheet.Sheets());
 
					// Append a new worksheet and associate it with the workbook.
					string relationshipId = spreadsheetDocument.WorkbookPart.GetIdOfPart(worksheetPart);
					string sheetName = fileName;
					OpenXmlSpreadsheet.Sheet sheet = new OpenXmlSpreadsheet.Sheet() {
						Id = relationshipId,
						SheetId = 1, 
						Name = sheetName
					};
					sheets.Append(sheet);
 
					OpenXmlSpreadsheet.Worksheet worksheet = new OpenXmlSpreadsheet.Worksheet();
					OpenXmlSpreadsheet.SheetData sheetData = new OpenXmlSpreadsheet.SheetData();
					OpenXmlSpreadsheet.Row row = new OpenXmlSpreadsheet.Row();
 
					OpenXmlSpreadsheet.Cell currentCell;
					int columnCount = 0;
					var entitySchema = UserConnection.EntitySchemaManager.GetInstanceByName(SchemaName);
					foreach (EntitySchemaColumn column in entitySchema.Columns) {
						if (!column.DefValue.IsSystemValue &&
								(column.UsageType != EntitySchemaColumnUsageType.Advanced) && 
								(column.UsageType != EntitySchemaColumnUsageType.None) &&
								!column.Name.Equals("ProcessListeners") &&
								!(column.DataValueType is ImageDataValueType) &&
								!(column.DataValueType is ImageLookupDataValueType)) {
							currentCell = new OpenXmlSpreadsheet.Cell();
							currentCell.DataType =  OpenXmlSpreadsheet.CellValues.String;
							if (column.RequirementType == EntitySchemaColumnRequirementType.ApplicationLevel || column.RequirementType == EntitySchemaColumnRequirementType.DBLevel) {
								currentCell.StyleIndex = 3;
							} else {
								currentCell.StyleIndex = 4;
							}
							columnWidthsList.Add(CalculateWidth(column.Caption.Value));
							currentCell.CellValue = new OpenXmlSpreadsheet.CellValue(column.Caption.Value);
							row.AppendChild(currentCell);
						}
					}
					sheetData.Append(row);
					row = new OpenXmlSpreadsheet.Row();
					for (int i=0;i<columnWidthsList.Count; i++) {
						currentCell = new OpenXmlSpreadsheet.Cell();
						currentCell.DataType =  OpenXmlSpreadsheet.CellValues.String;
						currentCell.StyleIndex = 5;
						row.AppendChild(currentCell);
					}
					sheetData.Append(row);
					//Adding columns and specifeing width
					OpenXmlSpreadsheet.Columns columns = new OpenXmlSpreadsheet.Columns();
					uint j = 1;
					foreach (var w in columnWidthsList) {
						columns.Append(CreateColumnData(j, w));
						j++;
					}
					worksheet.Append(columns);
					//--
					worksheet.Append(sheetData);
 
					workbookStylesPart.Stylesheet = CreateStyleSheet();
					worksheetPart.Worksheet = worksheet;
					spreadsheetDocument.Close();
 
					stream.Seek(0, SeekOrigin.Begin);
					byte[] dataArray = stream.ToArray();
 
					Page.Response.BinaryWrite(dataArray);
					Page.Response.End();
				}
			}
			return true;
		}

 

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

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

Нравится

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

Александр, деталь с карточкой или с редактируемым реестром?

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

Добрый день столкнулся с проблемой , что при выполнении бп выдало ошибку 
Terrasoft.Common.NullOrEmptyException: Невозможно получить экземпляр схемы типа "Terrasoft.Core.Process.PldLeadManagementIdentification2PaladinPackage3Schema"
   в Terrasoft.Core.SchemaManager`1.InitializeSchema(ISchemaManagerItem schemaManagerItem, Assembly assembly)
   в Terrasoft.Core.SchemaManagerItem`1.get_Instance()
   в Terrasoft.Core.Process.BaseProcessElementFactory.CreateSubProcessInstance(ProcessSchemaSubProcess schemaSubProcess)
   в Terrasoft.Core.Process.ProcessElementFactory.CreateFlowElementInstance(ProcessSchemaBaseElement schemaElement)
   в Terrasoft.Core.Process.ProcessComponentSet.CreateFlowElementInstance(String schemaElementName, Boolean isReExecution)
   в Terrasoft.Core.Process.ProcessComponentSet.ForceGetFlowElementInstance(String schemaFlowElementName)
   в Terrasoft.Core.Process.ProcessComponentSet.Execute(String elementName, FlowContext flowContext)
   в Terrasoft.Core.Process.TaskService.Terrasoft.Core.Process.IHandler<Terrasoft.Core.Process.FlowMessages.ExecuteProcessElementRequest>.Receive(ExecuteProcessElementRequest message)
   в Terrasoft.Core.Process.MediatorBuilder.Mediator.Send[TMessage](TMessage message)
   в Terrasoft.Core.Process.FlowVisitor.ProcessQueueItem(FlowVisitorQueueItem queueItem)
   в Terrasoft.Core.Process.FlowVisitor.Dequeue()
   в Terrasoft.Core.Process.FlowService.<>c__DisplayClass32_0.<Terrasoft.Core.Process.IHandler<Terrasoft.Core.Process.FlowMessages.ExecuteProcessElementResponse>.Receive>b__0()
   в Terrasoft.Core.Process.FlowService.InvokeWithHandleCancelRequestedEvent(FlowVisitor visitor, Action action)
   в Terrasoft.Core.Process.FlowService.Terrasoft.Core.Process.IHandler<Terrasoft.Core.Process.FlowMessages.ExecuteProcessElementResponse>.Receive(ExecuteProcessElementResponse message)
   в Terrasoft.Core.Process.MediatorBuilder.Mediator.Send[TMessage](TMessage message)
   в Terrasoft.Core.Process.TaskService.ProcessFlowElementExecuted(Object sender, ProcessActivityAfterEventArgs args)
   в Terrasoft.Core.Process.ProcessFlowElement.OnExecuted(ProcessActivityAfterEventArgs e)
   в Terrasoft.Core.Process.ProcessUserTask.OnExecuted(ProcessActivityAfterEventArgs e)
   в Terrasoft.Core.Process.ProcessActivity.CompleteExecuting(Object[] parameters)
   в Terrasoft.Core.Process.Configuration.ActivityUserTask.CompleteExecuting(Object[] parameters)
   в Terrasoft.Core.Process.ProcessActivity.Complete(Object[] parameters)
   в Terrasoft.Core.Process.ProcessEngineImpl.CompleteExecutingInternal(Guid elementUId, IReadOnlyDictionary`2 parameterValues, ContinueProcessCommand command, Boolean removeMessageFromStore, Object[] parameters)

После обращение тп выяснилось, что в бп устарел метод GetParentApplicationUrl(HttpRequest) 
И посоветовали просто переименовать его в GetParentApplicationUrl(Terrasoft.Web.Http.Abstractions.HttpRequest)
Но проблема осталась, может кто сталкивался с такой проблемой ? 

Нравится

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

Максим, этот процесс Вы сами создавали? Из каких элементов он состоит, на каком вылетает ошибка? Процесс и его подпроцессы в отдельных схемах перед этим сохранили и публиковали?

 

Функция GetParentApplicationUrl, скорее всего, у Вас использована для получения адреса сайта.

Если просто закомментировать код с GetParentApplicationUrl или заменить его вызов на строку с реальным адресом (или читать из системной настройки), всё остальное работает?

 

А по поводу устарения, это относится к совместимости доработок с сайтами на Net Core, там нужно всё делать через классы из Terrasoft.Web.Http.Abstractions. Возможно, в новых версиях будет только так и работать.

В ходе доступа к сайту поддержки выяснилось, что причина, по которой происходит ошибка при публикации процесса, заключается в вызове метода GetParentApplicationUrl, а именно в параметре, который нужно передавать в метод.
Передавать нужно экземпляр класса HttpRequest из namespace Terrasoft.Web.Http.Abstractions, который можно определить так: HttpContext.Current.Request, например:

HttpRequest request = HttpContext.Current.Request;
applicationUrl = Terrasoft.Web.Common.WebUtilities.GetParentApplicationUrl(request);

При этом нужно добавить namespace "Terrasoft.Web.Http.Abstractions" и убрать "System.Web".

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

Get<Guid>("Код параметра")

 

Зверев Александр,

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

На всякий случай ещё раз сохраните и опубликуйте.

Откройте исходный код схемы и найдите строки с этими номерами.

Может, не везде в коде исправили.

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

Добрый день!

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

 

Видел вариант с переход по вкладкам

this.setActiveTab("имя_вкладки");

Нравится

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

Вешаете на кнопку обработчик. А в него что-то вроде этого: 

var tag = "SectionModuleV2/ActivitySectionV2/";
this.sandbox.publish("PushHistoryState", {hash: tag});

ActivitySectionV2 - реестр раздела Активности

Вешаете на кнопку обработчик. А в него что-то вроде этого: 

var tag = "SectionModuleV2/ActivitySectionV2/";
this.sandbox.publish("PushHistoryState", {hash: tag});

ActivitySectionV2 - реестр раздела Активности

Дмитрий А.,

Спасибо, попробую

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

Коллеги, подскажите пожалуйста логику оформление розничной продажи физическому лицу в Creatio 7.15

Ранее создавали контрагента и контакт контрагента - фактически вели двойные записи. На контрагента Иванова Ивана Ивановича создавалась продажа, заказ, договор. Не очень удобно так делать. Может изменилось что то?

Нравится

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

Есть три основных подхода:

1. Вести юридические лица в контрагентах, физические - в контактах. И модифицировать Лиды, Продажи, Договора, Документы, Счета, Заказы для использования обоих типов клиентов. Зато для маркетинга всё хорошо, и информация не дублируется (зато часто дублируется клиентский функционал в Контрагентах и Контактах)

2. Вести всех клиентов (физ. и юр. лица) в контрагентах, а их контактные лица - в контактах. Тогда вся Sales часть работает по базовой логике, но начинает страдать Marketing часть, либо надо дублировать информацию о клиентах-физ.лицах в контрагентах и контактах (можно, конечно, писать БП для синхронизации)

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

В каждом проекте приходится серьезно взвешивать плюсы и минусы. Универсального решения пока не нашёл

Есть три основных подхода:

1. Вести юридические лица в контрагентах, физические - в контактах. И модифицировать Лиды, Продажи, Договора, Документы, Счета, Заказы для использования обоих типов клиентов. Зато для маркетинга всё хорошо, и информация не дублируется (зато часто дублируется клиентский функционал в Контрагентах и Контактах)

2. Вести всех клиентов (физ. и юр. лица) в контрагентах, а их контактные лица - в контактах. Тогда вся Sales часть работает по базовой логике, но начинает страдать Marketing часть, либо надо дублировать информацию о клиентах-физ.лицах в контрагентах и контактах (можно, конечно, писать БП для синхронизации)

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

В каждом проекте приходится серьезно взвешивать плюсы и минусы. Универсального решения пока не нашёл

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

Доброго времени суток!
Сервис Террасофта под названием SaveWebFormLeadData использует данные со стороны клиента и отправляет на их ссылку обработки.
Интересует возможность отправки этих же данных со стороны сервера, например cURL ( PHP ).

С уважением, Сергиенко Николай

Нравится

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

Здравствуйте, Николай!

Этот вес сервис используется для сохранения лидов с вебформ в продукте Marketing. Какую задачу Вы хотите решить таким образом?

Здравствуйте, Алексей!

Нужно отправлять через Лиды информацию с помощью утилиты CRON

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

Добрый день,
Пытаемся внедрить Terrasoft Service Desk.
Есть необходимость добавлять группу контактов в Terrasoft Service Desk напрямую через базу по шедулеру, не создавая им логины.
Для этого написан некий SQL-скрипт для добавления необходимой информации в таблицу tbl_Contact. Администраторы спокойно видят все добавленные скриптом контакты и созданные, но обычные пользователи видят только контакты созданные в ручную. Права на все пока по умолчанию, ничего не меняли.
В чем может быть причина? Еще куда то нужно писать информацию о контактах?

Нравится

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

Необходимо записать информацию о правах на импортированные контакты в таблицу tbl_ContactRight.

Также, вполне возможно, будет необходимо импортировать информацию и на другие детали раздела, такие как: "Средства связи", "Адреса", "Карьера".

"Кулак Олег" написал:Необходимо записать информацию о правах на импортированные контакты в таблицу tbl_ContactRight.
Или настроить права доступа по умолчанию для пользователя, от имени которого выполняется импорт данных.

"Кулак Олег" написал:Необходимо записать информацию о правах на импортированные контакты в таблицу tbl_ContactRight

Спасибо, помогла запись в таблицу tbl_ContactRight

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