Добрый день!

У меня в карточке проекта встроено несколько реестров.
Как мне при копировании проекта копировать и эти реестры со всеми данными в них?
Я хотела сделать на dlDataOnDatasetAfterCopy сохранение записи и потом вставку данных в реестры, но у меня в этом событии еще не заполнены другие поля проекта.

1) Где мне лучше сохранить проект? OnShow, может быть?
2) Каким образом я потом пойму, что датасет у меня копирован? И как лучше организовать передачу данных из встроенных реестров в новый проект?

Нравится

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

Признак копирования - в создаваемое окно при копировании записи передается атрибут 'SourceRecordID' с соответствующим значением. Плюс если не путаю атрибут 'IsCopy'

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

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

function dlDataOnDatasetAfterCopy(Dataset, KeyValue) {    
	var TArray = GetData('ds_UserTypes', KeyValue, 'UserTypeID');//сбор записей из встроенного реестра существующего проекта
	Dataset.Post();
	var ProjectID = Dataset('ID');
	SetData('ds_UserTypesInProject', ProjectID, TArray, 'UserTypeID');//вставка данных в строенный реестр нового проекта
	Dataset.Open();
	Dataset.Edit();
	RefreshDetails();//рефреш детали (в карточке проекта),на которой расположен встроенный реестр	
}

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

---------------------------
Error
---------------------------
Ошибка сохранения записи. Оригинальное сообщение об ошибке: The statement has been terminated.
The INSERT statement conflicted with the FOREIGN KEY constraint "FUserTypesInProjectProjectID". The conflict occurred in database "CRM", table "dbo.tbl_Project", column 'ID'
---------------------------
OK   
---------------------------

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

Самое банальное -- попробуйте сделать Post датасета :
Dataset.Post();
после обновления детали.

нет, не помогает. проблема все еще актуальна.

Ошибка происходит из-за наличия внешнего ключа FUserTypesInProjectProjectID в таблице tbl_UserTypes. Видимо запись данных во встроенный реестр (а именно поля ProjectID) происходит до того, как этот ID попадает в таблицу tbl_Project. Соответственно по проверке целостности происходит ошибка.

Нет, я же перед заполнением встроенного реестра делаю Dataset.Post() и только после этого беру получившийся ProjectID и пишу его в датасет встроенного реестра.
У меня проблема именно в отображении карточки проекта, на ней нет возможности добавить записи в встроенный реестр почему-то. Если переоткрываю карточку, там есть записи в реестрах скопированные.
Была попытка закрыть в скрипте окно проекта (Self.Close()) и затем снова открыть его на редактирование , но ошибка все равно была та же.
Я думала, что RefreshDetail, который использую в коде поможет, но ошиблась.
Может кто-то подсказать, как правильно произвести создание копии и открытие на редактирование записи с встроенными реестрами?

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

Реестры в карточке - это просто данные из справочников. Справочники доработанные. А деталь в карточке - это просто Page для разделения полей на карте, а то все не умещаются.

"Kat" написал:Нет, я же перед заполнением встроенного реестра делаю Dataset.Post() и только после этого беру получившийся ProjectID и пишу его в датасет встроенного реестра.

Вот здесь и проблема. ID в момент копирования датасета также копируется, новый не генерируется. Потому и проблема с внешним ключом. Нужно ID сгенерировать, потом сделать Post, а потом уже и скопировать все детали. К тому же вызовы Open и Edit после Post'а уже ни к чему. Рекомендую переписать Вашу функцию таким образом:

function dlDataOnDatasetAfterCopy(Dataset, KeyValue) {    
        var TArray = GetData('ds_UserTypes', KeyValue, 'UserTypeID');//сбор записей из встроенного реестра существующего проекта
        var RecordID = Connector.GenGUID();
        Dataset.Values('ID') = RecordID;
        Dataset.Post();
        var ProjectID = RecordID;
        SetData('ds_UserTypesInProject', ProjectID, TArray, 'UserTypeID');//вставка данных в строенный реестр нового проекта
        RefreshDetails();//рефреш детали (в карточке проекта),на которой расположен встроенный реестр    
}

в функции CopyRecord есть генерация ID для новой записи, поэтому после копирования выдается ошибка

function CopyRecord(BaseDBEdit, Window){
	var Dataset = BaseDBEdit.Dataset;
	Dataset.DisableGettingDisplayValues();
	try {
		var SourceRecordID = Window.Attributes('SourceRecordID');
		try {
			Dataset.Copy(SourceRecordID);// тут перескакивает в мой обработчик
		} catch (E){
			ShowWarningDialog(E.message);
			Dataset.Close();
			return;
		}
	    BaseDBEdit.RecordID = Connector.GenGUID();
		Dataset.ValAsGUID('ID') = BaseDBEdit.RecordID;//тут вылетает
		Window.Attributes('RecordID') = BaseDBEdit.RecordID;
		SetDefaultValues(BaseDBEdit);
	} finally {
		Dataset.EnableGettingDisplayValues();
	}
	SetEditWindowCaption(BaseDBEdit, Window);
}

В Ваш обработчик перескакивает еще до генерации нового ID, который уже на тот момент Вам нужен для копирования деталей. Поэтому предлагаю генерацию ID в dlDataOnDatasetAfterCopy оставить, а написать проверку в CopyRecord на то, не был ли еще сгенерирован ID : вместо

Dataset.ValAsGUID('ID') = BaseDBEdit.RecordID;

писать

if (IsEmptyGUID(Dataset.ValAsGUID('ID'))) {
   Dataset.ValAsGUID('ID') = BaseDBEdit.RecordID;
}

Виталий, спасибо за совет!
Немного изменила ваш пример кода на:

if (IsEmptyGUID(Dataset.ValAsGUID('ID'))) {
	BaseDBEdit.RecordID = Connector.GenGUID();
	Dataset.ValAsGUID('ID') = BaseDBEdit.RecordID;
} else {	
	BaseDBEdit.RecordID = Dataset.ValAsGUID('ID');
}

все работает!

Огромное всем спасибо за помощь!!!!

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

Добрый день!
Подскажите пожалуйста как модифицировать функцию копирования продуктов при копировании счета таким образом чтобы не копировались определенные поля продукта(например стоимость).
Если я правильно понимаю следует модифицировать след. ф-цию, добавив проверку на имя поля:

function CopyTreeDetail(SourceDataset, DestinationDataset,
        SourceParentItemIDFieldName, DestinationParentItemIDFieldName,
        DestinationParentItemID) {
        SourceDataset.Open();
        if (IsDatasetEmpty(SourceDataset)) {
            SourceDataset.Close();
            return;
        }
        var ParentIDFieldName = 'ParentID';
        var DestinationID;
        var DestinationIDs = GetNewDictionary();
        var SourceID;
        var SourceParentID;
        var DestinationParentID;
        var SourceIDDataField = SourceDataset.DataFields('ID');
        var DestinationIDDataField = DestinationDataset.DataFields('ID');
        var SourceParentIDDataField = SourceDataset.DataFields(ParentIDFieldName);
        var DestinationParentIDDataField = DestinationDataset.DataFields(ParentIDFieldName);
        var DestinationParentItemIDDataField = DestinationDataset.DataFields(DestinationParentItemIDFieldName);
        var SourceParentIDs = GetNewDictionary();
        var ExcludedFieldsList = GetNewDictionary();
        ExcludedFieldsList('ID') = true;
        ExcludedFieldsList(SourceParentItemIDFieldName) = true;
        ExcludedFieldsList(ParentIDFieldName) = true;
        while (!SourceDataset.IsEOF) {
                SourceID = SourceIDDataField.Value;
                DestinationID = Connector.GenGUID();
                DestinationIDs(SourceID) = DestinationID;
                SourceParentID = SourceParentIDDataField.Value;
                SourceParentIDs(DestinationID) = SourceParentID;
                DestinationDataset.Append();
                CopyRowDataEx(SourceDataset, DestinationDataset, ExcludedFieldsList);
                DestinationIDDataField.Value = DestinationID;
                DestinationParentItemIDDataField.Value = DestinationParentItemID;
                DestinationDataset.Post();
                SourceDataset.GotoNext();
        }
        SourceDataset.Close();
        DestinationDataset.Close();
        ApplyDatasetFilter(DestinationDataset, DestinationParentItemIDFieldName,
                DestinationParentItemID, true);
    DestinationDataset.Open();
        while (!DestinationDataset.IsEOF) {
            DestinationID = DestinationIDDataField.Value;
                SourceParentID = SourceParentIDs(DestinationID);
                if (SourceParentID != null) {
                        DestinationParentID = DestinationIDs(SourceParentID);
                        DestinationDataset.Edit();
                        debugger;
                        DestinationParentIDDataField.Value = DestinationParentID;
                        DestinationDataset.Post();
                }
                DestinationDataset.GotoNext();
        }
        DestinationDataset.Close();
}

