Получить и передать значение в LookupDataControl

Каким образом можно получить и в зависимости от полученного значения передать другое значение в LookupDataControl?
Для примера: На карточке "Контакт" есть поле с выпадающим списком "Средства связи". В нем значения: Мобильный, Телефон, Факс, Личный и т.п.
Каким образом сделать так, чтобы при выборе значения "Личный" выдавалось предупреждение, к примеру "Личным" пользоваться запрещено"!!! и значение в LookupDataControl'е с "Личного" менялось на к примеру "Телефон".
Выбираю так

var Name = DataField.Name;
var Dataset = DataField.ParentDataFields.ParentDataset;
if (Name == 'ID') {
var Value = DataField.Value;
if (Value == '{5C0A-4C2D-8684}'/*Личный*/){
ShowWarningDialog('Личным пользоваться запрещено!!!');
// Здесь как перевести в выборе с Личного на Телефон

А вот как с "Личного" поменять на "Телефон"?

Нравится

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

Меняйте не значение поля, а значение датасета

dlData.Dataset('Communication1TypeID') = 'id нужного типа';

Используйте событие OnDatasetDataChange невизуального компонента dlData:

if(DataField.Name == 'Communication1TypeID') {
     if(DataField.Value == 'some_id') {
            DataField.Value = DataField.OldValue;
            ShowErrorDialog('ERROR!');
     }
}

Все идентификаторы желательно выносить в scr_Consts, и затем писать так:
if(DataField.Name == myComTypeConst)

ps: желательно просто отфильтровать источник данных, чтобы в списке просто не отображались те варианты, которые выбрать нельзя. Так корректней согласитесь.

Спасибо, за варианты, все работает. Единственное, после нажатия ОК на ShowErrorDialog это диалоговое окно открывается повторно и после второго его закрытия в LookupDataControl'е изменяется значение на OldValue и можно закрыть карточку Контакта. Как можно сделать, чтобы окно ShowErrorDialog открывалось только 1 раз?
Для дальнейшего изучения Террасофта немного усложнил эту задачу:
ShowErrorDialog открывается в зависимости не только от some_id, но и от условий, данные которых находятся в Деталях.
Пример: У Контакта есть в Деталях Счета, у Счетов есть поле "Состояние оплаты".
В зависимости от:
при выборе значения "Личный" в карточке Контакта и если в поле "Состояние оплаты" Счетов этого Контакта, к примеру, есть значения "Не оплачен" запрещаем менять значение ("Личный") в нашем предыдущем примере:

if(DataField.Name == 'Communication1TypeID') {
     if(DataField.Value == 'some_id') && /*Состояние оплаты Счетов == 'Не оплачен'*/ {
            DataField.Value = DataField.OldValue;
            ShowErrorDialog('ERROR!');
     }
}

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

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

var Dataset = DataField.ParentDataFields.ParentDataset;
if(DataField.Name == 'Communication1TypeID') {
     if(DataField.Value == 'some_id') && /*Состояние оплаты Счетов == 'Не оплачен'*/ {
            Dataset.DisableEvents();
            DataField.Value = DataField.OldValue;
            Dataset.EnableEvents();
            ShowErrorDialog('ERROR!');
     }
}

Вариант обхода двойного открытия окна с отключением эвентов

            Dataset.DisableEvents();
            DataField.Value = DataField.OldValue;
            Dataset.EnableEvents();

не проходит. Все равно два раза вызывается диалоговое окно.

Скорее всего у Вас в скрипте условие прописано дважды или выполняется в каком-то цикле.

Прикрепите, пожалуйста, к посту весь скрипт (экспортируйте его из конфигурации).

По поводу Вашего вопроса с модификацией условия:

if (DataField.Name == 'Communication1TypeID') {   
	var bool = false;
	var ContactID = Dataset.Values('ID');
	var InvoiceDataset = Services.GetNewItemByUSI('ds_Invoice');
	ApplyDatasetFilter(InvoiceDataset, 'ContactID', ContactID, true); 
   	InvoiceDataset.Open(); 
  	if (InvoiceDataset.Values('BillStatusID')=='{89E2E62C-E2B0-47E6-A183-B774ED20CDE8}') //если по Контакту есть счет с состоянием оплаты = Ожидание оплаты
        {
            bool = true;
  	}
   	InvoiceDataset.Close(); 
	if((DataField.Value == '{FA08FC2A-9D55-40C9-9576-0017EAED3E49}')&&(bool==true)) {
              ShowErrorDialog('ERROR!');
     	}	
	return;
}

Андрей, в твоем примере проверится только первый счет, а их может быть несколько :)
Лучше всего, конечно, это создать новый сервис sq_MySelectQuery (сервис выборки данных с определенной таблицы), в котором добавить фильтр сравнения по ContactID с параметром ContactID и по StatusID с параметром StatusID, оба фильтра сразу же включить в сервисе. Из таблицы выбирать только одну колонку, к примеру ID. Затем в коде прописать что-то вроде следующего:

var ContactID = Dataset('ContactID');
var StatusID = isWaiting; //isWaiting - это константа в scr_Consts
//var isWaiting = '{id_состояния_оплаты_счета_в_ожидании}';
var mySQ = Services.GetNewItemByUSI('sq_MySelectQuery');
SetParameterValue(mySQ.Parameters, 'ContactID', ContactID, true);
SetParameterValue(mySQ.Parameters, 'StatusID', StatusID, true);
var Result = mySQ.Open();
if(Result.IsEOF) {
     ShowWarningDialog('Для тек. контакта счетов в состоянии "Ожидание оплаты" нет');
} else {
     ShowWarningDialog('Для тек. контакта есть счета в состоянии "Ожидание оплаты"! Их количество: ' + Result.RecordsCount);     
}

А вот по поводу двойного входа сюда:

if(DataField.Name == 'Communication1TypeID') {
     if(DataField.Value == 'some_id') {
            DataField.Value = DataField.OldValue;
            ShowErrorDialog('ERROR!');
     }
}

немного теории: событие OnDatasetDataChange выполняется каждый раз, когда значение какого либо поля датасета изменяется. Внутри события, при изменении конкретного поля (Communication1TypeID), при определенных условиях вы опять таки его меняете (обратно), тоесть событие будет вызвано еще раз. Но, т.к. у Вас стоит доп. условие

if(DataField.Value == 'some_id')

т.е. по сути - второй раз мы не должны зайти внутрь условия:

if(DataField.Value == 'some_id') {
            DataField.Value = DataField.OldValue;
            ShowErrorDialog('ERROR!');
     }

, т.к. мы изменили это значение на другое. Но раз заходит, значит что-то пошло не так. Тут нужна отладка - поставьте перед условием отладчик (Ctrl+F5), включите в реестре Windows регистр JitDebug в 1 и перезапустите Террасофт. Для отладки нужна Visual Studio (либо MS Script Debugger, но он крайне неудобен).

Если что - пишите.

Кажется Вам поможет следующая конструкция:

1. В самом начале скрипта объявите глобальную переменную-объект. Чаще всего её называют по имени сервиса. Она может быть у Вас уже и объявлена (если это коробочный датасет):

var myObject = {};

2. Структуру события OnDatasetDataChange нужно сделать следующей:

function SelfOnDatasetDataChange(DataField) {
try {
   if(myObject.IsUpdating) {
         return;
   }
   myObject.IsUpdating = true;
   if(DataField.Name == 'Name') {
         //ваш код
   }
   //....
} finally {
   myObject.IsUpdating = false;   
}
}

Дмитрий, спасибо. Не все кейсы проверил изначально.

Так должно работать:

if (DataField.Name == 'Communication1TypeID') {   
	var bool = false;
	var ContactID = Dataset.Values('ID');
	var InvoiceDataset = Services.GetNewItemByUSI('ds_Invoice');
	ApplyDatasetFilter(InvoiceDataset, 'ContactID', ContactID, true);
	InvoiceDataset.Open(); 
   	while(!InvoiceDataset.IsEOF) {
		  if (InvoiceDataset.Values('BillStatusID')=='{89E2E62C-E2B0-47E6-A183-B774ED20CDE8}')
  		       {
  			    bool = true; 
  	               }
		  InvoiceDataset.GotoNext();
	 }
   	InvoiceDataset.Close(); 
	if((DataField.Value == '{FA08FC2A-9D55-40C9-9576-0017EAED3E49}')&&(bool==true)) {
	       ShowErrorDialog('ERROR!');
     	}	
	return;
}

Спасибо, Дмитрий, Андрей!
Вот код, который отлично работает, за исключением пресловутого двойного открытия диалогового окна:

function dlDataOnDatasetDataChange(DataField) {
   if (System.GetLocalComputerName() == 'MYCOMP') { debugger; }   
   if(ContactEdit.IsUpdating) { 
         return;
   };
   var Dataset = DataField.ParentDataFields.ParentDataset;   
       ContactEdit.IsUpdating = true;
try {  
    var Communication1TypeName = dlData.Dataset.DisplayValues('Communication1TypeID'); 
    if (DataField.Name == 'Communication1TypeID') {   
        var bool = false;
        var ContactID = Dataset.Values('ID');
        var InvoiceDataset = Services.GetNewItemByUSI('ds_Invoice');
        ApplyDatasetFilter(InvoiceDataset, 'ContactID', ContactID, true);
        InvoiceDataset.Open();
        while(!InvoiceDataset.IsEOF) {
                  if (InvoiceDataset.Values('BillStatusID')=='{89E2E62C-E2B0-47E6-A183-B774ED20CDE8}')     //Ожидание оплаты
                       {
                            bool = true; 
                       }
                  InvoiceDataset.GotoNext();
         }
        InvoiceDataset.Close();
        if((DataField.Value == '{63153BB8-52E0-468A-8DDA-1EFD4B959780}')&&(bool==true)) { //Личный
               DataField.Value = DataField.OldValue;
	       ShowErrorDialog('Внимание! В Контакте есть счета на стадии "Ожидание оплаты"!\r\n\Пользоваться ' + Communication1TypeName + ' запрещено!');
        }       
        return;
      }
    }
     finally {
       ContactEdit.IsUpdating = false;   
   }
}

На основе дебаггера удалось выяснить, что после того как выбрали "Личный" открывается ДО (Диалоговое окно) первый раз.
После закрытия окна в LookupControl'e "Личный меняется" на OldValue.
Затем OldValue опять меняется на "Личный" и ДО открывается второй раз.
После второго раза закрытия ДО "Личный" меняется на OldValue.
После этого процесс завершается и можно закрыть карточку Контакта.
Между этими двумя открытиями дебаггер заходит в scr_Contact в
function SelfOnDatasetDataChange(DataField). Но не только сюда.
Это из основного что удалось проанализировать, но механизма, чтобы открывалось только 1 раз ДО не нашел.

Alex GF,

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

Нужно ставить доп. условие. Посмотрите значения Value и OldValue перед каждым вызовом ДО - они у Вас должны отличаться. И затем дополните условие, что то вроде

DataField.OldValue != DataField.Value

Если не выйдет, напишите значения OldValue и Value перед каждым вызовом ДО.

Значение перед вызовом ДОкна:
Value - 'Личный'
OldValue - 'Мобильный'
После первого вызова ДОкна:
Value - 'Мобильный'
OldValue - 'Мобильный'

Затем уходит на второй круг и возвращается к нашему debbuger:
Value - 'Личный'
OldValue - 'Мобильный'
После 2-го вызова ДОкна:
Value - 'Мобильный'
OldValue - 'Мобильный'

Можно Вас попросить поставить отладчик тут:

if((DataField.Value == '{63153BB8-52E0-468A-8DDA-1EFD4B959780}')&&(bool==true)) { //Личный
debugger;
DataField.Value = DataField.OldValue;

И вот прямо в месте остановки, посмотрите DataField.Value и DataField.OldValue, и запостите сюда еще раз пожалуйста. Т.е. должно быть только две остановки, соответственно два значения.

В момент остановки
Value = 'Личный'
OldValue = 'Мобильный'

Далее, в момент открытия ДО
Value = 'Мобильный'
OldValue = 'Мобильный'

После закрытия ДО
Value = 'Личный'
OldValue = 'Мобильный'

Заходим второй раз в нашу функцию, значения теже, выходим из нашей функции делаем цикл, опять заходим в нашу функцию, значения теже.
Проходим циклом по while(!InvoiceDataset.IsEOF), далее по функции, далее открывается второй раз Диалоговое Окно.
Value = 'Мобильный'
OldValue = 'Мобильный'

P.S. Версия Террасофт 3.3.2

Что-то странное. Не смог воспроизвести. Кажется, виноват return, выделенный жирным.

InvoiceDataset.Close();
if((DataField.Value == '{63153BB8-52E0-468A-8DDA-1EFD4B959780}')&&(bool==true)) { //Личный
DataField.Value = DataField.OldValue;
ShowErrorDialog('Внимание! В Контакте есть счета на стадии "Ожидание оплаты"!\r\n\Пользоваться ' + Communication1TypeName + ' запрещено!');
}
return;
}
}
finally {
ContactEdit.IsUpdating = false;
}
}

Если нет - ставьте заглушку... После первого показа окна установите значение в свойство глобального объекта, к примеру:

if(!ContactEdit.WindowWasShowed) {
 ShowErrorDialog('Внимание! В Контакте есть счета на стадии "Ожидание оплаты"!\r\n\Пользоваться ' + Communication1TypeName + ' запрещено!'); 
}
ContactEdit.WindowWasShowed = true;

И где-то на OnPrepare Еще добавьте строку:

ContactEdit.WindowWasShowed = false;

Спасибо Дмитрий, помогла только заглушка!

Проделал тоже самое (тот же самый код, но без заглушки) на версии террасофт 3.5.1. Там двойного открытия Диалогового окна нет! Единственный неопределенный (для меня) момент там:
После закрытия Диалогового Окна в LookupDataControl значение Value не меняется на OldValue. Хотя затем при нажатии на карточке "ОК" результат в базе правильный - OldValue.
В версии 3.3.2 в LookupDataControl значения меняются как надо.

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