Задача - запретить удаление выполненных операций всем пользователям, кроме нескольких определенных.
Что делаю - на событие OnDatasetBeforeDelete для ds_Cashflow в скрипте ds_CashflowScript пишу функцию

function SelfOnDatasetBeforeDelete(Dataset) {
        ContactID = Connector.CurrentUser.ContactID ;
        CashflowScript.StatusID = GetFieldValueFromDisabledField(Dataset, 'StatusID');
        if (CashflowScript.StatusID=='{FDEA47BE-53FE-4730-BF4F-4F44C3B5D61A}'){
           if (ContactID != '{32E85247-DE68-4C6A-B032-06F5CDFB2B3A}'  &&
                 ContactID != '{E2093D56-49ED-4D4D-B69E-C9FF0623DE95}'  &&
                 ContactID != '{ED75744C-BC2C-469C-8993-8473E23E7F06}'  &&
                 ContactID != '{14925854-AAFD-4516-8A23-F2B5312F4C63}'){
                 ShowInformationDialog('У Вас нет прав удалять выполненную операцию');
                 return;
                  }
            }
        }

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

Нравится

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

Мне кажется, такую логику лучше сделать путём раздачи прав на группу таблиц этого раздела.

Это не то, что мне необходимо. Потому что удаление операций других состояний, кроме " выполненные" , допускается. Мне надо сделать именно так, как я описала.

По идее полный обработчик выглядит так (3.4.1)

ds_CashflowOnDatasetBeforeDelete(Dataset, DoDelete)

если внутри по условию присвоить

DoDelete.Value = true;

то удаление должно отмениться

У меня в 3.3.2 функция выглядит без DoDelete. Если его туда дописать, то ничего не изменится

Виктория, странно, у меня в 3.3.2 есть параметр DoDelete. Конструкция ниже работает:

function ds_CashflowOnDatasetBeforeDelete(Dataset, DoDelete) {
 
	CashflowScript.StatusID = GetFieldValueFromDisabledField(Dataset, 'StatusID');
 
	if (CashflowScript.StatusID =='{FDEA47BE-53FE-4730-BF4F-4F44C3B5D61A}')  { 	
		ShowInformationDialog('У Вас нет прав удалять выполненную операцию');
	    DoDelete.Value = false;
	}   

}

Но можно также создать обработчик события кнопки "Удалить" грида Операций, в которой реализовать аналогичное условие:

function btnDeleteOnClick(Control) {
 
	 if (grdData.DatasetLink.Dataset.ValAsGUID('StatusID')  == '{FDEA47BE-53FE-4730-BF4F-4F44C3B5D61A}')  {
	  	ShowInformationDialog('У Вас нет прав удалять выполненную операцию');
	 }
	 else
	 	scr_BaseGridArea.btnDeleteOnClick(Control);
}

"Безродный Андрей" написал:Но можно также создать обработчик события кнопки "Удалить" грида Операций, в которой реализовать аналогичное условие:

Кнопочка delete все равно будет работать по выделенной записи :)

"Александр Кудряшов" написал:
Безродный Андрей пишет:

Но можно также создать обработчик события кнопки "Удалить" грида Операций, в которой реализовать аналогичное условие:

Кнопочка delete все равно будет работать по выделенной записи :)


Александр,

проверил:

по кнопке Delete на клавиатуре аналогично появляется окно с предупреждением (бинарные файлы 3.3.2.311):

http://i68.fastpic.ru/big/2014/0930/1d/cf02d7f1e5909e18df53bdfa5f1de61d.png

У меня только в скрипте wnd_CashflowGridAreaScript есть функция с DoDelete, называется function dlDataOnDatasetBeforeDelete(Dataset, DoDelete).

Сейчас попробую привязаться к кнопке "Удалить"

"Безродный Андрей" написал:по кнопке Delete на клавиатуре аналогично появляется окно с предупреждением (бинарные файлы 3.3.2.311)

Спасибо, вот этого я не знал... или хорошо забыл :smile:

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

function btnDeleteOnClick(Control) {
 
	var CashflowDataset = CashflowGridArea.CashflowDataset;
	 ContactID = Connector.CurrentUser.ContactID ;
	 	 CashflowDataset.Open();
	   StatusID = CashflowDataset.ValAsGUID('StatusID');
	  CashflowDataset.Close();
 
	 //если статус операции = выполнена, а изменяющий не из группы упр.фин, то не удалять
     	if (StatusID=='{FDEA47BE-53FE-4730-BF4F-4F44C3B5D61A}'){
     	if (ContactID != '{32E85247-DE68-4C6A-B032-06F5CDFB2B3A}'  &&
		 ContactID != '{E2093D56-49ED-4D4D-B69E-C9FF0623DE95}'  &&
		 ContactID != '{ED75744C-BC2C-469C-8993-8473E23E7F06}'  &&
		 ContactID != '{14925854-AAFD-4516-8A23-F2B5312F4C63}'){
     	ShowInformationDialog('У Вас нет прав удалять выполненную операцию НОВОЕ');
     	return;
     				  }   else  scr_BaseGridArea.btnDeleteOnClick(Control);
 
	} else 	scr_BaseGridArea.btnDeleteOnClick(Control);
 
} 

Поэтому возвращаюсь к первоначальному вопросу - нельзя ли все-таки как-то правильно использовать событие OnDatasetBeforeDelete для ds_Cashflow в скрипте ds_CashflowScript?

Т.к. везде по системе для отображения операций используется грид wnd_CashflowGridArea (за возможно единственным исключением - окном выбора wnd_SelectData), то используйте свой код с первого же вашего поста, только на событии

У меня только в скрипте wnd_CashflowGridAreaScript есть функция с DoDelete, называется function dlDataOnDatasetBeforeDelete(Dataset, DoDelete).

и не забудьте дописать

DoDelete.Value = false; 

Если это критично, и хочется точно знать что лазеек в безопасности нет, ни от куда нельзя будет удалить операцию - пишите триггер на таблицу.

Дмитрий, не поверите, подправила функцию

function dlDataOnDatasetBeforeDelete(Dataset, DoDelete) {
	 ContactID = Connector.CurrentUser.ContactID ;
      StatusID = Dataset.ValAsGUID('StatusID');
        if (StatusID=='{FDEA47BE-53FE-4730-BF4F-4F44C3B5D61A}'){
           if (ContactID != '{32E85247-DE68-4C6A-B032-06F5CDFB2B3A}'  &&
                 ContactID != '{E2093D56-49ED-4D4D-B69E-C9FF0623DE95}'  &&
                 ContactID != '{ED75744C-BC2C-469C-8993-8473E23E7F06}'  &&
                 ContactID != '{14925854-AAFD-4516-8A23-F2B5312F4C63}'){
                 ShowInformationDialog('У Вас нет прав удалять выполненную операцию');
                  DoDelete.Value=false;
 
 
                  }
            }
}

