Условие:
Существует два поля Дата начала, Дата окончания и третье поле Количество дней.
Функция подсчёта встроена в след функцию, которая выполняется на пересчёт датасета "Продукт в договоре":
//вычисление дней между датами курса (продолжительность курса)
// Первая дата
var Date1 = new Date(Dataset.ValAsDateTime('DeliveryDate'));
Date1.setUTCHours(0,0,0,0);
// Вторая дата
var Date2 = new Date(Dataset.ValAsDateTime('FinishDate'));
Date2.setUTCHours(0,0,0,0);
// Сколько целых дней между датами
var Year1 = Date1.getYear();
var Year2 = Date2.getYear();
if ((Year1>2000) && (Year2>2000)) {
var Days = Math.floor((Date2.getTime() - Date1.getTime())/(1000*60*60*24));
Dataset.ValAsInt('CourseDuration') = Days;
}
else
{
Dataset.ValAsInt('CourseDuration') = 0;
}
Исполнение пересчёта происходит после повторного открытия окна.
Задача:
Каким образом запустить пересчёт после изменения Даты1 или Даты2, без повторного открытия окна?
Нравится
Можно повесить событие на Таймер.
upd/
не посмотрел что на изменения дат.
На событие датасета OnDatasetDataChange, если даты будут изменятся с помощью датасета, а не апдейт запросов или тригеров.
думаю, что нужно сделать на DatasetDataChange, а если будет необходимо изменять даты без помощи датасета, то учесть, что пересчет придется делать дополнительно
Пробовал через OnDatasetDataChange - выдаёт ошибку
"TSObjectLibrary.DBDataset: Не возможно изменить значение вычисляемого поля"
function DataChange(DataField, ScriptObject){ if (DataField == null) { return; } var DataFields = DataField.ParentDataFields; var Dataset = DataFields.ParentDataset; var Value = DataField.Value; switch (DataField.Name){ case ('DeliveryDate') : //вычисление дней между датами курса (продолжительность курса) // Первая дата (год, месяц, день) var Date1 = new Date(Dataset.ValAsDateTime('DeliveryDate')); Date1.setUTCHours(0,0,0,0); // Вторая дата (год, месяц, день) var Date2 = new Date(Dataset.ValAsDateTime('FinishDate')); Date2.setUTCHours(0,0,0,0); // Сколько целых дней между датами var Year1 = Date1.getYear(); var Year2 = Date2.getYear(); if ((Year1>2000) && (Year2>2000)) { var Days = Math.floor((Date2.getTime() - Date1.getTime())/(1000*60*60*24)); Dataset.ValAsInt('CourseDuration') = Days; } else { Dataset.ValAsInt('CourseDuration') = 0; }
Я так понимаю это поле CourseDuration. Так его в событии вычисления полей нужно менять.
Upd: наконец-то понял в чем вопрос :) Попробуйте на изменение полей значение не присваивать , а.... получать :) Т.е. взов FillOfferingName с присвоением значения поля CourseDuration переносим в OnCalc. А в OnChange делаем просто
var CourseDuration = Dataset.Values('CourseDuration');
Пока что 2 поля: Offering и CourseDuration.
Отладчик ругается на CourseDuration в вышеизложенном коде
Dataset.ValAsDateTime('CourseDuration') = Days;
:) так то оно так. Может проще будет его обычным сделать, а не вычисляемым?
Если я правильно понял, то здесь:
var Days = Math.floor((Date2.getTime() - Date1.getTime())/(1000*60*60*24));
Days - будет целое число
А здесь
Dataset.ValAsDateTime('CourseDuration') = Days;
Вы его в DateTime присваиваете... Сделайте вычисляемое Целое..
"Глова Сергей" написал:Может проще будет его обычным сделать, а не вычисляемым?
уже подумывал над этим...
мне то нужно только отображение количества дней между двумя датами в интерактиве...
Ща попробую вариант Underscore a.k.a. _, если не выйдет сделаю обычным и ручную калькуляцию OnPrepare и OnChange..
---
Конфигурация:
TSCRM 3.3.1.36
Firebird 2.0 (v.2.0.3.12981)
А какая такая калькуляция на OnPrepare ? И обратите внимание на замечание Виталия.
"Виталий Ковалишин aka samael" написал:Вы его в DateTime присваиваете... Сделайте вычисляемое Целое..
Та нет, поле вычисляемое целое, это копипейст враг логики))
поправил в первом коде...
Виталий, дело как раз в присвоении значения вычисляемому полю...
"Underscore a.k.a. _" написал:Т.е. взов FillOfferingName с присвоением значения поля CourseDuration переносим в OnCalc. А в OnChange делаем просто
К сожалению не помогло...
Теперь при смене даты она становиться не активной, а вычисляемое поле Offering очищается...
---
Конфигурация:
TSCRM 3.3.1.36
Firebird 2.0 (v.2.0.3.12981)
на datasetdatachange при изменении дат можно вызвать
Dataset.CalcDataFields();
"Раловец Ольга" написал:
К сожалению не работает, так как неопределён датасет в datasetdatachange(datafield), а если получать его по datafield'у
var DataFields = DataField.ParentDataFields;
var Dataset = DataFields.ParentDataset;
то весь запуск TS заканчивается подвисанием и завершением задачи...
---
Конфигурация:
TSCRM 3.3.1.36
Firebird 2.0 (v.2.0.3.12981)
var DataFields = DataField.ParentDataFields; var Dataset = DataFields.ParentDataset;
Вот именно так и нужно брать датасет, зависание происходит, наверное, по какой-то другой причине.
Вам нужно пересчитывать значение вычисляемого поля только в OnDatasetCalcFields(Dataset) и на datasetdatachange только вызывать CalcDataFields. Возможно, у Вас одно из полей дата пересчитывается при изменении другого и закцикливается, пройдитесь под отладчиком.
"Раловец Ольга" написал:
Нет даты не пересчитываются, но есть ещё одно поле Offering(так как я работаю с "Продуктами в договоре")
Думаю оно и влияет...
Цикл видится так:
в момент присваевания Dataset.Values('Offering') = Offering; во время пересчёта полей по функции
function FillOfferingName(Dataset) { var Offering; var OfferingID = GetFieldValueFromDisabledField(Dataset, 'OfferingID'); if (!IsEmptyGUID(OfferingID)) { Offering = GetFieldValueFromDisabledField(Dataset, 'OfferingName'); } else { Offering = GetFieldValueFromDisabledField(Dataset, 'CustomOffering'); } debugger; Dataset.Values('Offering') = Offering; //вычисление дней между датами курса (продолжительность курса) //Первая дата (год, месяц, день) var Date1 = new Date(Dataset.ValAsDateTime('DeliveryDate')); Date1.setUTCHours(0,0,0,0); // Вторая дата (год, месяц, день) var Date2 = new Date(Dataset.ValAsDateTime('FinishDate')); Date2.setUTCHours(0,0,0,0); // Сколько целых дней между датами var Year1 = Date1.getYear(); var Year2 = Date2.getYear(); if ((Year1>2000) && (Year2>2000)) { var Days = Math.floor((Date2.getTime() - Date1.getTime())/(1000*60*60*24)); Dataset.ValAsInt('CourseDuration') = Days; } else { Dataset.ValAsInt('CourseDuration') = 0; } }
меняется значение поля Offering, при этом вызывается ondatasetdatachange в котором, в свою очередь запускается Dataset.CalcDataFields(); Так что при такой реализации не возможно выйти из данного цикла...соответственно способ неверен.
UPD: Та же история повторяется с присвоением CourseDuration.
Само присвоение уже меняет датасет, а изменение датасета запускает пересчёт.
Может есть ещё идеи?
---
Конфигурация:
TSCRM 3.3.1.36
Firebird 2.0 (v.2.0.3.12981)
Так а зачем вызывать Dataset.CalcDataFields() при изменении поля Offering? Его же надо вызывать только при изменении дат?
Если Вы напишете следующим образом
Dataset.DisableEvents(); Dataset.ValAsInt('CourseDuration') = Days; Dataset.EnableEvents();
то при изменении значения поля CourseDuration событие DataChange вызываться не будет. В datasetdatachange должно быть условие
if ((DataField.Name == 'DeliveryDate') || (DataField.Name == 'FinishDate')) { Dataset.CalcDataFields(); }
То есть, нужно вызывать CalcDataFields() только на изменение этих двух дат, тогда после изменения поля "Название" внутри FillOfferingName() CalcDataFields() срабатывать не будет.
"Underscore a.k.a. _" написал:Так а зачем вызывать Dataset.CalcDataFields() при изменении поля Offering? Его же надо вызывать только при изменении дат?
Ну да, спасибо, поправил как посоветовала Ольга
"Раловец Ольга" написал:
Спасибо большое, всё заработало.
Upd: правда добавил ещё 2 поля даты('DepartureDate','ReturnDate'), 1 поле исчисляемое('TripDuration')...
Сделал всё как для предидущего случая..
Dataset.DisableEvents(); Dataset.ValAsInt('TripDuration') = Days2; Dataset.EnableEvents();
if ((DataField.Name == 'DeliveryDate') || (DataField.Name == 'FinishDate') || (DataField.Name == 'DepartureDate') || (DataField.Name == 'ReturnDate')) { Dataset.CalcDataFields(); }
Всё считает и показывает...
Но при нажатии на кнопку OK выскакивает сообщение "Не возможно изменить значение вычисляемого поля" для TripDuration в onCalc.
---
Конфигурация:
TSCRM 3.3.1.36
Firebird 2.0 (v.2.0.3.12981)
Добрый день.
Александр, я всё-таки не совсем понимаю, зачем устанавливать значения вычисляемым полям в функции FillOfferingName, тем более, что к названию продукта они никаким образом не относятся. Нельзя всё, что идёт после строки
//вычисление дней между датами курса (продолжительность курса)
перенести в обработчик события OnDatasetCalcFields? Вся эта часть должна нормально отработать и там.
Если так сделать, то будет иметь смысл перед вызовом FillOfferingName в обработчике OnDatasetDataChange вставить проверку
if ((DataField.Name == 'OfferingID') || (DataField.Name == 'CustomOffering')) {
FillOfferingName(Dataset);
}
поскольку только эти два поля влияют на название.
"Лабьяк Олег Игоревич" написал:
Спасибо, за время ковыряния уже перенёс функции вычисления из
FillOfferingName в отдельные RecalcCourseDates и RecalcTripDates. Но от перестановки слагаемых, сумма не меняется...(смайлик пожимает плечами)
OnDataChange:
var DataFields = DataField.ParentDataFields; var Dataset = DataFields.ParentDataset; DataChange(DataField, ds_OfferingInContractScript); if ((DataField.Name == 'DeliveryDate') || (DataField.Name == 'FinishDate')) { RecalcCourseDates(Dataset); } if ((DataField.Name == 'DepartureDate') || (DataField.Name == 'ReturnDate')) { RecalcTripDates(Dataset); }
OnCalc:
function SelfOnDatasetCalcFields(Dataset) { FillOfferingName(Dataset); RecalcCourseDates(Dataset); RecalcTripDates(Dataset); }
Возможно что то измениться если подскажите как получить DataField в OnDatasetDataChange(Dataset)?
---
Конфигурация:
TSCRM 3.3.1.36
Firebird 2.0 (v.2.0.3.12981)
Александр, насколько я знаю, в OnDatasetDataChange именно DataField и является параметром:
function SelfOnDatasetDataChange(DataField) {...}
В обработчике SelfOnDatasetCalcFields(Dataset) DataField можно получить так:
DataField = Dataset.DataFields.ItemsByName(FieldName);
Попробуйте вместо
if ((DataField.Name == 'DeliveryDate') || (DataField.Name == 'FinishDate')) {
RecalcCourseDates(Dataset);
}
вызывать
if ((DataField.Name == 'DeliveryDate') || (DataField.Name == 'FinishDate')) {
Dataset.CalcDataFields();
}
как писала Оля Раловец. Соответственно и для второго условия.
Олег мы с Вами запутались...
"Лабьяк Олег Игоревич" написал:Если так сделать, то будет иметь смысл перед вызовом FillOfferingName в обработчике OnDatasetDataChange вставить проверку
if ((DataField.Name == 'OfferingID') || (DataField.Name == 'CustomOffering')) {
FillOfferingName(Dataset);
}
Дело в том что FillOfferingName вызывается в OnCalc, a не в OnChange...
А в OnChange вызывается сам OnCalc...
В общем результат тот же...Остановился на варианте Ольги...
Осталось придумать как решить вопрос с присвоением полю второго количества дней.
"Швец Александр" написал:Всё считает и показывает...
Но при нажатии на кнопку OK выскакивает сообщение "Не возможно изменить значение вычисляемого поля" для TripDuration в onCalc.
---
Конфигурация:
TSCRM 3.3.1.36
Firebird 2.0 (v.2.0.3.12981)
Сорри, когда я писал свой пост, я видел только то Ваше сообщение, в котором "выполняется на пересчёт датасета "Продукт в договоре"". Скриптов, которые Вы привели в последнем сообщении, не было.
Я бы сделал так:
function SelfOnDatasetDataChange(DataField) {
var DataFields = DataField.ParentDataFields;
var Dataset = DataFields.ParentDataset;
DataChange(DataField, ds_OfferingInContractScript);
if ((DataField.Name == 'DeliveryDate') || (DataField.Name == 'FinishDate') || (DataField.Name == 'DepartureDate') || (DataField.Name == 'ReturnDate')) {
Dataset.CalcDataFields();
}
}
------------------------------------------
function SelfOnDatasetCalcFields(Dataset) {
FillOfferingName(Dataset);
RecalcCourseDates(Dataset);
RecalcTripDates(Dataset);
}
"Лабьяк Олег Игоревич" написал:
так и сделал...
результат:
TSObjectLibrary.DBDataset: Не возможно изменить значение вычисляемого поля
в
function RecalcTripDates(Dataset) { var Date3 = new Date(Dataset.ValAsDateTime('DepartureDate')); Date3.setUTCHours(0,0,0,0); // Вторая дата (год, месяц, день) var Date4 = new Date(Dataset.ValAsDateTime('ReturnDate')); Date4.setUTCHours(0,0,0,0); // Сколько целых дней между датами var Year3 = Date3.getYear(); var Year4 = Date4.getYear(); if ((Year3>2000) && (Year4>2000)) { var Days2 = Math.floor((Date4.getTime() - Date3.getTime())/(1000*60*60*24)); Dataset.DisableEvents(); Dataset.ValAsInt('TripDuration') = Days2; Dataset.EnableEvents(); } else { Dataset.DisableEvents(); Dataset.ValAsInt('TripDuration') = 0; Dataset.EnableEvents(); } }
на строке
Dataset.ValAsInt('TripDuration') = Days2;
UPDATE
Обход данной ошибки реализован заменой вычисляемого поля - обычным Целочисленным.
И переносом функции исчисления второго количества дней из OnCalc в OnChange...
Хотелось бы уточнить у старожилов смысл исчесляемых полей?
Как мне показалось это дубль обычного поля и функции он OnChange...
Поправте меня пожалуйста...
P.S. Ещё раз всем спасибо за помощь и поддержку....
Вычисляемые поля - это данные которые нет надобности или смысла хранить в базе данных. Так как их можно вывести по какой-нибудь формуле.
Например есть поле дедлайн в базе данных. А нужно знать сколько времени осталось до дедлайна - вот в таких случаях очень пригодится вычисляемое поле
"Глова Сергей" написал:
спасибо, действительно не обратил внимание на эту возможность вычисляемого поля...
Предлагаю добавить к данному типу поля, функцию "рефреш"(если она ещё не реализована), которая бы обновляла данные из базы, так как присвоить для вывода новые данные, как показывает пример выше, нет возможности.
В этом бы случае в онкалк проводился бы сам расчёт, а в ончендж делался бы рефреш поля для вывода вычесляемых данных в обход ошибки показаной в примере выше...
---
Конфигурация:
TSCRM 3.3.1.36
Firebird 2.0 (v.2.0.3.12981)
Аможет убрать в
Dataset.DisableEvents(); Dataset.ValAsInt('TripDuration') = 0; Dataset.EnableEvents();
включение\отключение событий? Вполнее может быть что из-за баги датасета калькулируемые поля не могут менятся, если события отключены.
Кстати такие вещи нужно в try finally делать.
"Underscore a.k.a. _" написал:Аможет убрать в
В этом случае будут зацикливаться события OnCalc OnChange.
Так как при измении поля TripDuration в OnCalc без отключения событий запускается событие OnChange в котором прописан запуск OnCalc и так до безконечности...
---
Конфигурация:
TSCRM 3.3.1.36
Firebird 2.0 (v.2.0.3.12981)
Так CalcDataFields вызывается по изменению других полей.
"Underscore a.k.a. _" написал:Так CalcDataFields вызывается по изменению других полей.
Да, но в контроле поля на форме результат исчисления показывается только после повторного открытия формы...
До этого - сохраняется старое значение...
---
Конфигурация:
TSCRM 3.3.1.36
Firebird 2.0 (v.2.0.3.12981)
Можно попробовать контрол отвязать от поля и привязать его обратно
"Underscore a.k.a. _" написал:Можно попробовать контрол отвязать от поля и привязать его обратно
Это в смысле
edtTripDuration.DataField='';
edtTripDuration.DataField='TripDuration';
И на какое именно событие этот "бубен" :smile: вешать?
---
Конфигурация:
TSCRM 3.3.1.36
Firebird 2.0 (v.2.0.3.12981)
Ну после изменения значения поля. Если контрол не видит изменения значения, должен же хотя бы увидеть, что поле поменяли :)