Добрый день.
Если я копирую процесс (кнопка "Копировать диаграмму") который находится в пакете "MyPackage", то копия создается в пакете "Custom". Даже если в системной настройке CurrentPackageId установлен "MyPackage".

Является ли данное поведение корректным?
И есть ли возможность при создании копии указывать в какой пакет создавать эту копию?
Версия 7.10.0.1742

Нравится

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

В новой диаграмме, которая создалась после копирования, нужно перейти на закладку Settings (Настройки) и в поле Package (Пакет) указать нужное значение (см. прикрепленный файл).

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

Насчет корректности такого поведения системы (копирования в Custom). Логично копировать в тот пакет, который указан пользовательским. Вы пишите, что сменили значение настройки CurrentPackageId, попробуйте изменить значение настройки CustomPackageUId, после вылогиниться и потом зайти и проверить, куда будет скопирована диаграмма.

"Алла Савельева" написал:В новой диаграмме, которая создалась после копирования, нужно перейти на закладку Settings (Настройки) и в поле Package (Пакет) указать нужное значение (см. прикрепленный файл).

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

"Алла Савельева" написал:
...
попробуйте изменить значение настройки CustomPackageUId
...

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

Просьба к сотрудникам Террасофт:
Может стоит указать об этой особенности копирования диаграмм в документации или исправить код который смотрит на CustomPackageUId вместо CurrentPackageId?

На самом деле в документации информация об этой настройке есть на Академии по ссылке: https://academy.terrasoft.ru/documents/technic-sdk/7-9/paket-custom, хотя, как по мне, объяснение отличий не очень очевидно :confused:

Информацию по работе с пакетами можно найти тут: https://academy.terrasoft.ru/documents/technic-sdk/7-9/rabota-s-paketami

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

Нужно разработать логику которая бы копировала и данные детали раздела.
Есть такой код isCopyMode

onEntityInitialized: function() {
                                if (this.isAddMode() || this.isCopyMode()) {
//код
}
}

Но как узнать данные источника откуда копируется раздел ?

Нравится

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

Если это isCopyMode то id от кого скопировались узнать можно так:
this.get("SourceEntityPrimaryColumnValue")
Если isAddMode, то понятно, что нет никакого источника.

Максим спасибо.
Буду делать копирование так

onEntityInitialized: function() {
				if (this.isCopyMode()) {
 
this.get("SourceEntityPrimaryColumnValue"); //id источника
//код копирования
 
				}

вопрос...как я понимаю пока еще нет же созданной записи в БД?

"Юсупов Марат" написал:как я понимаю пока еще нет же созданной записи в БД?

Все верно, данной записи еще нет в бд, но вы можете вызвать:
this.save({silent:true}) что сохранит данную запись в бд, не закрывая карточку. После чего можно будет добавлять в неё и записи деталей.

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

Доброго дня!
Столкнулся со след. задачей:
Имеется 2 раздела: "Заказы" и "Инженерная спецификация"
Оба раздела имеют свою древовидную деталь "Продукты"
Мне необходимо скопировать выделенные продукты из раздела "Инженерная спецификация" в раздел "Заказы". Копироваться должны только определенные поля.
Подскажите пожалуйста, как это можно реализовать?

Нравится

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

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

У каждого грида есть свойство SelectedIDs - возвращаемое массив идентификаторов выделенных записей. Зная идентификатор записи, можете выполнить обновление продуктов в древовидном реестре другого реестре. Обновление можно реализовать несколькими способами:

  1. 1. Используя датасет
  2. 2. Используя запрос на обновление
  3. 3. Используя хранимую процедуру

Рекомендую использовать первый способ, выполнить обновление через датасет.
Как за пример, можете взять логику обновления данных детали [Средства связи] раздела [Контрагент] реализованной в функции ActualizeCommunication сервиса scr_Account.

Cпасибо, сейчас попробую!

Можно еще посмотреть реализацию копирования дерева продуктов в scr_DocumentUtils...
Функция CopyOfferingInItemDetail. И далее из нее вызовы...

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

Здравствуйте!
Возникла проблема при копировании записей.
Использую следующий конструктор:
var newEntity = new Entity (entity);
Запись корректно создается, однако события объекта не срабатывают, в чем может быть проблема?

Полный код приведен ниже:

private void copyRouteDetail(Guid TemplateId, Guid TransRequestId) {
        EntitySchema schema = UserConnection.EntitySchemaManager.GetInstanceByName("SxRouteDetailTransRequest");
        EntitySchemaQuery esq = new EntitySchemaQuery(schema);
        esq.AddAllSchemaColumns();
        esq.UseAdminRights = false;
        esq.Filters.Add(esq.CreateFilterWithParameters(FilterComparisonType.Equal,
                                                        "SxRouteOptions", TemplateId));
        EntityCollection entities = esq.GetEntityCollection(UserConnection);
         foreach (Entity entity in entities)
        {
                var newEntity = new Entity (entity);
                newEntity.SetColumnValue("SxRouteOptionsId", null);
                newEntity.SetColumnValue("SxTransRequestId", TransRequestId);
                newEntity.UseAdminRights = false;
                newEntity.Save(false);
        }
}

Нравится

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

Здравствуйте, данный вопрос будет решён в рамках Вашего обращения №0300267

"Пащенко Александр Сергеевич" написал:Запись корректно создается, однако события объекта не срабатывают, в чем может быть проблема?

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

я бы рекомендовал внимательно проанализировать все бизнес-процессы выполняемые в сохраняемом Entity, внимательно проанализировать сначало Глазами, а потом внимательно проанализировать в Дебагере, что происходит в вашей базе данных при сохранение, и найти место где возникает исключение, при котором запись создается, но не выполняются события.

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

Михаил, все процессы процессы построены корректно и работают стабильно.
Процитирую ответ техподдержки:
"Данное поведение проявляется по причине того что, при использования данного конструктора класса Entity свойства создаваемого объекта отвечающие за вызов событий проставляются как false. К сожалению, изменить данные свойства после создания объекта нельзя.

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

var userConnection = context.UserConnection;
var schema = UserConnection.EntitySchemaManager.GetInstanceByName("Account");
var newEntity = schema.CreateEntity(UserConnection);
newEntity.SetDefColumnValues();
//код установки значений колонок объекта
newEntity.Save();

Упростить задачу копирования значений колонок из одного объекта в другой можно используя метод public IEnumerable GetColumnValueNames(), который возвращает имена колонок объекта. Затем перебирая в foreach результат данного метода скопировать значения колонок."

При отладке, единственная разница, которую нашел - при использовании конструктора
var newEntity = new Entity(oldEntity)
у newEntity.Process=null,
у oleEntity.Process = не nulll, а ссылка на метод.

Михаил, все процессы процессы построены корректно и работают стабильно.
Процитирую ответ техподдержки:
"Данное поведение проявляется по причине того что, при использования данного конструктора класса Entity свойства создаваемого объекта отвечающие за вызов событий проставляются как false. К сожалению, изменить данные свойства после создания объекта нельзя.

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

var userConnection = context.UserConnection;
var schema = UserConnection.EntitySchemaManager.GetInstanceByName("Account");
var newEntity = schema.CreateEntity(UserConnection);
newEntity.SetDefColumnValues();
//код установки значений колонок объекта
newEntity.Save();

Упростить задачу копирования значений колонок из одного объекта в другой можно используя метод public IEnumerable GetColumnValueNames(), который возвращает имена колонок объекта. Затем перебирая в foreach результат данного метода скопировать значения колонок."

При отладке, единственная разница, которую нашел - при использовании конструктора
var newEntity = new Entity(oldEntity)
у newEntity.Process=null,
у oleEntity.Process = не nulll, а ссылка на метод.

"Пащенко Александр Сергеевич" написал:Данную задачу можно решить следующим образом:

var userConnection = context.UserConnection;
var schema = UserConnection.EntitySchemaManager.GetInstanceByName("Account");
var newEntity = schema.CreateEntity(UserConnection);
newEntity.SetDefColumnValues();
//код установки значений колонок объекта
newEntity.Save();

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

так техническая поддержка вам предложила верный вариант решения вашей проблемы. Чем он вам не нравиться? что не устраивает?

Мне нужно сделать точную копию записи, в которой более 70 колонок, изменив всего 1 значение. Использовать для этого указанный выше конструктор считаю более целесообразным, но он некорректно работает. О варианте перебора всех колонок я знал. Сюда ответ продублировал на случай, если кому-то понадобится еще.

Спасибо за отклик!

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

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

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

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

Нравится

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

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

Если Вам не трудно, прошу Вас уточнить Ваш вопрос скриншотами.

Если я правильно понял, то Вас интересует как передать значение со страницы проекта в страницу детали, чтобы рассчитать значение полей. Это можно сделать с помощью messages.
1. В объект messages проекта добавить SUBSCRIBE. В объект messages детали добавить PUBLISH.
2. На init проекта подписаться на сообщение
this.sandbox.subscribe("GetSomeInfoFromDetail", function(argument) {
return this.someFunctionUsingArgument();
}, this, ["key"]);
3. На детали, когда Вам нужно получить значение с проекта сделать sandbox.publish
var result = this.sandbox.publish("GetSomeInfoFromDetail", valueToSend, ["key"]);

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

Спасибо, Вы очень помогли :smile:

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

В этом блоге хочу рассказать о развитии Drag&Drop в версии 3.4.x+
Никому не секрет, что DataGrid «умеет» принимать файлы с помощью Drag&Drop. Такая функциональность реализована на детали «Файлы». Но, для того, чтобы файлы извлечь с детали «Файлы», к примеру, прикрепить к письму, требуется совершать операции по сохранению его на жесткий диск, а уж потом перемещать куда нужно. Данный способ манипулирования файлами занимает много времени. Поэтому, в DataGrid был добавлен функционал полноценного Drag&Drop. Теперь есть возможность сохранять файлы только с помощью мыши. Также, весьма немаловажным бонусом, появилась возможность копировать и вставлять файлы с помощью комбинаций Ctrl+C/Ctrl+V. Именно с помощью этих операций не обойтись при переносе файлов внутри системы (файлы из одной записи в другую, из одного раздела – в другой). Плюс к выше сказанному появилась «автогенерация файлов по формату». Т.е., при нажатии Ctrl+V система анализирует содержимое буфера и генерирует (+ сохраняет) файл нужного формата.
Поддерживаемые форматы:

  1. Все изображения. Любое изображение будет сохранено в PNG формате (даже если Вы нажали Print Screen, то система сгенерирует Вам готовый файл)
  2. RTF. Любой форматированный текст буде сохранен в формате *.rtf
  3. HTML. Любой текст с HTML разметкой будет сохранен в *.html файл
  4. TEXT. Любой текст, который не является RTF – будет сохранен в *.txt файл

Дальше речь пойдет о реализации этих возможностей. Для начала пройдемся по имеющимся, а также новым свойствам:
Pic 1

  • CanCopySystemObjects – свойство, которое разрешает или запрещает копирование с помощью Ctrl+C
  • CanDragDropSystemObjects – знакомое свойство с предыдущих версий, которое разрешает или запрещает Drag&Drop в систему. Однако, в 3.4.x оно еще отвечает и за Drag&Drop из системы
  • CanPasteSystemObjects – свойство, которое разрешает или запрещает вставку с помощью Ctrl-V

Также, помимо свойств, появились новые события:
Pic 2

  1. OnGetSystemObjectNames – в обработчике события нужно заполнить имена файлов, которые будут копироваться (файлы будут сохранены под этими именами и расширениями). По количеству имен в списке будут генерироваться события OnGetSystemObjectData
  2. OnGetSystemObjectData - это событие придет столько раз, сколько было задано имен файлов в обработчике события OnGetSystemObjectNames. И при каждом вызове будет прислан Index файла, данные которого нужны. В обработчике необходимо вернуть поток данных согласно индексу файла
  3. OnPasteSystemObject – данное событие будет генерироваться при нажатии Ctrl+V. В него будет передан список файлов, которые нужно сохранить
  4. OnGetSystemObjectText – данное событие генерируется в том случае, когда «приемник» файла не может принять файл, но может принять текст. Поэтому в обработчике можно изменить текст, который будет передан

Маленьким, но приятным бонусом, появилась возможность отображать иконки, зарегистрированные в Windows согласно расширению файла:
Pic 3
Для того, нужно в обработчике OnGetRowDrawInfo, параметру ImageName нужно установить значение имени файла с расширением. Причем имя значения не имеет, так как поиск будет происходить только по расширению файла. Пример:

function OnGetRowDrawInfo(DataGrid, Color, TextColor, ImageName, Font) {
ImageName.Value = 'File.docx'; //Все элементы будут отображать иконку Word’а
ImageName.Value = 'File.xls' //Все будут отображать иконку Excel
}

Перед тем, как подвести итог, я предоставлю код реализации этих возможностей:

//Эта функция идентична для сохранения файлов как на
//событие OnDragDropSystemObjects, так и на OnPasteSystemObjects
function InsertSystemObjects(SystemObjectsList) {
        var FilesArray = SystemObjectsList.CommaText.split(',');
        var FileNames = System.CreateObject('TSObjectLibrary.StringsList');
        for (var i = 0; i FilesArray.length; i++) {
                FileNames.Clear();
                var Path = FilesArray[i];
                while (Path.indexOf('"', 0) != -1) {
                        Path = Path.replace('"', '')
                }
                FileNames.Add(Path);
                if (!CheckFileExists(Path)) {
                        AddObject(ft_FolderLink, Path, 1, null);
                } else {
                        AddObject(ft_File, FileNames, 1, null);
                }      
        }
}
 
//Реакция на Drag&Drop
function grdDataOnDragDropSystemObjects(DataGrid, DataGridColumn, SystemObjectsList, DestinationRowID, DropMode, WithCtrl) {
        InsertSystemObjects(SystemObjectsList);
}

//Реакция на Ctrl+V
function grdDataOnPasteSystemObjects(DataGrid, DataGridColumn, SystemObjectsList, SelectedIDs) {
        InsertSystemObjects(SystemObjectsList);
}

//Запрос имен объектов, который нужно копировать
//Если, в обработчике , вы запретите копировать какой-то
//файл (не передадите его имя), то в обработчике события
//OnGetSystemObjectData вам также нужно реализовывать
//дополнительные проверки
function grdDataOnGetSystemObjectNames(DataGrid, SourceDataGrid, SelectedIDs, SystemObjectNames) {
        var Dataset = GetSingleItemByCode('ds_Files', 'SystemObjectsFileDetail');
        var FileDataDataField = Dataset.DataFields.ItemsByName('FileData');
        EnableDatasetFilters(Dataset, false, 'ID');
        EnableDatasetField(Dataset, FileDataDataField, false);  
        Dataset.Open();
        for (var i = 0; i SelectedIDs.Count; i++) {
                if (!Dataset.Locate('ID', SelectedIDs(i))) {
                        continue;
                }
                SystemObjectNames.Add(Dataset('Link'));
        }
        Dataset.Close();
}

//Как было сказано выше - это событие генерируется для
//каждого файла, имя которого было указано в обработчике
//события OnGetSystemObjectNames. Порядок в списке
//файлов совпадает с порядком генерации события.
//Т.е., параметр SystemObjectIndex – это индекс в списке имен файлов
function grdDataOnGetSystemObjectData(DataGrid, SourceDataGrid, SelectedIDs, SystemObjectIndex, SystemObjectData) {
        if (!SelectedIDs) {
                return;
        }
       
        if (SystemObjectIndex >= SelectedIDs.Count) {
                return;
        }
        var RecordID = SelectedIDs(SystemObjectIndex);
        var Dataset = GetSingleItemByCode('ds_Files', 'SystemObjectsFileDetail');
        var FileDataDataField = Dataset.DataFields.ItemsByName('FileData');
        ApplyDatasetFilter(Dataset, 'ID', RecordID, true);
        EnableDatasetField(Dataset, FileDataDataField, true);
        Dataset.Open();
        //Из Blob поля мы просто сохраняем значение в поток SystemObjectData
        Dataset.DataFields.ItemsByName('FileData').GetValAsBlob(SystemObjectData);
        Dataset.Close();
}

Важно для разработчика:

  1. События OnGetSystemObjectNames, OnGetSystemObjectData и OnGetSystemObjectText начинают приходить только после того, как вы нажали Ctrl+V или отпустили кнопку мыши (при перетаскивании файлов) вне зависимости от того наше ли это приложение, или же стороннее
  2. События OnGetSystemObjectNames, OnGetSystemObjectData и OnGetSyestemObjectText в качестве параметра SelectedIDs принимают список IDs записей, которые действительно были скопированы (это важно). Этот список может не совпадать с DataGrid.SelectedIDs или SourceDataGrid.SelectedIDs. Связано это с тем, что вы можете переносить файлы, к примеру, с одного договора – в другой, и при этом DataGrid == SourceDataGrid (ведь это та же деталь), а при переходе на другую запись основного реестра применится фильтр для детали, и SourceDataGrid .SelectedIDs уже будет содержать совершенно другие значения
  3. В событие OnGetSystemObjectNames в качестве параметра SystemObjectNames приходит уже созданный IStringsList, Вам не нужно его создавать или же уничтожать – нужно только заполнить
  4. В событие OnGetSystemObjectData в качестве параметра SystemObjectData приходит уже созданный IStream. Вам достаточно просто записать в него значение. Создавать или уничтожать его также не нужно
  5. Событие OnGetSystemObjectText приходит только тогда, когда «приемник» не может принять файл(ы) (к примеру, при вставке в открытый документ Word). В нем можно указать текст, который необходимо вставить
  6. Вы можете пропустить копирование какого-либо файла. Для этого достаточно внести изменения в код обработчика OnGetSystemObjectData:
  7. //Чтобы отменить копирование файла не обязательно его
    //отсутствие в списке файлов, достаточно отменить его по индексу
    function grdDataOnGetSystemObjectData(DataGrid, SourceDataGrid, SelectedIDs, SystemObjectIndex,   SystemObjectData) {
            if (!SelectedIDs) {
                    return;
            }
           
            if (SystemObjectIndex >= SelectedIDs.Count) {
                    return;
            }
           //К примеру мне не хочется, чтобы пользователь
           //скопировал 2-й выбранный файл
           //Для этого достаточно выйти из функции, не
           //заполняя поток SystemObjectData
           If (SystemObjectIndex == 1) {
              return;
    }
  8. Перемещение между DataGrid внутри системы осуществляется разными способами, в зависимости от установленных свойств. Рассмотрим 2 случая:
    1. Перемещение по умолчанию
    2. Pic 4

    3. Копирование файла(ов)
    4. Pic 5

      При манипуляции комбинациями Ctrl+C/Ctrl+V все аналогично, только учитывается свойство CanPasteSystemObjects

    Собственно, это все, что я хотел рассказать о развитии Drag&Drop’а.

    Приятной работы!

    P.S. Вся функциональность доступна в версиях 3.4.0.99+ и 3.4.1.16+

Нравится

Поделиться

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

Подскажите, есть ли возможность обновиться с версии 3.3.2.310 до версии 3.4? Необходимо получить возможность перетягивания файлов из Террасофт в письмо MS Outlook.
С какими проблемами можно столкнуться при этом (совместимость конфигурации 3.3.2 с версией бинарных файлов 3.4, прочее)?

Андрей, обновление возможно. Как один из вариантов - развернуть 3.4 и перенести данные из 3.3.2 на уровне БД. Также, необходимо согласовать изменение лицензий с ответственным менеджером со стороны Terrasoft.

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

Заметил такую вещь, что при копировании записи (например, счета или договора) копируются также поля синхронизации с 1С (UID1C, Object1C, Code1C). Из-за этого рушится система синхронизации с 1С для таких записей с одинаковыми полями.
Начал копать в этом направлении:
1. Копирование записи реализуется функционалом wnd_BaseGridArea:

CopyGridAreaData(Self, BaseGridArea);

2. Эта функция в скрипте scr_BaseGridAreaUtils

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

Нравится

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

Попробуйте в соответствующем Dataset снять "галочку" копировать для этих полей.

А там галочки и так не стоят не стоят (((

Копирование полей при копировании записей производится в ядре системы. Для того, чтобы указать, какие колонки следует/не следует копировать, в наборе данных для соответствующих колонок нужно изменить значение параметра «Копирование поля». Если галочка не стоит, то и поле не будет копироваться.
В Вашем случае, при интеграции с 1С, указанные поля добавляются в таблицу, но по умолчанию не добавляются в набор данных. Добавили ли Вы их в набор данных? Если да, то убрали ли галочки напротив параметра «Копирование поля»? Если таких полей в наборе данных нет, то добавьте их и снимите галочки копирования.
Если же у Вас в наборе данных присутствуют указанные поля, для них сняты галочки копирования, но копирование все равно происходит, то Вам следует сделать скриншоты параметров этих полей набора данных, указать версию бинарных файлов и используемый Вами продукт, ОС, тип БД, экспортировать сервисы таблицы, запроса на выборку и набора данных и прислать все эти данные для более детального рассмотрения проблемы. В последнем случае ситуация нетипична.

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

Добрый день!

У меня в карточке проекта встроено несколько реестров.
Как мне при копировании проекта копировать и эти реестры со всеми данными в них?
Я хотела сделать на 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)

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