Сообщение появляется, когда положено, но операция все равно УДАЛЯЕТСЯ.
Все так же как и было изначально. :(
Зато работает быстро.

Виктория, это какая-то фантастика - никогда подобного не встречал. Вы обновлялись до последней версии бинарников? Попробуйте, как минимум на 3.3.2.311.

Виктория,

действительно, проверьте на последней версии бинарных файлов (3.3.2.311).

Как я писал ранее, оба варианта на версии бинарных файлов 3.3.2.311 и конфигурации версии 3.3.2.130 работают корректно.

Если обновление не сработает, пришлите на support@terrasoft.ru копию базы, - посмотрим.

Обновление на новые бинарные файлы помогло!

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

Добрый день!
Почему-то не не обновляется объект IDataGrid.
Смотрю ActiveView.Items(1).DataField.DisplayValue там одно значение, а реально на экране совсем другое. Особенность грида в том что в DatasetLink указан редактируемый MemoryDataset, в котором так же установлено нормальное значение.
Обновляться прекращает после того как в форме нажимаешь ОК (не важно редактировался ли MemoryDataset). После чего всегда визуально отображается содержимое формы по которой был клик ОК, не смотря на то что открывается совсем другое содержимое...

Нравится

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

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

DataGrid.DatasetLink.Dataset.DataFields('ID').Value;

И заметьте, т.к. это редактируемый грид, значение в DataField'е обновится только при выходе из режима редактирования ячейки (либо после нажатия на Enter)

"Олейник Дмитрий" написал:Ммм, обращаться к активной (текущей выделенной) строке нужно так:

DataGrid.DatasetLink.Dataset.DataFields('ID').Value;

Дмитрий, добрый день! Возможно я "ммм" не очень понятно пишу, но еще раз подчеркну

"AlexLS" написал:в DatasetLink указан редактируемый MemoryDataset, в котором так же установлено нормальное значение

"Олейник Дмитрий" написал:при выходе из режима редактирования ячейки

никакого редактирования не происходило, только открытие формы в которой в датасете и в гриде дебаггер показывает правильные значения а "глаза" видят значения из той формы в которой последний раз был апдейт/инсерт!

Алекс, добрый день.

Все еще не понял про "нормальные значения".

Ни разу не сталкивался с таким поведением. Грид не кеширует значения датасета. Мне кажется чтото не так с датасетом, привязанным к гриду, вернее скорее всего датасет, в котором корректные значения не привязан к гриду. Можете написать кейс воспроизведения, при котором грид визуально отображает данные старой (насколько я понял) формы?
Я также, желательно, приведите код заполнения memoryDataset'a.

В GridAreaOnPrepare пишу:

if (Assigned(ParentDataset = GetAttribute(Window, 'ParentDataset'))	) 
  if (!IsEmptyValue(IsInserting = ParentDataset.State == dstInsert)) {
    var ds = Services.GetNewItemByUSI(IsInserting ? 
                'ds_RequestStockTypeMec' : 'ds_TypeMecDetail');
      if(!IsInserting) 
        ApplyDatasetFilter(ds, 'RequestStock2ID', ParentDataset('ID'),true);
      ds.Close();
      ds.Open();
      mdsTypeMecDetail.Dataset.Close();
      CopyMemoryDataset(ds, mdsTypeMecDetail.Dataset);
      if (Assigned(grdData))
      if(grdData.DatasetLink.Name != 'mdsTypeMecDetail')
      grdData.DatasetLink = mdsTypeMecDetail;
      ds.Close();
 
  }

где ds_TypeMecDetail - штатный датасет для данной детали,
ds_RequestStockTypeMec - датасет справочника, из которого подгружаются данные при Append
mdsTypeMecDetail - собственно меморидатасет, который и нужно заполнять в карточке
ParentDataset - датасет карточки редактирования записи, в которой и находится windowcontainer с гридом.

Проблема скорее всего тут:

grdData.DatasetLink = mdsTypeMecDetail;

Лучше всего сначала очистить, а затем заполнить свойство DatasetLink:

grdData.DatasetLink = System.EmptyValue;
grdData.DatasetLink = mdsTypeMecDetail;

А затем нужно открыть mds:

grdData.DatasetLink = System.EmptyValue;
grdData.DatasetLink = mdsTypeMecDetail;
grdData.DatasetLink.Dataset.Open();

И определить событие для DatasetLink(mdsTypeMecDetail): OnDatasetAfterOpen(Dataset), и уже внутри него заполнять mds. Тогда все будет корректно.

function dlDataOnDatasetAfterOpen(Dataset) {
Dataset.Append();
....
Dataset.Post();
}

Также не понял, почему используется GridAreaOnPrepare... Но тут нужно знать вашу бизнес задачу.

"Олейник Дмитрий" написал:Также не понял, почему используется GridAreaOnPrepare... Но тут нужно знать вашу бизнес задачу.

Исходно нужно реализовать "множественный выбор" (то есть все значения из справочника с галочками... очень похоже на деталь Доступ :smile: только в карточке редактирования).
Уже работающую деталь потребовалось положить в эдитвиндоу. Пытался "малой кровью" эту деталь использовать с подменой обычного датасета на меморидатасет.

Ага, я когда-то чтото подобное делал с напоминаниями: открывается каточка детали "напоминания" какого либо раздела, там есть лукапное поле контакт, жмем на лупу и открывается multi_select_window - можно выбрать несоклько контактов, а затем при закрытии краточки - на деталь добавлялось столько записей, сколько выбрано было контактов.
Посмотрите, может быть поможет:

http://www.community.terrasoft.ru/blogs/8462

Здорово получается, до инсерта/апдейта

grdData.DatasetLink.Dataset.Open();

вызывает memoryDatasetOnDatasetAfterOpen
а после апдейта .Open() уже игнорирует AfterOpen!

[quote="Олейник Дмитрий"]Ага, я когда-то чтото подобное делал с напоминаниями: открывается каточка детали "напоминания" какого либо раздела, там есть лукапное поле контакт, жмем на лупу и открывается multi_select_window - можно выбрать несоклько контактов, а затем при закрытии краточки - на деталь добавлялось столько записей, сколько выбрано было контактов.
Посмотрите, может быть поможет:

http://www.community.terrasoft.ru/blogs/8462[/quote]
Спасибо, Дмитрий! Попробую поразбираться.

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

Alex, а что значит Update?

Немного про мемориDS: это штука живет от Open() до Close(); Заполняется на OnDatasetAfterOpen(); После открытия датасета, можно позиционироваться на любой строке,и изменять любую ячейку:
Dataset.Edit();
Dataset.Post();
Все изменения грид перерисует самостоятельно.
Помните, что каждый рефреш, (Dataset.Close(), Dataset.Open()) вызывает перезаполнение датасета. Т.е. если вы не сохранили измененные данные куда то в таблицу БД, с которой заполняете датасет, то он заполнится старыми значениями.

"Олейник Дмитрий" написал:

Alex, а что значит Update?

Немного про мемориDS: это штука живет от Open() до Close(); Заполняется на OnDatasetAfterOpen(); После открытия датасета, можно позиционироваться на любой строке,и изменять любую ячейку:

Dataset.Edit();

Dataset.Post();

Все изменения грид перерисует самостоятельно.

Помните, что каждый рефреш, (Dataset.Close(), Dataset.Open()) вызывает перезаполнение датасета. Т.е. если вы не сохранили измененные данные куда то в таблицу БД, с которой заполняете датасет, то он заполнится старыми значениями.

С уважением,

Олейник Дмитрий


В какой-то момент меморидатасет перестает реагировать на Dataset.Close() и Dataset.Open().
Пишу

grdData.DatasetLink = System.EmptyValue;
grdData.DatasetLink = mdsRStock2AccDivision;
grdData.DatasetLink.Dataset.Close();
SetAttribute(grdData.DatasetLink.Dataset, 'ParentWindow', Window);
if (Assigned(ParentDataset = GetAttribute(Window, 'ParentDataset'))	)		SetAttribute(grdData.DatasetLink.Dataset, 'ParentDataset', ParentDataset);
grdData.DatasetLink.Dataset.Open();

но ни
function mds_RStock2AccDivisionOnDatasetAfterOpen
ни
function mds_RStock2AccDivisionOnDatasetAfterClose

не срабатывают!

наконец нашел свой косяк:
в посте (записи из мемдатасета в реальный датасет) для мемдатасета делал DisableEvents(), а EnableEvents() забыл сделать.... теперь все обновляется!

Только почему-то если

Dataset.EnableEvents();
Dataset.Close();

то возникает ошибка:

TSObjectLibrary.MemoryDataset: Cannot focus a disabled or invisible window

а

Dataset.Close();
Dataset.EnableEvents();

ошибку не вызывает

Проверьте состояние датасета в момент Open() - быть может он уже в состоянии Open(), тогда функция не вызывается и события AfterOpen() не тригернутся.

Alex, да, бывает :)

