3.4.0 XRM
Ситуация следующая:
При сохранении карточки (добавление\обновление) надо было создавать и обновлять, если уже было создано, заранее неизвестное количество операций (одна или две или три - в зависимости от значения поля). Я решил эти так:
сделал триггер After Update на tbl_Task, который удаляет все связанные с задачей (и "созданные автоматически" - поле в tbl_Cashflow) операции, а на AfterPost датасета задач добавил функцию добавления операций.
Все работает нормально, но есть 1-10% записей для которых операции отсутствуют. То ли не создаются, то ли удаляются - т.е. такое ощущение, что сначала отрабатывает AfterPost датасета - создает операции, а потом отрабатывает триггер (из-за каких-нибудь гипотетических тормозов сервера) - удаляет операции.
Нагрузка на сервер маленькая, если не минимальная.
Вопросы:
1) Возможна ли ситуация когда триггер After Update отрабатывает после AfterPost датасета?
2) если да, то есть ли еще варианты, как это решить, кроме как создавать доп. поля в задаче для записи ИД созданных операций или обновления\удаления записей по ним
Нравится
Там еще такой важный момент был, что задачу мог изменять, например, директор и тогда операции создавались от его имени и с урезанными для менеджеров правами - в результате чего менеджеры их не видели и у них создавались дубликаты этих операций. Именно поэтому я перенес логику на БД
1. Такого быть не может. Скорее всего ошибка в самом триггере (в запросе на удаление). По умолчанию триггер работает в рамках одной транзакции, т.е. или он отрабатывает полностью или не отрабатывает вообще (вываливается с ошибкой). Смотрите в тело триггера.
"Евгений Либин" написал:триггер работает в рамках одной транзакции
да, действительно, что это я :lol:
однако в триггере "ничего такого":
SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO ALTER TRIGGER [dbo].[tr_tbl_Task_AU] ON [dbo].[tbl_Task] AFTER UPDATE AS BEGIN SET NOCOUNT ON; declare @TaskID uniqueidentifier select @TaskID = [INSERTED].[ID] from [INSERTED] DELETE FROM [dbo].[tbl_Cashflow] WHERE([tbl_Cashflow].[TaskID] = @TaskID AND [tbl_Cashflow].[AutoCreated] = 1) END
в карточке редактирования тоже все предельно просто:
function dlDataOnDatasetAfterPost(Dataset) { CopyContactInTasksIfNeed(Dataset); //ProcessSendMailMessageForTask(Dataset); //отключено CreateContactsInTask(Self, Dataset); CreateCashflowByTask(dlData.Dataset); // вот это }
Не знаю, почему там используется даталинк - наверно, просто как следствие копипаста). Вряд ли это могло вызвать эту проблему, исправил.
в самой функции, если убрать все лишнее и заполнение значений тоже все довольно просто:
function CreateCashflowByTask(TaskDataset) { // на апдейт задачи стоит tr_tbl_Task_AU (удаляет старые операции) var TaskCode = GetTaskTypeCodeByID(TaskDataset.ValAsGUID('TypeID')); var CashflowDataset = Services.GetNewItemByUSI('ds_Cashflow'); // сюда наверно лучше поставить GetSingleItemByCode() CashflowDataset.DataFields.ItemsByName('DebtorCreditorID').IsRequired = false; CashflowDataset.Append(); FillCashFlowDatasetValuesByTask(CashflowDataset, TaskDataset, false, TaskCode); CashflowDataset.Post(); CashflowDataset.Close(); }
В результате, по-прежнему не понимаю, почему может так происходить.
Причем воспроизвести сохранение задачи без формирования операции у меня пока не получилось. Не знаю, что и делать - не сидеть же мне и не клепать записи задач пока не получу такой случай...
А что внутри FillCashFlowDatasetValuesByTask?
Похоже что туда надо смотреть, т.е. триггер отработал а FillCashFlowDatasetValuesByTask нет.
если прочитаете, то вот ))
если коротко - там просто заполняются значения, все действия происходят вне функции
function FillCashFlowDatasetValuesByTask(CashflowDataset, TaskDataset, HeadPhones, TaskCode) { var ClauseID = GetCashflowClauseIDByCode(TaskCode); var ExpenseTypeID; if ((TaskCode == 'Bus') /*&& IsEmptyGUID(TaskDataset.ValAsGUID('OpportunityID'))*/) { ExpenseTypeID = GetCashflowExpenseTypeIDByCode(TaskCode); } else /*if (TaskDataset.ValAsBool('OrderByGuide')){ ExpenseTypeID = GetCashflowExpenseTypeIDByCode('drugoe'); } else*/ { ExpenseTypeID = GetCashflowExpenseTypeIDByCode('Tour'); } if (TaskDataset.ValAsBool('IsCash') || TaskCode == 'Bus' || HeadPhones) { CashflowDataset.ValAsGUID('CashAccountID') = '{4FAFDE39-D5EB-4309-BAFD-9C3DA1FCF0EB}'; // наличная касса } else { CashflowDataset.ValAsGUID('CashAccountID') = '{ADEC1E41-17EE-4B02-B04E-D677DEA48D39}'; // расчетный счет } if (TaskDataset.ValAsGUID('OpportunityID')) { CashflowDataset.ValAsGUID('OpportunityID') = TaskDataset.ValAsGUID('OpportunityID'); }CashflowDataset.ValAsGUID('StatusID') = '{FDEA47BE-53FE-4730-BF4F-4F44C3B5D61A}'; // выполнена if (!HeadPhones) { CashflowDataset.ValAsStr('Subject') = TaskDataset.ValAsStr('Title'); } else { CashflowDataset.ValAsStr('Subject') = TaskDataset.ValAsStr('Title') + ' наушники'; } //var IsTop = GetIsUserInGroup(Connector.CurrentUser.Name, 'УЧРЕДИТЕЛИ'); CashflowDataset.ValAsBool('UseAsCashflow') = false; CashflowDataset.ValAsBool('UseAsPandL') = true; CashflowDataset.ValAsBool('AutoCreated') = true; CashflowDataset.ValAsBool('ForFlow') = true; CashflowDataset.ValAsGUID('ClauseID') = ClauseID; CashflowDataset.ValAsGUID('ExpenseTypeID') = ExpenseTypeID; // группы CashflowDataset.ValAsGUID('CurrencyID') = TaskDataset.ValAsGUID('CurrencyID'); if (!HeadPhones) { CashflowDataset.ValAsFloat('Amount') = TaskDataset.ValAsFloat('FullAmount'); } else { CashflowDataset.ValAsFloat('Amount') = TaskDataset.ValAsFloat('HeadphonesAmount'); } CashflowDataset.ValAsGUID('PayerID') = Connector.CurrentUser.AccountID; CashflowDataset.ValAsFloat('AutocalcAmount') = true; if (!HeadPhones) { CashflowDataset.Values('RecipientID') //= CashflowDataset.Values('DebtorCreditorID') = TaskDataset.Values('SupplierID'); } else { CashflowDataset.Values('RecipientID') //= CashflowDataset.Values('DebtorCreditorID') = TaskDataset.Values('Supplier2ID'); } //SetBasicPriceInDataset(CashflowDataset, 'BasicAmount', 'Amount', 'CurrencyID', 'CurrencyRate'); CashflowDataset.ValAsGUID('TaskID') = TaskDataset.ValAsGUID('ID'); CashflowDataset.DisableEvents(); CashflowDataset.ValAsDateTime('ActualDate') = CashflowDataset.ValAsDateTime('EstimatedDate') = TaskDataset.ValAsDateTime('StartDate'); CashflowDataset.EnableEvents(); return CashflowDataset; }
на всякий случай вот полный текст функций
createcashflowbytask.txt
На первый взгляд всё ОК.
Проверьте 2 пункта
1. Посмотрите напрямую из базы
Select * from tbl_CashFlow Where TaskID = 'Нужный ID задачи'
2. Если вышеуказанный запрос вернет данные, то посмотрите на sq_CashFlow возможно где-то inner join или в where доп. условия.
"Евгений Либин" написал:Проверьте 2 пункта
1. нет
2. нет(
"Евгений Либин" написал:А где CashflowDataset.Open(); ?
мне его читать не надо - есть только Append() в CreateCashflowByTask
"SQK" написал:Если набор данных не находится в состоянии добавления записи (значение свойства IDataset::State не равно "dstInsert"), и у текущего пользователя есть права на добавление записи (значение свойства IDataset::CanInsert равно "True"), то вызывает событие IDatasetEvents::OnDatasetBeforeAppend. Иначе метод завершает работу.
Я бы рекомендовал всегда перед добавление делать открытие датасета, тем более есть такая строка
CashflowDataset.DataFields.ItemsByName('DebtorCreditorID').IsRequired = false;
.
Для ускорения можно делать фильтр по любому левому ID
ApplyDatasetFilter(CashflowDataset, 'ID', GUID_NULL, true);
Обращения по ID моментальное, набор данных будет пустой и датасет будет проинициализирован.
Спасибо, Евгений, воспользуюсь Вашими советами, хотя и не понимаю, для чего мне открывать датасет (на Before\After Open ничего нет)
Тут смысл не в отработке событий, а в инициализации самого датасета.
Здравствуйте, Дмитрий!
Помогли ли советы Евгения по открытию DataSet'а?
В данном случае также возможны следующие вариант, что операции создаются, но не привязываются к записям.
Попробуйте включить логирование и при возникновении проблемы проверьте, добавлялись ли записи вообще.
"Андрей Каспаревич" написал:Помогли ли советы Евгения по открытию DataSet'а?
Пока не знаю. Я дописал открытие с фильтрацией. Через какое-то время посмотрим, будет ли повторяться ситуация.
"Андрей Каспаревич" написал:операции создаются, но не привязываются к записям
почему? Не вижу причин, почему ИД задач и продаж могли бы не подставляться в скрипте. Кроме того я бы видел разницу между результатами запросов в SQL и в Террасофт
"Андрей Каспаревич" написал:
Попробуйте включить логирование и при возникновении проблемы проверьте, добавлялись ли записи вообще.
Так и сделал))
Через 2 недели - месяц отпишусь, что все ок, либо раньше - что нет
А делать DELETE FROM [dbo].[tbl_Cashflow] можно под всеми пользователями?
"Борисов Михаил Евгеньевич" написал:А делать DELETE FROM [dbo].[tbl_Cashflow] можно под всеми пользователями?
триггеру можно)
"Андросов Дмитрий" написал:
Борисов Михаил Евгеньевич пишет:
А делать DELETE FROM [dbo].[tbl_Cashflow] можно под всеми пользователями?
триггеру можно)
Не понял, как? Тригер срабатывает в рамках транзакции где пошел update. Он будет обращаться к таблицам от имени того пользователя от которого пошел update.
"Борисов Михаил Евгеньевич" написал:Он будет обращаться к таблицам от имени того пользователя от которого пошел update
вот только пользователи работают с представлениями (и их триггерами), а я сделал на таблицу
"Андросов Дмитрий" написал:
Борисов Михаил Евгеньевич пишет:
Он будет обращаться к таблицам от имени того пользователя от которого пошел update
вот только пользователи работают с представлениями (и их триггерами), а я сделал на таблицу
У меня только один вопрос: от имени какого пользователя открыта транзакция в тригере таблицы? Что возращает suser_name(): supervisor, vasay или еще что то? Я подозреваю, что vasay и прав у него ровно столько сколько дали через grant.
На сколько я понял - проблем с удалением записей нет. Вопрос только с созданием новых. Если речь идет о контексте выполнения триггера, то нужно для начала уточнить версию СУБД. Разные СУБД и даже разные версии СУБД по разному используют (не используют) контекст.
Проблема была в том, что пользователи использовали массовое редактирование записей (делается через датасет), а создание операций повешено на скрипт карточки....
зато заглавный вопрос прояснил :wink: