Процесс инициализации.

Есть реестр раздела, есть окно редактирования записей реестра, на этом окне редактирования две закладки, на одной из закладок WindowContainer - свойство Window = ОкноНаследник-wnd_BaseGridArea, в этом окне расположен грид, в который нужно добавлять данные, редактировать и тд и тп...
Как проинициализировать всю эту цепочку:
Кликнул на реестре - открылось окно редактрования, в нем на одной закладке отображаются данные текущей записи реестра, на другой закладке в гриде отображаются данные, которые лежат в другой таблице и ссылаются на текущую запись реестра, под этим гридом нажимаю кнопку (добавить, изменить, удалить) - открывается окно редактирования текущей записи грида.

Нравится

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

Добрый день.

Правильно я понимаю, что у Вас в окне редактирования две страницы (Pages), на одной из которых основные данные, а на другой - реестр с данными из другой таблицы? Можно сделать так: написать функцию инициализации и обновления реестра по аналогии с тем, как реализованы подобные функции для деталей в разделе (примеры можно посмотреть в любом из скриптов, название которого содержит Workspace; функции обычно называются Initialize...Detail и Refresh...Detail соответственно). Но при реализации необходимо учесть, что в примерах родительским окном является окно раздела, а в Вашем случае это будет окно редактирования. По этой причине не получится использовать базовую функцию RefreshCommonDetail (скрипт scr_WorkspaceUtils), но можно создать по аналогии свою функцию.

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

Да, Вы правильно меня поняли. Я уже добился обновления этого реестра на одной из Page, но при этом:
1. его кнопка "добавить" почему то неактивна;
2. окно редактирования этого реестра не позволяет редактировать данные, даже не отображает их, а при попытке нажать на кнопку "ок" выдает ошибку о неоткрытом датасете;
3. нажатие кнопки "удалить" вообще не приводит к каким либо последствиям.

То что я наворотил выглядит так:
событие OnPrepare окна карточки редактирования (это в ней две Page, на одной из которых расположен грид (реестр)):

function wnd_BaseDBEditOnPrepare(Window) {
        scr_BaseDBEdit.wnd_BaseDBEditOnPrepare(Window);	
 
	SetAttribute(wcExpence.Window,'ParentItemFieldName', 'S_ID');
	SetAttribute(wcExpence.Window,'EditWindowUSI', 'wnd_SiteExpEdit');
	SetAttribute(wcExpence.Window,'DatasetUSI', 'sq_CashflowExp');
	wcExpence.Window.Prepare();
 
	if (Pages.ActivePage.Name == 'Page1'){
		PagesOnChangedActivePage(Pages)
	}
}

событие OnChangedActivePage объекта Pages:

function PagesOnChangedActivePage(Pages) { 
	var DatasetSite = dlData.Dataset;
	var S_ID = DatasetSite.Values('ID');
	if (Pages.ActivePage.Name == 'Page1'){
		var DatasetCashflow = wcExpence.Window.ComponentsByName('dlData').Dataset;
		DatasetCashflow.Close();
		ApplyDatasetFilter(DatasetCashflow, 'S_ID', S_ID, true);
		DatasetCashflow.Open();	
 
	}
}

Сразу возникло несколько замечаний:

1) Почему в атрибут DatasetUSI передаётся запрос, а не датасет?
2) Я бы использовал событие PagesOnChangeActivePage, а не PagesOnChangedActivePage. Первое событие автоматически срабатывает при изменении активной страницы. В связи с этим:
3) Не совсем понимаю, зачем в wnd_BaseDBEditOnPrepare вызывать

if (Pages.ActivePage.Name == 'Page1'){
        PagesOnChangedActivePage(Pages)
}

4) Кнопка "Добавить" может быть неактивна из-за того, что в окне реестра не определён атрибут ParentItemID. Попробуйте его передавать при обновлении дочернего реестра.

1) Да, это была ошибка, уже исправил.
2) Я убрал обработчик этого события и его тело вставил в обработчик события OnPrepare.
3) В соответствии с п. 2 этого уже нет.
4) А можно подробнее? Я вобщем то новичок как в Terrasoft так и в jscript.

Определил атрибут ParentItemID:

SetAttribute(wcExpence.Window,'ParentItemID','SiteID');

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

"SSV" написал:4) А можно подробнее?

С учётом Вашего кода, думаю, получится где-то так:

var DatasetSite = dlData.Dataset;
var S_ID = DatasetSite.Values('ID');
if (Pages.ActivePage.Name == 'Page1'){
        var DatasetCashflow = wcExpence.Window.ComponentsByName('dlData').Dataset;
        SetAttribute(wcExpence.Window, 'ParentItemID', S_ID);
        RefreshDetailData(DatasetSite, 'ID', DatasetCashflow, 'S_ID');
}

Это при условии, что в запросе дочернего реестра создан параметр с названием S_ID и фильтр с кодом S_ID сравнения соответствующей колонки с этим параметром. Попробуйте это вместо кода

DatasetCashflow.Close();
ApplyDatasetFilter(DatasetCashflow, 'S_ID', S_ID, true);
DatasetCashflow.Open();

Попробуйте всё-таки выполнять обновление реестра не на OnPrepare окна редактирования, а в обработчике события OnChangeActivePage объекта Pages.

Всё сделал, как Вы написали, но окно редактирования этого дочернего реестра всё равно не работает

(

SetAttribute(wcExpence.Window,'EditWindowUSI', <strong>'wnd_SiteExpEdit'</strong>);

).

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

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

Это реализовано в функционале базовых окон реестра и редактирования: во-первых, события по нажатию кнопок "Добавить" и "Изменить" обрабатываются по-разному. Окну редактирования передаётся атрибут RecordID, и если он содержит пустое значение (первый случай), датасет окна устанавливается в режим добавления записи (Dataset.Append()). Если же этот атрибут не пустой, датасет устанавливается в режим редактирования (Dataset.Edit()).

Установка атрибутов перед вызовом окна редактирования по факту осуществляется с помощью функции AddRequiredValuesToDictionary (во всех случаях), но она в разных случаях получает разные атрибуты. Посмотрите её текст в скрипте scr_BaseGridAreaUtils, а также тексты функций, которые её вызывают: GetAddDataAttributes, GetCopyDataAttributes, GetEditDataAttributes и т.д.

Спасибо за терпение, Олег, но...
Вот они ключевые слова - "Это реализовано в функционале базовых окон реестра и редактирования". Так что же нужно сделать, чтобы этот реализованный функционал заработал?

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

Хм, а может я не всё корректно тут изложил. Дело в том, что в окне редактирования, на котором лежит объект Pages, есть еще один даталинк, который и содержит датасет с данными из дочернего реестра. Даталинк с тем же датасетом есть в окне редактирования дочернего реестра. Может здесь проблема? Может эти датасеты нужно как то увязать?

А в каком виде их выложить? В виде XML файлов?

Да, в контекстном меню дерева сервисов есть возможность сохранить сервис в файл.

Что касается второго даталинка, Вы как-то с ним работаете в скрипте основного окна? Если нет, то не думаю, чтобы он как-то повлиял.

Сервисы.

Есть несколько вопросов.

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

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

Также прошу уточнить версию Terrasoft. Мне удалось определить только, что она ниже 3.1.0.

И последнее: при загрузке wnd_SiteExpenceEdit у меня пропадает окно wnd_BaseDBEdit. Значит ли это, что Вы получили это окно путём редактирования базового окна?

Заранее благодарен за помощь.

Нет, окно я получил созданием нового окна и присвоением свойству TemplateWindow значения wnd_BaseDBEdit. Моё окно wnd_BaseDBEdit на месте и не пропадает.

Версия - 3.0.2.244

Нужны все таблицы которые участвуют во всех запросах?

Вот тут вроде бы все сервисы.

Мне кажется, для корректной работы (если в остальном окно работает нормально) должно быть достаточно такого текста в обработчике PagesOnChangedActivePage:

function PagesOnChangedActivePage(Pages) {
	if (Pages.ActivePage.Name == 'Page1'){
		var DatasetCashflow = wcExpence.Window.ComponentsByName('dlData').Dataset;
		var SiteID = dlData.Dataset.Values('ID');
		SetAttribute(wcExpence.Window,'ParentItemFieldName', 'SiteID');
		SetAttribute(wcExpence.Window,'EditWindowUSI', 'wnd_SiteExpenceEdit');
		SetAttribute(wcExpence.Window, 'ParentItemID', SiteID);
        RefreshDetailData(dlData.Dataset, 'ID', DatasetCashflow, 'SiteID');
	} 
}

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

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

Записи начали удаляться.
А вот остальные функции так и не заработали - не редактируются поля Сумма и Описание, только дату можно выбрать. Кроме этого заметил еще вот что - при повторном вызове окна wnd_SiteExpenceEdit в одном сеансе работы программы, у него пропадает заголовок и названия полей (Дата, Сумма, Описание). Получается, что датасет в даталинке этого окна вообще куда то пропадает?