"Олейник Дмитрий" написал:

Alex, да, бывает :)

С уважением,

Олейник Дмитрий


Дмитрий, спасибо Вам за помощь!

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

Добрый день!

По-умолчанию в Террасофте настроены права доступа на таблицу Контактов таким образом, что редактировать контакт может либо ответственный, либо админ.

У нас возникла необходимость в таблицу Контакты добавить несколько полей, НО чтобы их могли редактировать все пользователи.

Т.е. на эти новые поля права для всех, а на все остальные - по-умолчанию.

Подскажите, пожалуйста, как лучше это реализовать?

Заранее спасибо!

Нравится

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

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

Добрый день!

Для настройки прав доступа к колонкам, Вам необходимо перейти в Админитсрирование: Доступ к объектам, найти объект "Контакт", и включить для него администрирование по колонкам. После чего найти необходимую Вам колонку и добавить для нее пользователей и уровни доступа для этих пользователей, как показано на скриншоте.

Александр, спасибо за ответ, но у меня не Online, у меня нет "Доступ к объектам".

Алексей, настроить права доступа таким образом невозможно, так как иерархически доступ к объекту "Контакт" находится выше чем к полю.

Понятно, спасибо!

Я сделал следующим образом:

  • добавил еще одну таблицу, в которой хранятся те самые несколько полей, которые должны быть общими для всех, связанными с таблицей контактов по ID контакта,
  • при открытии карточки контакта идет считывание в поля (поля обычные, не data),
  • при сохранении проверяется нет ли уже записи для этого контакта, и либо Edit(), либо Append()

единственный "косяк" в том, что пришлось сделать дополнительную кнопку для сохранения этих данных, т.к. кнопка "Ок" так же завязана на права.

Можете прокомментировать такое решение?
Спасибо!

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

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

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

Приветствую, коллеги:)
Один простой(может и не очень вопрос).
В DataGrid выведено для показа единственное вычисляемое поле, которое в свою очередь собирается из отдельных полей датасета, ну например:
Dataset('CalcField') = Dataset(Name) + ' получил следующий бонус: ' + Dataset(Bonus);
Соответственно колонки Name и Bonus - значения из таблицы.

Вопрос:
Могу ли я раскрасить это поле не целиком на событии OnGetRowDrawInfo, а отдельно в этом поле для Dataset(Name) присвоить скажем зеленый цвет, для Dataset('Bonus') присвоить красный.
Чтобы получилось так что в одном выводимом вычисляемом поле использовались бы разные цвета?

Нравится

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

Половину ячейки в гриде одним цветом, половину другим вы точно не сделаете.
Делайте два поля и красьте каждую ячейку отдельно если версия TS 3.2 и выше

"Александр Кудряшов" написал:

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

Делайте два поля и красьте каждую ячейку отдельно если версия TS 3.2 и выше


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

"Нестеров Артем Валерьевич" написал:Вопрос был конкретный, предложения реорганизовать отображение данных не интересны.

"Александр Кудряшов" написал:Половину ячейки в гриде одним цветом, половину другим вы точно не сделаете.

Конкретизирую - раздельной окраски насколько мне известно нет.
Нижайший поклон и прошу покорнейше простить что ответил не то. Мне стыдно

"Александр Кудряшов" написал:Могу ли я раскрасить это поле не целиком на событии OnGetRowDrawInfo, а отдельно в этом поле для Dataset(Name) присвоить скажем зеленый цвет, для Dataset('Bonus') присвоить красный.

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

"Бондарь Наталия" написал:
Александр Кудряшов пишет:

Могу ли я раскрасить это поле не целиком на событии OnGetRowDrawInfo, а отдельно в этом поле для Dataset(Name) присвоить скажем зеленый цвет, для Dataset('Bonus') присвоить красный.

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


Благодарю, это и хотел услышать, функциональные возможности интересовали.
Спасибо всем, тема закрыта.

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

Добрый день

В окне есть DataGrid, привязанный к набору данных A. Под DataGrid'ом есть WindowContainer внутри которого есть DataGrid привязанный к набору данных B.
Набор данных B основан на мудрёном запросе на выборку, основанном на приличном количестве таблиц.

Прошу пояснить как осуществляется процесс добавления записи из набора A в набор B и отобразить сие действо в DataGrid'е окна контейнера.

Делаю следующие шаги.
1. Беру запись из набора A
2. Беру набор B (

var B = Services.GetNewItemByUSI('B');
)
3. Определяю поля и значения
var Fields = new Array(...);
var Values = new Array(...);

4. Использую
AppendRecordInDataset(B, Fields, Values, true);

Прошу помощи в понимании данного процесса.

1. Добавление из A в B.
2. Обновление DataGrid для отображения того что добавили.

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

Нравится

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

Как я полагаю, надо слать Notify. Вопрос чему - Окну (и там проводить обработку) или Набору данных?

Если есть наборы данных А и В, привязанные через DatasetLink к соответствующим гридам и необходимо скопировать выделенную запись из А в В, то нужно:
var Fields = new Array(имена полей, которые копируем);
var Values = new Array(значение из набора данных А (например, А('Поле1') и т.д.));
AppendRecordInDataset(B, Fields, Values, true);
B.RefreshRecord(B('ID'), true);

Если наборы данных находятся в разных окнах, то надо слать Notify окну, в котором В, а в качестве данных передать набор данных А. Функии копирования поместить в обработку OnNotify окна с В.

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

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

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

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

Создал функцию, которая считает сумму счетов по продаже и пишет в Продажу в колонку AmountPaid полученную сумму:

 var Dataset = dlData.Dataset;
       Dataset.Open();
 ...
SumAmount = SumAmount + Amount;
}
  if (SumAmount!=Dataset.Values('AmountPaid')){
              Dataset.Edit();
              Dataset.Values('AmountPaid') = SumAmount;
              Dataset.Post();
              Dataset.Close();
              }
              InvoiceDataset.Close();

Подцепляю эту функцию на событие dlDataOnDatasetAfterOpen для wnd_OpportunityEditScript. Все работает, но я уверен, что такой подход в не совсем правильный, начиная от места расположения функции, заканчивая событием. Т.к. к примеру подсчет и запись происходит в момент открытия окна карточки Продажи. По правильному нужно, чтобы при изменении в Счете (Деталь Продаж) Состояния оплаты на Оплачен на это событие обновлялось значение "СуммаОплаченныхСчетов" в Продаже и визуально это сразу видно было бы в Гриде Продаж.
Подскажите как это реализовать правильно или может где есть хороший пример реализации такой связки?

Нравится

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

Я считаю, что правильно было бы вообще реализовать это уже в запросе, а не в датасете.
Скрины в приложении (по пунктам)

0) (Опасно, сделайте бекапы) Удалить все, что связано с этим полем (все скрипты с ним, удалить из карточки, грида, датасета, запроса, таблицы)
Или просто будем работать с другим именем поля. Например, PaidAmount.

1) Зайти в sq_Opportunities, добавить колонку подзапроса

2) Меняем имя колонки на нужное нам AmountPaid

3) В подзапросе выбираем таблицу tbl_invoice, называем ее как InvoicePaid

4) В select в подзапросе выбираем Amount или BasicAMount (смотря, что по душе. Я взял BasicAmount) и говорим, что нам нужна его сумма (ставим в поле итог Сумма)

