Подскажите, пожалуйста, как получить количество дней в месяце в формуле процесса?



DaysInMonth() редактору формул не нравится

Изображение удалено.

Нравится

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

Сделайте скриншот формулы с текстом ошибки.

Можно попробовать добавить System в Namespace.

И посмотрите еще раз пример.

int daysInJuly = System.DateTime.DaysInMonth(2021, 7);

Владимир, у меня всё работает:

В процесс дополнительно ничего не добавлял.

Зверев Александр пишет:

Андрей, у меня всё работает:

Очень интересно! Я проверил в облаке - действительно, работает.

А на on-site - выдаёт ошибку.



В чём может быть разница? 

Может, версия отличается? Я пробовал на 7.17.2.

Зверев Александр пишет:

развернули Marketing 7.17.2 PostgreSQL на Windows - то же самое...

Чистка кэша браузера и Redis не помогает?

В «инструментах разработчика» браузера есть более подробная информация об ошибке?

Чистка, рестарт не помогли, в консоли тоже ничего.  

Значит, есть ещё какие-то различия между системами, где работает, и где нет. А если на одном сайте создать БП, выгрузить схему в файл и загрузить на другой сайт, тоже перестаёт работать?

Зверев Александр пишет:

Значит, есть ещё какие-то различия между системами, где работает, и где нет. А если на одном сайте создать БП, выгрузить схему в файл и загрузить на другой сайт, тоже перестаёт работать?

Проверил ещё на парочке on-site (которые ставили не мы) - везде повторяется такая же ошибка.. 

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

Зверев Александр,

Вернулся к этому вопросу. Если перенести схему из облака, то процесс успешно выполняется. 

Однако при попытке его редактировать, снова та же ошибка.



В логах не нашли ничего подозрительного

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

Здравствуйте!
Скажите пожалуйста как в БП посчитать количество дней в текущем месяце в которых например было 30 исходячих звонков длительностью больше 30 сек?

И как посчитать в БП количество всего робочих дней в этом месяце ?
Спасибо!

Нравится

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

Добрый день.
1. Кол-во дней в месяце с заданными условиями
1. Определить временной интервал (начало и конец периода). Например, добавить параметры startDate, endDate, oneDay (startDate.AddDays(1)).
2. Добавить счетчик. Параметр в котором будет хранится кол-во дней c интересующими условиями.
Процесс:
1) Читать данные в звонках. Читать кол-во записей где: тип – исходящий, продолжительность>30, CallDate >= startDate, CallDate < oneDay
2) Потоки:
- условный. Если Пункт 1 >= 30 - записываем в счетчик + 1. startDate и oneDay увеличиваем на 1 день и возвращаемся к пункту 1
- поток по умолчанию. startDate и oneDay увеличиваем на 1 день и возвращаемся к пункту 1
- условный. Если oneDay = endDate – завершить процесс

2. Кол-во рабочих дней.
Самый простой способ - посчитать кол-во дней в месяце и отнять кол-во дней, которые = суббота или воскресенье.
Достаточно базовых методов DateTime - https://msdn.microsoft.com/en-us/library/system.datetime(v=vs.110).aspx
Пример также можно найти в схеме CalendarUtilities (пакет Project).

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

Условие:
Существует два поля Дата начала, Дата окончания и третье поле Количество дней.
Функция подсчёта встроена в след функцию, которая выполняется на пересчёт датасета "Продукт в договоре":

function FillOfferingName(Dataset)
        //вычисление дней между датами курса (продолжительность курса)
        // Первая дата
        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, без повторного открытия окна?

Нравится

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

Можно повесить событие на Таймер.
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 присваиваете... Сделайте вычисляемое Целое..

--
www.it-sfera.com.ua

"Глова Сергей" написал:Может проще будет его обычным сделать, а не вычисляемым?

уже подумывал над этим...
мне то нужно только отображение количества дней между двумя датами в интерактиве...
Ща попробую вариант 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)

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

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

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

Получение количества дней в месяце определенного года (решение в одну строку :))))))

var n = (m != 2 ?((m % 2) ^ (m > 7)) + 30 : (!(y % 400) || !(y % 4) && (y % 25) ? 29 : 28));
где m - месяц, y - год

Получение номера недели (U.S. Standart)

function y2k(number) {
        return (number 1000) ? number + 1900 : number;
}

function getWeek(day,month,year) {
        year = y2k(year);
    var when = new Date(year,month,day);
    var newYear = new Date(year,0,1);
    var offset = 7 + 1 - newYear.getDay();
    if (offset == 8) offset = 1;
    var daynum = ((Date.UTC(y2k(year),when.getMonth(),when.getDate(),0,0,0) - Date.UTC(y2k(year),0,1,0,0,0)) /1000/60/60/24) + 1;
    var weeknum = Math.floor((daynum-offset+7)/7);
    if (weeknum == 0) {
        year--;
        var prevNewYear = new Date(year,0,1);
        var prevOffset = 7 + 1 - prevNewYear.getDay();
        if (prevOffset == 2 || prevOffset == 8) weeknum = 53; else weeknum = 52;
    }
    return weeknum;
}

Получение номера недели (Europe and ISO Standard):

function y2k(number) {
        return (number 1000) ? number + 1900 : number;
}

function getWeekEUR(day,month,year) {
        year = y2k(year);
        var when = new Date(year,month,day);
    var newYear = new Date(year,0,1);
    var modDay = newYear.getDay();
    if (modDay == 0) modDay=6; else modDay--;

    var daynum = ((Date.UTC(y2k(year),when.getMonth(),when.getDate(),0,0,0) -
                 Date.UTC(y2k(year),0,1,0,0,0)) /1000/60/60/24) + 1;

    if (modDay 4 ) {
        var weeknum = Math.floor((daynum+modDay-1)/7)+1;
    }
    else {
        var weeknum = Math.floor((daynum+modDay-1)/7);
        if (weeknum == 0) {
            year--;
            var prevNewYear = new Date(year,0,1);
            var prevmodDay = prevNewYear.getDay();
            if (prevmodDay == 0) prevmodDay = 6; else prevmodDay--;
            if (prevmodDay 4) weeknum = 53; else weeknum = 52;
        }
    }

    return + weeknum;
}

Нравится

Поделиться

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