Попытался нажимать кнопку ОК в окне wnd_SiteExpenceEdit - в скрипте scr_BaseDBEditUtils в функции CheckRequiredDataControl, на строке

var Condition = !IsEmptyValue(DataField.Value) && (DataField.Value != 0);

выпадает ошибка о том, что источник данных ds_CashflowExp не открыт. При повторном вызове этого окна и попытке нажать ОК выпадает ошибка о том, что датасет равен NULL уже на строке

var DataField = DatasetLink.Dataset.DataFields(Control.DataFieldName);

в той же самой функции. Как такое может быть? И почему датасет может не открываться?

После добавления в обработчик события wnd_BaseDBEditOnPrepare окна wnd_SiteExpenceEdit строки

scr_BaseDBEdit.wnd_BaseDBEditOnPrepare(Window);

всё заработало.

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

Видимо, у меня всё работало, потому что я не создавал для wnd_SiteExpenceEdit своего скрипта, а в архиве его не было. В этом случае используется базовый скрипт.

По поводу инициализации. В основном инициализация окна извне (из другого окна, по какому-либо событию) заключается в передаче окну необходимых атрибутов и вызова его метода Prepare (во время обработки которого завершается инициализация). Список атрибутов может отличаться для разных окон, в зависимости от их предназначения и дальнейшей функциональности. Например, для реестра детали обязательно нужно указать EditWindowUSI - USI окна редактирования, ParentItemFieldName - название родительского поля, ParentItemID - ID родительского элемента. Иногда необходимо скрыть фрейм кнопок реестра - за это отвечает атрибут HideButtonsFrame. Если в окне реестра явно не указан датасет, необходимо заполнить атрибут DatasetUSI. И так далее. Например:

function InitializeCommunicationsDetail() {
	AccountsWorkspace.CommunicationsWindow = wndCommunicationsDetail.Window;
	var DetailWindow = AccountsWorkspace.CommunicationsWindow;
	SetAttribute(DetailWindow, 'DatasetUSI', 'ds_AccountCommunication');
	SetAttribute(DetailWindow, 'ParentItemFieldName', 'AccountID');
	SetAttribute(DetailWindow, 'EditWindowUSI', 'wnd_CommunicationEdit'); 
	SetAttribute(DetailWindow, 'WorkspaceDataset', BaseWorkspace.GridDataset);
	SetAttribute(DetailWindow, 'ParentItemID',
		BaseWorkspace.GridDataset.ValAsStr('ID'));
	DetailWindow.Prepare();
	AccountsWorkspace.CommunicationsDataset = 
		DetailWindow.ComponentsByName('dlData').Dataset;
 }

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

Некоторые атрибуты достаточно передать один раз (в процессе работы с окном они не меняются), но есть и такие (например, ParentItemID), которые нужно обновлять гораздо чаще. Поэтому в таких случаях создают отдельную функцию обновления окна, которая включает в себя функцию инициализации, если окно ещё не проинициализировано. Также эта функция присваивает атрибутам новые значения и обновляет датасет окна. Например:

function RefreshOpportunityDetail() {
	if (AccountsWorkspace.InitializeOpportunityFlag != true) {
		InitializeOpportunityDetail();                           
		AccountsWorkspace.InitializeOpportunityFlag = true;
	}
	var AccountID = BaseWorkspace.GridDataset.ValAsGUID('ID');
	if (AccountID == AccountsWorkspace.OpportunityOldAccountID) {
		return;
	} else {
		AccountsWorkspace.OpportunityOldAccountID = AccountID;  
	}
	SetAttribute(AccountsWorkspace.OpportunityWindow, 'ParentItemID', 
		AccountID);
	RefreshDetailData(BaseWorkspace.GridDataset, 'ID', 
		AccountsWorkspace.OpportunityDataset, 'CustomerID');
}

В принципе, для обновления окна детали в большинстве случаев достаточно тех действий, которые выполняет функция RefreshCommonDetail из скрипта scr_WorkspaceUtils.

Пример вызова окна редактирования:

        var EditWindowUSI = 'wnd_ContactEdit';
	var Attributes = GetNewDictionary();
	var AccountID = BaseWorkspace.GridDataset.ValAsGUID('ID');
	Attributes.Add('RecordID', GUID_NULL);		
	Attributes.Add('NotifyObject', Self);
        var DefaultValues = GetNewDictionary();
        DefaultValues.Add('AccountID', AccountID);	
	DefaultValues.Add('OwnerID', 
		Connector.CurrentUser.ContactID);
	ShowEditWindowEx(EditWindowUSI, Attributes, DefaultValues);

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

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

Спасибо за развернутый ответ.

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