5) Необходимо наложить фильтры
5.1) Фильтр связи продажи с счетом
В подзапросе в Where создать фильтр сравнения, назовем InvoicePaidOpportunityID
Слева в фильтре InvoicePaid.OpportunityID, справа Opportunity.ID
Включить его (не забыть поставить галочку)
5.2) Счета должны быть оплачены
В подзапросе присоединиям таблицу BillStatusID к таблице InvoicePaid
Назовем ее InvoicePaidBillStatus (Остальные параметры смотри на скриншоте 5_2_1)
В подзапросе в Where создать пользовательский SQL фильтр, назвать InvoicePaidBillStatusIsFinish, внизу объеденить с таблицей tbl_BillStatus AS InvociePaidBillStatus, в самом запросе написать 'InvoicePaidBillStatus.IsFinish = 1', не забыть включить фильтр

Все теперь сам запрос вычисляем нам это поле, теперь с ним можно работать как с обычным. Зайти в датасет добавить дробное поле, указать AmountPaid, лучше поставить ему свойство только для чтения, добавить в реестр, в карточку и т.д.

"Сазанов Александр Владимирович" написал:Я считаю, что правильно было бы вообще реализовать это уже в запросе, а не в датасете.
Скрины в приложении (по пунктам)

А не будет ли это очень долго считаться, когда колонка будет отображаться в grid'e?

Спасибо Александр! Работает! Ещё бы где-то рефреш добавить, чтобы в гриде сразу видно было обновление, а не после нажатия на кнопку обновить.
И ещё, где мы наш псевдомин в запросе AmountPaid привязываем к реальному столбцу в таблице?

Если делаете в конфигурации, то лучше всего создать специальный UpdateQuery сервис, который будет выполнять апдейт таблицы "Продажи" (в вашем случае), а затем выполнить его:

var myUQ = Services.GetNewItemByUSI('uq_UpdateOpportunitySumm');
SetParameterValue(myUQ.Parameters, 'OpportunityID', OpportunityID);
SetPArameterValue(myUQ.Parameters, 'OpportunitySumm', Summ);
myUQ.Execute();

Также перед этим, рекомендую еще и написать отдельный SelectQuery, который будет получать сумму по всем счетам продажи (чтобы не работать с датасетом, избежать циклов while и пр., иначе в будущем столкнотесь с проблемой быстродействия). Т.е. в SQ добавить одну колонку с агрегирующей функцией суммы по нужной колонке, и с фильтром сравнения по OpportunityID.

var mySQ = Services.GetNewItemByUSI('sq_GetInvoiceSumm');
SetParameterValue(mySQ.Parameters, 'OpportunityID', OpportunityID);
var DS = sq.Open();
var InvoiceSumm = DS('InvoiceSumm');
DS.Close();

"Владимир Соколов" написал:

А не будет ли это очень долго считаться, когда колонка будет отображаться в grid'e?


Когда отображаться не думаю, в гриде обычно по 40 записей на странице. По-моему Террасофт только их в запрос и берет. А вот когда в скрипте открываем весь датасет, то может быть.
Ну точно узнаем, если попросим Алекса замерить запросы с этим полем и без него.

"Alex GF" написал:

Спасибо Александр! Работает! Ещё бы где-то рефреш добавить, чтобы в гриде сразу видно было обновление, а не после нажатия на кнопку обновить.

И ещё, где мы наш псевдомин в запросе AmountPaid привязываем к реальному столбцу в таблице?


В том-то и дело, что в таблице его нет, он вычисляется в запросе, поэтому не нужно никуда вешать никакие скрипты для его вычисления. А то пришлось бы вешать скрипты на Счета на AfterPost и BeforeDelete или AfterDelete

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

Здравствуйте!
В Продаже в контекстном меню выполняю «Создать счет». Окно «Счет» висит секунд 30 (не отвечает), затем отвисает.
Тоже самое, если из Продаж создать Договор.

Дебаггером дохожу до функции

function RefreshDataset(Dataset) {
       CheckAssigned(Dataset, 'Dataset');
       Dataset.Close();
       Dataset.Open(); // Здесь 30 секунд висим
}

Профайлер показывает, что на Dataset.Open(); выполняется Select sq_Opportunity за 3 секунды.
И далее в течение 30 секунд висим и затем в профайлере Select Opportunity ID ….
После этого окно Счет отвисает.

Как можно проанализировать, что происходит в Dataset.Open();, чтобы понять причину зависания?
И в чем может быть причина?

Нравится

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

Есть еще события OnDatasetBeforeOpen(Dataset), и OnDatasetAfterOpen(Dataset) - скорее всего там накручена какая-то логика, которая выполняется слишком долго. Установите в этих событиях отладчик, либо на строке Dataset.Open() нажмите F11 (зайти внутрь) чтобы понять что происходит в фоне.

Установил на эти события (wnd_InvoiceEditScript) отладчик, а также на строке Dataset.Open() нажимал F11.
Дальше строки Dataset.Open() не проходит. Сразу после Dataset.Open() выходим на окно Счет, где висим секунд 30, затем отвисаем. Что ещё может быть?

Alex, посмотрите профайлером какой запрос идет в БД и выполните его в Management Studio.
Как долго выполняется запрос?

"Бондарь Наталия" написал:Alex, посмотрите профайлером какой запрос идет в БД и выполните его в Management Studio.
Как долго выполняется запрос?

В заглавном сообщение писал про Профайлер:

"Профайлер показывает, что на Dataset.Open(); выполняется Select sq_Opportunity за 3 секунды.
И далее в течение 30 секунд висим и затем в профайлере Select Opportunity ID …."
В Management Studio он также выполняется за 3 сек.

Также отладчик ставил на ds_Opportunity (OnDatasetBeforeOpen(Dataset), и OnDatasetAfterOpen(Dataset)). (Пробовал ставить отладчик на все функции в ds_OpportunityScript, но не в одну из функций не заходит).

После 30 секундного зависания переходит на ds_OpportunityOnDatasetAfterOpen.

Что происходит в эти 30 секунд скрыто в Dataset.Open() куда не получается войти.

Сам метод Open() не тяжелый. Чтобы проверить, что происходит в методе, необходимо отлаживать исходники приложения, что по понятным причинам Вы сделать не сможете.

"Alex GF" написал:(Пробовал ставить отладчик на все функции в ds_OpportunityScript, но не в одну из функций не заходит)

Вот это самое странное, в базовой реализации обработка присутствует и, если Вы отлаживаетесь в студии, то по F11 должны были попасть на стандартный AfterOpen.
Проверьте наличие подписки на события в ds_Opportunity. Если с этим все в порядке, то останется только передать в поддержку обезличенную копию БД для анализа.

"Maxim Gritsenko" написал:
Проверьте наличие подписки на события в ds_Opportunity. Если с этим все в порядке, то останется только передать в поддержку обезличенную копию БД для анализа.

Удалось ли что-нибудь выяснить по поводу зависания? Обезличенная БД в начале сентября была передана в техподдержку.

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

Максим, скажите пожалуйста, какой именно запрос Вы запускали в Студии?
У меня на рабочей базе (там где виснет 30 сек при создании Счета или Договора) запрос sq_Opportunity в Студии выполняется за 2 сек.
Сами продажи в модуле Продажи работают без зависания.

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

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

Получили ответ от коллег из департамента разработки, для решения данного вопроса, необходимо в сервисе wnd_InvoiceEditScript, в функции Initialize заменить:

if (IsAppend) {
                               var OpportunityID = Dataset.Values('OpportunityID');
                               if (OpportunityID) {
                                               var LookupDataset =
                                                               Dataset.DataFields('OpportunityID').LookupDataset;
                                               RefreshDataset(LookupDataset);
                                               LookupDataset.Locate('ID', OpportunityID);
                                               Dataset.Values('OpportunityID') = OpportunityID;
                               }
                }