Но не могу разобрать где именно.
Версия 3.1.0.23
Заранее спасибо!

Нравится

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

Добавьте после блока

        ExcludedFieldsList('ID') = true;
        ExcludedFieldsList(SourceParentItemIDFieldName) = true;
        ExcludedFieldsList(ParentIDFieldName) = true;

такой код:

if (SourceDataset.USI.indexOf('ds_OfferingInInvoice') != -1) {
        ExcludedFieldsList(<Название поля1>) = true;
        ...
        ExcludedFieldsList(<Название поляN>) = true;
}

Спасибо!
А как запретить копирование поля в самом счете, например состояние счета, пробовал:

 if (SourceDataset.USI.indexOf('ds_Invoice') != -1) {
        ExcludedFieldsList('BillStatusID') = true;
     }

не прошло.
заранее спасибо!

Пробую в срипте scr_DB в функции:

function SetDefaultValuesByDataset(Dictionary, Dataset) {
	var DatasetCopy = Services.GetNewItemByUSI(Dataset.USI);
	ApplyDatasetFilter(DatasetCopy, 'ID', Dataset.ValAsStr('ID'), true);
	DatasetCopy.Open();
	for (var i = 0; i < DatasetCopy.DataFields.Count; i++) {
        var DataField = DatasetCopy.DataFields.Items(i);
 
	var Name = DataField.Name;
	if (!IsSystemField(Name) && (DataField.FieldType != dftCalc) && (DataField.Value != null)) {
	var Key = Name;
	Dictionary.Add(Key, DataField.Value);
			}
 
	}
	DatasetCopy.Close();
}

заменить

	if (!IsSystemField(Name) && (DataField.FieldType != dftCalc) && (DataField.Value != null))

на

	if (!IsSystemField(Name) && (DataField.FieldType != dftCalc) && (DataField.Value != null) && (DataField.Name != 'BillStatusID'))