На
                if (IsAppend) {
                               var OpportunityID = Dataset.Values('OpportunityID');
                               if (OpportunityID) {
                                               var LookupDataset =
                                                               Dataset.DataFields('OpportunityID').LookupDataset;
                                               ApplyDatasetIDFilter(LookupDataset, OpportunityID, true);
                                               RefreshDataset(LookupDataset);
                                               ApplyDatasetIDFilter(LookupDataset, null, false);
                                               Dataset.Values('OpportunityID') = OpportunityID;
                               }
                }

Спасибо, помогло!

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

Есть функция проверки валидности email:

function CheckCommunications(Dataset) {
   var Result = false;
   var emailRegEx = new RegExp("^[_\.0-9a-zA-Z-]+@([0-9a-zA-Z][0-9a-zA-Z-]+\.)+[a-zA-Z]{2,6}$");
       if ((Dataset('Communication1TypeID') == '{7A628D16-D7D0-4979-B8BA-B64EF54A0366}') && !IsEmptyValue(Dataset('Communication1'))) {
           var emailStr=Dataset.Values('Communication1');
           Result = emailRegEx.test(emailStr);
        }
      if (!Result) {
      ShowErrorDialog('Проверьте правильность заполнения поля email!');
      return;
      }
     
}

Подскажите на каком событии её корректно вызывать перед записью в базу?
Пробовал на btnOKOnClick
function btnOKOnClick(Control) {
        if (!CheckAccountData()) {
            return;
        }
        scr_BaseDBEdit.btnOKOnClick(Control);

    var Dataset = dlData.Dataset;               // Validation email
    Dataset.Close();
    Dataset.Open();
        CheckCommunications(Dataset);  
        //Dataset.Close();                            //&

}
, но отрабатывает некорректно - проверять проверяет, но пишет в базу не валидный email.

Нравится

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

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

Данные то сохраняются раньше на строке

scr_BaseDBEdit.btnOKOnClick(Control);

Поэтому просто переделайте обе функции:

function btnOKOnClick(Control) { 
        if (!CheckAccountData()) {
            return;
        }
        if(!CheckCommunications(dlData.Dataset)) {
            return;
        }
        scr_BaseDBEdit.btnOKOnClick(Control);
}
 
function CheckCommunications(Dataset) {
   var Result = false;
   var emailRegEx = new RegExp("^[_\.0-9a-zA-Z-]+@([0-9a-zA-Z][0-9a-zA-Z-]+\.)+[a-zA-Z]{2,6}$"); 
       if ((Dataset('Communication1TypeID') == '{7A628D16-D7D0-4979-B8BA-B64EF54A0366}') && !IsEmptyValue(Dataset('Communication1'))) {
           var emailStr=Dataset.Values('Communication1');
           Result = emailRegEx.test(emailStr);
        } 
      if (!Result) {
      ShowErrorDialog('Проверьте правильность заполнения поля email!');      
      }
      return Result;      
}
Показать все комментарии

Подскажите пожалуйста, как можно обнулить датасет в следующем случае:

function функция(StDataset, ParentWindow)
var Dataset             = GetOpenedDatasetByUSIWithFilter(DatasetUSI, FilterName, ParamValue, UniqueCode, DisableFieldsArray, DoDisableEvents);

        var Stop        = Dataset('Stop');                                     
       
    if (!IsZeroValue(Stop))  {
               
                        MessageBox(Стоит галка Стоп. Запись создана не будет!');
//Здесь перед выходом пробую очистить датасет, но не получается
                        Dataset.DisableEvents();
                        Dataset.Close();
                        return;
                       
                }

        for(Dataset.GotoFirst(), Counter = 0; ( !Dataset.IsEOF ); Counter++, Dataset.GotoNext()) {
.....
Dataset.Edit();
.....


 }
Dataset.Close();
}

Если Stop > 1, то вычисляем какие-то данные и заносим в БД.
Если Stop ==1, то выходим из функции, при этом очищаем датасет.

В случае Stop > 1, все нормально, данные пишутся в БД и при повторном вызове функции дата сет отрабатывает выделенную запись.
Но, если Stop ==1, т.е. мы ничего не хотим писать в БД и просто выйти с очисткой датасета.

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

Нравится

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

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

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

Алекс, а при отладке перед return какой Dataset,RecordsCount? Не очищается?

"Maxim Gritsenko" написал:

Алекс, а при отладке перед return какой Dataset,RecordsCount? Не очищается?


Не очищается. В датасете те строки, в которых при предыдущих вызовах был Stop ==1 и наша текущая строка. Если мы насильно удалим из базы эти строки (где был Stop ==1) или в этих строках сделаем Stop = 0, то из датасета они пропадают.

Что значит очистить датасет? Заполнить все колонки, кроме ID значением null? Удалить запись? Не совсем понятно... Опишите, пожалуйста, Вашу бизнес задачу.

Если у вас точно известно, что

var Dataset             = GetOpenedDatasetByUSIWithFilter(DatasetUSI, FilterName, ParamValue, UniqueCode, DisableFieldsArray, DoDisableEvents);

вернет одну строку (к примеру вы фильтруете по ID), то пишем просто:

if(!Stop) {
  //пишем данные
}

если это точно не известно то в цикле:

while(!Dataset.IsEOF) {
          var Stop = Dataset('Stop');
          if(!Stop) {
          Dataset.Edit();
          ...
          Dataset.Post();
          }
          Dataset.GotoNext();
}

пс: мне кажется, что функция GetOpenedDatasetByUSIWithFilter возвращает старый экземпляр датасета. Может поможет если напишите явно?

var Dataset = Services.GetNewItemByUSI('ds_Stop');
EnableDatasetFilters(Dataset, false);
ApplyDatasetFilter(Dataset, 'FilterName', FilterValue, true);
Dataset.Open();
....

"Олейник Дмитрий" написал:

пс: мне кажется, что функция GetOpenedDatasetByUSIWithFilter возвращает старый экземпляр датасета. Может поможет если напишите явно?

var Dataset = Services.GetNewItemByUSI('ds_Stop');

EnableDatasetFilters(Dataset, false);

ApplyDatasetFilter(Dataset, 'FilterName', FilterValue, true);

Dataset.Open();

....

С уважением,

Олейник Дмитрий

На GetOpenedDatasetByUSIWithFilter очень много завязано, поэтому поменять на GetNewItemByUSI просто не получится.
Вот последовательность действий:
Если на выделенной строке (ID=A1) в мастере Stop <> 1, то пишутся в базу некие данные в деталь. Датасет А1.
Если на выделенной строке (ID=A2) в мастере Stop <> 1, то пишутся в базу некие данные в деталь. Датасет А2.
Если на выделенной строке (ID=A3) в мастере Stop == 1, то НЕ пишем в базу данные в деталь и показываем окно-предупреждение, что Stop == 1 и ничего нельзя писать в БД. Датасет А3.
Если на выделенной строке (ID=A4) в мастере Stop <> 1, то пишутся в базу некие данные в деталь. При этом dataset обращается к ID = A3, обрабатывает, затем к A4. Т.е. A3 сохраняется в датасете. Датасет А3, А4.
Если же теперь мы в A3 сделаем Stop <> 1 и отработаем эту строку. То она исчезает из дата сета. Датасет А3.
Если на выделенной строке (ID=A5) в мастере Stop <> 1, то пишутся в базу некие данные в деталь. Датасет А5.
Есть ли какой-нибудь способ принудительной очистки данного датасета в этом случае?
Пробовал вариант:

if(!Stop) {
  //пишем данные
}

Но то же самое.

Правильно ли я понял, что выделенная строка с разными ID - это строки, которые попали в датасет после его открытия через GetOpenedDatasetByUSIWithFilter ?
Если так, то по этому датасету вы ходите в цикле и при определенных условиях что-то пишете в детали? Каким образом с 4-ой строки вы перемещаетесь на 3-ую, и снова на 4-ую?

Если же теперь мы в A3 сделаем Stop <> 1

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

В общем, все-ровно не понял как это все у вас устроено. Можете прикрепить весь свой код сюда полностью, а также объясните, пожалуйста, все таки: все эти строки обрабатываются в одном цикле, или эта функция вызывается несколько раз? Если несколько - то с какими параметрами, и меняете ли Вы uniqueCode для датасета? Сбрасываются ли фильтра?

Более подробно:
Выбираю строку в гриде. В контекстном меню - Действия - ВыполнитьДействие. Попадаем сюда:

function amiOnExecute(ActionMenuItem, Sender) {
	var OpDataset  = dlOp.Dataset;
//Уже сюда вынес проверку на Stop
	var Stop = OpDataset('Stop'); 
	    if (!IsZeroValue(Stop)) {
	    MessageBox('Стоит галка Stop ! Ничего в базу не пишем!');
//Здесь пытаюсь очистить датасет, как-будто я не выбирал строку в гриде (но не чистит)
	    OpDataset.Close();
	    OpDataset.Open();
	    return false;
	    }
 
	var Result = hConfirmation(OpDataset, OpWorkspace.COpWindow);
}

Далее следующая функция для проверки:

function hConfirmation(OpDataset, ParentWindow) 
 
{
	var Message = FormatStr('Вы действительно хотите ...' , 
					GetSystemParameterValueEx('ToPay', true));
	if (ShowConfirmationDialog(Message) != wmrYes) {
		return;
	}
 
 
	finalEv(OpDataset, ParentWindow);	
}

И наша финальная функция, которая отрабатывает и пишет в базу:

function finalEv(OpDataset, ParentWindow) {
var DatasetUSI	= 'ds_COp';
var Dataset		= GetOpenedDatasetByUSIWithFilter(DatasetUSI,	'CanEv', true, 'finalEv', null, true);
....
//Вот в этом цикле пишутся данные в БД. 
for(Dataset.GotoFirst(), Counter = 0; ( !Dataset.IsEOF ); Counter++, Dataset.GotoNext()) {
.....
Dataset.Edit();
.....
 
 }
Dataset.Close();
 
}

Хотелось бы, если Stop ==1, то выходим из функции, при этом очищаем датасет.
Судя по всему GetOpenedDatasetByUSIWithFilter запоминает строку (OLD), когда мы отрабатывали:

function amiOnExecute(ActionMenuItem, Sender) {
        var OpDataset  = dlOp.Dataset;
//Уже сюда вынес проверку на Stop
        var Stop = OpDataset('Stop'); 
            if (!IsZeroValue(Stop)) {
            MessageBox('Стоит галка Stop ! Ничего в базу не пишем!');
//Здесь пытаюсь очистить датасет, как-будто я не выбирал строку в гриде (но не чистит)
            OpDataset.Close();
            OpDataset.Open();
            return false;
            }
 
        var Result = hConfirmation(OpDataset, OpWorkspace.COpWindow);
}

и при выполнения Действия над другой новой (new) строкой (где Stop <>1) сначала выполняется обработка этой new строки (мы её просто выбираем в гриде), а затем тут же отрабатывается наша OLD строка.
Как очистить датасет от этой (этих) OLD строк, как будто их вообще небыло?
Это
OpDataset.Close();
OpDataset.Open();
не помогает.
Что-то такое
OpDataset.Clear();
не поддерживается?

function amiOnExecute(ActionMenuItem, Sender) {
        var OpDataset  = dlOp.Dataset;
        var Stop = OpDataset('Stop'); 
        if (!IsZeroValue(Stop)) {
            MessageBox('Стоит галка Stop ! Ничего в базу не пишем!');            
            return false;
        }
        var Result = hConfirmation(OpDataset, OpWorkspace.COpWindow);
}

Переоткрывать датасет

OpDataset.Close();
OpDataset.Open();

не имеет смысла, т.к. при открытии вы всегда будете позиционированы на первой строке. Т.е.

function Main() {	
	var Dataset = Services.GetNewItemByUSI('ds_CommunicationType');
	Dataset.Open();
        //Dataset.RecordNumber == 1;
	Dataset.GotoNext();
        //Dataset.RecordNumber == 2;
	Dataset.Close();
	Dataset.Open();
        //Dataset.RecordNumber == 1; 
}

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

Таким образом, что мы имеем:

первую функцию-действие лучше переписать так:

function amiOnExecute(ActionMenuItem, Sender) {
        var OpDataset  = dlOp.Dataset;
        var Stop = OpDataset('Stop'); 
        if (!IsZeroValue(Stop)) {
            MessageBox('Стоит галка Stop ! Ничего в базу не пишем!');            
            return false;
        }
        var Result = hConfirmation(OpDataset, OpWorkspace.COpWindow);
}

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

Вторая функция остается без изменений:

function hConfirmation(OpDataset, ParentWindow) 
 
{
        var Message = FormatStr('Вы действительно хотите ...' , 
                                        GetSystemParameterValueEx('ToPay', true));
        if (ShowConfirmationDialog(Message) != wmrYes) {
                return;
        }
 
 
        finalEv(OpDataset, ParentWindow);       
}

а вот с третьей непонятно. Самое интересное то, что вы ей передаете датасет реестра (OpDataset), который позиционирован на нужной строке (т.е. та в которой стоп = 0), а также ParentWidnow, но при этом нигде их не используете. Покажите как Вы его используете, потому что в текущем примере OLD строка никак не может быть обработана, если вы не выполняете никаких манипуляций , которые приводят к смене текущей активной строки, что-то вроде этого:

OpDataset.GotoNext();
OppDataset.Close();
OppDataset.Open();

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

function amiOnExecute(ActionMenuItem, Sender) {
        var OpDataset  = dlOp.Dataset;
        var Stop = OpDataset('Stop'); 
        if (!IsZeroValue(Stop)) {
            MessageBox('Стоит галка Stop ! Ничего в базу не пишем!');            
            return false;
        }
        var myValue1 = OppDataset('myVaule1');
        var myValue2 = OppDataset('myVaule2');
        var myValue3 = OppDataset('myVaule3');
        var Result = hConfirmation(myValue1,myValue2, myValue3,  OpWorkspace.COpWindow);
}

"Олейник Дмитрий" написал:

а вот с третьей непонятно. Самое интересное то, что вы ей передаете датасет реестра (OpDataset), который позиционирован на нужной строке (т.е. та в которой стоп = 0), а также ParentWidnow, но при этом нигде их не используете. Покажите как Вы его используете, потому что в текущем примере OLD строка никак не может быть обработана, если вы не выполняете никаких манипуляций , которые приводят к смене текущей активной строки, что-то вроде этого:

OpDataset.GotoNext();

OppDataset.Close();

OppDataset.Open();

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

function amiOnExecute(ActionMenuItem, Sender) {

        var OpDataset  = dlOp.Dataset;

        var Stop = OpDataset('Stop');

        if (!IsZeroValue(Stop)) {

            MessageBox('Стоит галка Stop ! Ничего в базу не пишем!');            

            return false;

        }

        var myValue1 = OppDataset('myVaule1');

        var myValue2 = OppDataset('myVaule2');

        var myValue3 = OppDataset('myVaule3');

        var Result = hConfirmation(myValue1,myValue2, myValue3,  OpWorkspace.COpWindow);

}