но тоже не помогает (

Отвечу себе сам, нашел решение:

if (Name == 'BillStatusID'){
   Dictionary.Add(Key, null);
}
else{
   Dictionary.Add(Key, DataField.Value);
}
Показать все комментарии

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

В службе поддержки порекомендовали использовать пример копирование договора, чему я и последовал.
В скрипте scr_DocumentUtils, на примере функции CopyOfferingInItemDetail, создал свою:

/* Функция копирование детали файлы: */

function CopyFilesInItemDetail(SourceFileInItemDatasetCode, //источник
    DestinationFileInItemDatasetCode, //назначение
    SourceParentItemIDFieldName,
    DestinationParentItemIDFieldName, SourceItemID, DestinationItemID) {
    var SourceItemName = GetItemNameByParentItemIDFieldName(
                SourceParentItemIDFieldName);
        var DestinationItemName = GetItemNameByParentItemIDFieldName(
                DestinationParentItemIDFieldName);
        if (SourceItemName == DestinationItemName) {
                var Message = FormatStr(CopyItemDetailConfirmation, "Файлы");
        } else {
                var Message = FormatStr(CopyItemDetailFromItemToItem, "Файлы",
                        SourceItemName, DestinationItemName);
        }
        var SourceDataset = GetSingleItemByCode(SourceFileInItemDatasetCode,
                'FileDetailSource'); //источник
        SourceDataset.FetchRecordsCount = -1;
        var DestinationDataset = GetSingleItemByCode(
                DestinationFileInItemDatasetCode, 'FileDetailDestination'); //назначение
        DestinationDataset.FetchRecordsCount = -1;
        SourceDataset.DisableEvents();
        DestinationDataset.DisableEvents();
        try {
                ApplyDatasetFilter(SourceDataset, SourceParentItemIDFieldName, SourceItemID,
                        true);
                EnableDatasetFields(SourceDataset, true);
                EnableDatasetFields(DestinationDataset, true);
                SourceDataset.Open();
                if (IsDatasetEmpty(SourceDataset)) {
                        SourceDataset.Close();
                        return;
                }
                if (ShowConfirmationDialog(Message) != wmrYes) {
                        SourceDataset.Close();
                        return;
                }
                CopyTreeDetail(SourceDataset, DestinationDataset,
                        SourceParentItemIDFieldName, DestinationParentItemIDFieldName,
                        DestinationItemID);
        } finally {
                SourceDataset.EnableEvents();
                DestinationDataset.EnableEvents();
        }
}

/* Функция копирование детали файлы END */  

Затем, для wnd_LibraryEditScript подключил scr_DocumentUtils и прописал:

/* Функция копирование детали файлы: */

function ProcessCopyFilesDetail(Dataset) {
        var IsCopy = Self.Attributes('IsCopy');
        if (!IsCopy) {
            return;
        }
        var SourceFileInItemDatasetCode = 'ds_FileInLibrary';
        var DestinationFileInItemDatasetCode = 'ds_FileInLibrary';
        var SourceParentItemIDFieldName = 'LibraryID';
        var DestinationParentItemIDFieldName = 'LibraryID';
        var SourceItemID = Self.Attributes('SourceRecordID');
        var DestinationItemID = Dataset.Values('ID');
        CopyFilesInItemDetail(SourceFileInItemDatasetCode,
                DestinationFileInItemDatasetCode, SourceParentItemIDFieldName,
    DestinationParentItemIDFieldName, SourceItemID, DestinationItemID);
}

/* Функция копирование детали файлы END */

Но возникает ошибка, с которой не могу справиться:

"Необработанное исключение (требуется объект)"

и дебаггер выбрасывает на строчку SourceParentID = SourceParentIDDataField.Value;
в скрипте scr_DB.

Может кто поможет решить проблему? :smile:

Нравится

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

деталь продукты - это дерево
а деталь файлы - это не дерево , из-за этого и происходит ошибка
попробуйте реализовать как в задачах , копирование контактов
scr_TaskUtils (CopyTaskContacts)

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

Здравствуйте, коллеги.

Хочу рассказать о нововведении, которое поможет вам в работе с корпоративной CRM-системой.

Всем пользователям корпоративной CRM-системы, у которых клиентское приложение обновлено до версии 3.3.1.111 или выше, доступна функциональность по копированию ссылок на объекты системы. В контекстном меню разделов появился соответствующий пункт меню:


При копировании гиперссылки в буфер обмена помещается название объекта и ссылки для перехода к нему и для открытия его карточки.

Пример: Гдуля Виталий (Перейти/Открыть)

При использовании гиперссылок на объекты Terrasoft почтовый клиент Outlook выдает предупреждение о возможном нарушении безопасности:

Работа с гиперссылками на объекты Terrasoft абсолютно безопасна, поэтому на вопрос «Продолжить?» следует отвечать «Да».

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

Нравится

Поделиться

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

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

было бы не плохо реализовать ссылку на объект в детали :wink:
Насколько я помню, Виталий, она в реализации?
Так же, вопрос системы безопасности Outlook решается внесением 1 параметра в реестр. И переходы будут еще более комфортные.

О безопасности. Чтобы отключить это надоедливое окошко изменить один ключ в реестре. В архиве для MS Outlook 2007 и MS Outlook 2010.

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