С уважением,

Олейник Дмитрий


Манипуляции выполняются в этом цикле:

//Вот в этом цикле пишутся данные в БД. 
for(Dataset.GotoFirst(), Counter = 0; ( !Dataset.IsEOF ); Counter++, Dataset.GotoNext()) {
.....
Dataset.Edit();
.....
 
 }
Dataset.Close();
 
}

Сначала выполняется текущая активная строка грида с Stop=0(null), а затем в этом цикле выполняется OLD строка (с Stop=1), которую вроде бы как мы покинули здесь:

if (!IsZeroValue(Stop)) {
            MessageBox('Стоит галка Stop ! Ничего в базу не пишем!');
            return false;
            }

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

Можно ли очистить наш OpDataset вообще как-нибудь, что-бы там не было никаких строк old?
И если можно, то в этом месте

if (!IsZeroValue(Stop)) {
            MessageBox('Стоит галка Stop ! Ничего в базу не пишем!');
// Здесь очищаем наш дата сет от нашей строки, которая попала сюда.
            return false;
            }

Думаю, Вам стоит передать обезличенную копию БД или сервисы в поддержку. Без этого долго воспроизводить придется.

Разобрался с вопросом. Спасибо!

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

Господа

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

В не визуальные компоненты окна - добавил необходимый набор данных, на форме разместил компонент LookupDataControl, для свойства DatasetLink - указал добавленный мной набор данных. Но при установке свойства DataFieldName - в журнал выбрасывается ошибка
"Невозможно установить имя поля "FieldName" для компонента "LookupDataControl""
В наборе данный ключевое поле, и первичное поле для отображения установлены.

В чём может быть причина ошибки?

С уважением
Егор

Нравится

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

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

0x8000ffff - TSObjectLibrary.DBDataset: Ошибка открытия источника данных "ds_OrderGiftsList".

Оригинальное сообщение об ошибке: All queries combined using a UNION, INTERSECT or EXCEPT operator must have an equal number of expressions in their target lists

Егор, что представляет собой sq_OrderGiftsList, он содержит union-ы?
Если содержит, проверьте чтобы все union содержали одинаковое количество колонок с одинаковым набором свойств.

SELECT
	[ID],
	[Name],
	[LotID],
	[LotName],
	[IsJewel],
	[IsEvent],
	[GiftID],
	[GiftName],
	[AmountFrom],
	[GiftTypeID],
	[GiftDescription]
FROM
	(
    SELECT
  	  '{00000000-0000-0000-0000-000000000002}' AS [ID],
  	  NULL AS [Name],
  	  [tbl_LotInGift].[LotID] AS [LotID],
  	  [tbl_Lot].[Name] AS [LotName],
  	  [tbl_Lot].[IsJewel] AS [IsJewel],
  	  [tbl_Lot].[IsEvent] AS [IsEvent],
  	  [tbl_Gift].[ID] AS [GiftID],
  	  [tbl_Gift].[Name] AS [GiftName],
  	  [tbl_Gift].[AmountFrom] AS [AmountFrom],
  	  [tbl_Gift].[TypeID] AS [GiftTypeID],
  	  NULL AS [GiftDescription]
    FROM
	    [dbo].[tbl_Gift] AS [tbl_Gift]
    INNER JOIN
	    [dbo].[tbl_LotInGift] AS [tbl_LotInGift] ON [tbl_LotInGift].[GiftID] = [tbl_Gift].[ID]
    LEFT OUTER JOIN
	    [dbo].[tbl_Lot] AS [tbl_Lot] ON [tbl_Lot].[ID] = [tbl_LotInGift].[LotID]
    LEFT OUTER JOIN
	    [dbo].[tbl_SaleSource] AS [tbl_SaleSource] ON [tbl_SaleSource].[ChannelID] = [tbl_Gift].[ChannelID]
    WHERE([tbl_Gift].[TypeID] = :gtOrderNumber AND
	    :Date BETWEEN [tbl_Gift].[DateFrom] AND [tbl_Gift].[DateTo] AND
	    (([tbl_Gift].[SourceID] IS NULL AND
	    [tbl_Gift].[ChannelID] IS NULL) OR
	    ([tbl_Gift].[SourceID] IS NULL AND
	    [tbl_SaleSource].[ID] = :SourceID) OR
	    ([tbl_Gift].[SourceID] = :SourceID)) AND
	    [tbl_Lot].[StatusID] IN (:LotStatusIsOpen) AND
	    [tbl_Gift].[StateID] = :gfstActive AND
	    dbo.tsf_GetIsOrderNumberGiftAppllicable(:OrderNumber, tbl_Gift.OrderNumberPeriod,
      tbl_Gift.OrderNumberMaxCount, tbl_Gift.OrderNumberStartValue, tbl_Gift.OrderNumberCount)   = 1)
UNION ALL
SELECT
	N'{00000000-0000-0000-0000-000000000001}' AS [ID],
	NULL AS [Name],
	[tbl_LotInGift].[LotID] AS [LotID],
	[tbl_Lot].[Name] AS [LotName],
	[tbl_Lot].[IsJewel] AS [IsJewel],
	[tbl_Lot].[IsEvent] AS [IsEvent],
	[tbl_Gift].[ID] AS [GiftID],
	[tbl_Gift].[Name] AS [GiftName],
	[tbl_Gift].[AmountFrom] AS [AmountFrom],
	[tbl_Gift].[TypeID] AS [GiftTypeID],
	SetLot1Code + ';' + SetLot2Code AS [GiftDescription]
FROM
	[dbo].[tbl_Gift] AS [tbl_Gift]
INNER JOIN
	[dbo].[tbl_LotInGift] AS [tbl_LotInGift] ON [tbl_LotInGift].[GiftID] = [tbl_Gift].[ID]
LEFT OUTER JOIN
	[dbo].[tbl_Lot] AS [tbl_Lot] ON [tbl_Lot].[ID] = [tbl_LotInGift].[LotID]
LEFT OUTER JOIN
	[dbo].[tbl_SaleSource] AS [tbl_SaleSource] ON [tbl_SaleSource].[ChannelID] = [tbl_Gift].[ChannelID]
WHERE([tbl_Gift].[TypeID] = :gtLotSet AND
	:Date BETWEEN [tbl_Gift].[DateFrom] AND [tbl_Gift].[DateTo] AND
	(([tbl_Gift].[SourceID] IS NULL AND
	[tbl_Gift].[ChannelID] IS NULL) OR
	([tbl_Gift].[SourceID] IS NULL AND
	[tbl_SaleSource].[ID] = :SourceID) OR
	([tbl_Gift].[SourceID] = :SourceID)) AND
	[tbl_Gift].[StateID] = :gfstActive AND
	dbo.tsf_GetIsLotSetGiftAppllicable(:OrderID, tbl_Gift.ID) = 1 AND
	[tbl_Lot].[StatusID] IN (:LotStatusIsOpen))
UNION ALL
SELECT
	'{00000000-0000-0000-0000-000000000000}' AS [ID],
	NULL AS [Name],
	[tbl_LotInGift].[LotID] AS [LotID],
	[tbl_Lot].[Name] AS [LotName],
	[tbl_Lot].[IsJewel] AS [IsJewel],
	[tbl_Lot].[IsEvent] AS [IsEvent],
	[tbl_Gift].[ID] AS [GiftID],
	[tbl_Gift].[Name] AS [GiftName],
	[tbl_Gift].[AmountFrom] AS [AmountFrom],
	[tbl_Gift].[TypeID] AS [TypeID],
	NULL AS [GiftDescription]
FROM
	[dbo].[tbl_Gift] AS [tbl_Gift]
INNER JOIN
	[dbo].[tbl_LotInGift] AS [tbl_LotInGift] ON [tbl_LotInGift].[GiftID] = [tbl_Gift].[ID]
LEFT OUTER JOIN
	[dbo].[tbl_Lot] AS [tbl_Lot] ON [tbl_Lot].[ID] = [tbl_LotInGift].[LotID]
LEFT OUTER JOIN
	[dbo].[tbl_SaleSource] AS [tbl_SaleSource] ON [tbl_SaleSource].[ChannelID] = [tbl_Gift].[ChannelID]
WHERE([tbl_Gift].[TypeID] = :gtOrderAmount AND
	:Date BETWEEN [tbl_Gift].[DateFrom] AND [tbl_Gift].[DateTo] AND
	(([tbl_Gift].[SourceID] IS NULL AND
	[tbl_Gift].[ChannelID] IS NULL) OR
	([tbl_Gift].[SourceID] IS NULL AND
	[tbl_SaleSource].[ID] = :SourceID) OR
	([tbl_Gift].[SourceID] = :SourceID)) AND
	[tbl_Lot].[StatusID] IN (:LotStatusIsOpen) AND
	[tbl_Gift].[StateID] = :gfstActive AND
 
	(SELECT
		SUM([tbl_Partable].[Amount]) AS [Amount]
	FROM
		[dbo].[tbl_Orders] AS [tbl_Orders]
	LEFT OUTER JOIN
		[dbo].[tbl_Partable] AS [tbl_Partable] ON [tbl_Partable].[OrdersID] = [tbl_Orders].[ID]
	WHERE([tbl_Orders].[ID] = :OrderID AND
		[tbl_Partable].[default] = :TRUE AND
		[tbl_Partable].[IsGift] = :False)) BETWEEN [tbl_Gift].[AmountFrom] AND [tbl_Gift].[AmountTo])
UNION ALL
SELECT
	'{00000000-0000-0000-0000-000000000004}' AS [ID],
	NULL AS [Name],
	[tbl_LotInGift].[LotID] AS [LotID],
	[tbl_Lot].[Name] AS [LotName],
	[tbl_Lot].[IsJewel] AS [IsJewel],
	[tbl_Lot].[IsEvent] AS [IsEvent],
	[tbl_Gift].[ID] AS [GiftID],
	[tbl_Gift].[Name] AS [GiftName],
	[tbl_Gift].[AmountFrom] AS [AmountFrom],
	[tbl_Gift].[TypeID] AS [TypeID],
	NULL AS [GiftDescription]
FROM
	[dbo].[tbl_Gift] AS [tbl_Gift]
INNER JOIN
	[dbo].[tbl_LotInGift] AS [tbl_LotInGift] ON [tbl_LotInGift].[GiftID] = [tbl_Gift].[ID]
LEFT OUTER JOIN
	[dbo].[tbl_Lot] AS [tbl_Lot] ON [tbl_Lot].[ID] = [tbl_LotInGift].[LotID]
LEFT OUTER JOIN
	[dbo].[tbl_SaleSource] AS [tbl_SaleSource] ON [tbl_SaleSource].[ChannelID] = [tbl_Gift].[ChannelID]
WHERE([tbl_Gift].[TypeID] = :gtClientLoyalty AND
	:Date BETWEEN [tbl_Gift].[DateFrom] AND [tbl_Gift].[DateTo] AND
	(([tbl_Gift].[SourceID] IS NULL AND
	[tbl_Gift].[ChannelID] IS NULL) OR
	([tbl_Gift].[SourceID] IS NULL AND
	[tbl_SaleSource].[ID] = :SourceID) OR
	([tbl_Gift].[SourceID] = :SourceID) OR
	[tbl_Lot].[StatusID] IN (:LotStatusIsOpen) OR
	[tbl_Gift].[StateID] = :gfstActive) AND
	[tbl_Gift].[LoyaltyID] = :LoyaltyID)
UNION ALL
SELECT
	[tbl_Partable].[LotsID] AS [ID],
	[ReasonLot].[Name] AS [Name],
	[tbl_LotInGift].[LotID] AS [LotID],
	[tbl_Lot].[Name] AS [LotName],
	[tbl_Lot].[IsJewel] AS [IsJewel],
	[tbl_Lot].[IsEvent] AS [IsEvent],
	[tbl_Gift].[ID] AS [GiftID],
	[tbl_Gift].[Name] AS [GiftName],
	[tbl_Gift].[AmountFrom] AS [AmountFrom],
	[tbl_Gift].[TypeID] AS [TypeID],
	cast([tbl_Partable].[LotsID] as nvarchar(40)) AS [GiftDescription]
FROM
	[dbo].[tbl_Orders] AS [tbl_Orders]
LEFT OUTER JOIN
	[dbo].[tbl_Partable] AS [tbl_Partable] ON [tbl_Partable].[OrdersID] = [tbl_Orders].[ID]
LEFT OUTER JOIN
	[dbo].[tbl_Gift] AS [tbl_Gift] ON [tbl_Gift].[LotID] = [tbl_Partable].[LotsID]
LEFT OUTER JOIN
	[dbo].[tbl_LotInGift] AS [tbl_LotInGift] ON [tbl_LotInGift].[GiftID] = [tbl_Gift].[ID]
LEFT OUTER JOIN
	[dbo].[tbl_Lot] AS [tbl_Lot] ON [tbl_Lot].[ID] = [tbl_LotInGift].[LotID]
LEFT OUTER JOIN
	[dbo].[tbl_SaleSource] AS [tbl_SaleSource] ON [tbl_SaleSource].[ID] = [tbl_Orders].[SourceID]
LEFT OUTER JOIN
	[dbo].[tbl_Lot] AS [ReasonLot] ON [ReasonLot].[ID] = [tbl_Partable].[LotsID]
WHERE([tbl_Gift].[TypeID] = :gtOrderQuantity AND
	:Date BETWEEN [tbl_Gift].[DateFrom] AND [tbl_Gift].[DateTo] AND
	(([tbl_Gift].[SourceID] IS NULL AND
	[tbl_Gift].[ChannelID] IS NULL) OR
	([tbl_Gift].[SourceID] IS NULL AND
	[tbl_Gift].[ChannelID] = [tbl_SaleSource].[ChannelID]) OR
	([tbl_Gift].[SourceID] = :SourceID)) AND
	[tbl_Gift].[StateID] = :gfstActive AND
	[tbl_Orders].[ID] = :OrderID AND
	[tbl_Partable].[default] = :TRUE AND
	[tbl_Partable].[IsGift] = :False AND
	[tbl_Lot].[StatusID] IN (:LotStatusIsOpen) AND
	0 = 0) 
GROUP BY
        [tbl_Gift].[ID],
        [tbl_Gift].[Name],
       	[ReasonLot].[Name],
        [tbl_LotInGift].[ID],        
	[tbl_Partable].[LotsID],
	[tbl_LotInGift].[LotID],
	[tbl_Lot].[Name],
	[tbl_Lot].[IsJewel],
	[tbl_Lot].[IsEvent],
	[tbl_Gift].[QuantityFrom],
	[tbl_Gift].[QuantityTo],
	[tbl_Gift].[TypeID],
	[tbl_Gift].[AmountFrom]
 
HAVING COUNT([tbl_Partable].[ID]) BETWEEN [tbl_Gift].[QuantityFrom]   AND [tbl_Gift].[QuantityTo]
 
) AS [U] ORDER BY [ID] --	)) AS [U]

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

Yegor, первое что бросается в глаза - заголовок 10-й колонки в первых 3-х подзапросах (union) GiftTypeID, а в последующих TypeID, что некорректно.
Вам нужно изменить заголовки и свойства колонок в запросе таким образом, чтобы они были одинаковы во всех union-х.

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