В БД значение даты храниться в формате dd.mm.yyyy hh:mm:ss

В шаблоне письма должна быть дата в формате dd.mm.yyyy. Дата вставляется в шаблон письма из пользовательского запроса. Как конвертировать дату в нужный формат в пользовательском запросе?

Может быть есть ещё какие-то варианты?

Нравится

Поделиться

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

Попробуйте изменить свойство Kind для нужного поля в шаблоне:
dtkDate - Дата.
dtkTime - Время.
dtkDateTime - Дата / Время.

"Вадим_" написал:В БД значение даты храниться в формате dd.mm.yyyy hh:mm:ss

Вадим, а как Вы это определили? Вы храните в текстовом поле?

Fishi, спасибо.

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

"Осауленко Александр" написал:Вы храните в текстовом поле?

Нет, конечно. Я беру это поле в пользовательский запрос, и оно выводится в этом формате.

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

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

Вечер добрый. Задачка вот такая... Не знаю, что за баг, но странно..... Итак -

Создаю SQL запрос.... Вроде бы все должно работать, прогоняю его - работает... Запрос простой - вот

SELECT
        [tbl_Document].[Title] AS [Title],
        [tbl_Document].[DocumentNumber] AS [DocumentNumber],
        [tbl_Document].[InvoiceID] AS [InvoiceID],
        [tbl_Document].[Description] AS [Description],
        [tbl_Invoice].[Amount] AS [InvoiceAmount],
        [tbl_Invoice].[InvoiceNumber] AS [InvoiceNumber],
        [Account].[Name] AS [AccountName],
        [EmployrContact].[Name] AS [EmployrName],
        [tbl_Document].[AccountID] AS [AccountID],
        [tbl_Document].[EmployrContactID] AS [EmployrContactID],
        (SELECT
                MAX([tbl_PlanningOfNumber].[DateOfIssue]) AS [DateOfIssue]
        FROM
                [dbo].[tbl_Outputs] AS [tbl_Outputs]
        LEFT OUTER JOIN
                [dbo].[tbl_PlanningOfNumber] AS [tbl_PlanningOfNumber] ON [tbl_PlanningOfNumber].[ID] = [tbl_Outputs].[PlanedPlanningNumberID]
        LEFT OUTER JOIN
                [dbo].[tbl_OfferingInDocument] AS [tbl_OfferingInDocument] ON [tbl_OfferingInDocument].[ID] = [tbl_Outputs].[OfferingInDocumentID]
        WHERE([tbl_Document].[ID] = [tbl_OfferingInDocument].[DocumentID])) AS [DateFinished],
        [tbl_Document].[PublicationsAmount] AS [PublicationsAmount],
        [tbl_Document].[Sum] AS [Sum]
FROM
        [dbo].[tbl_Document] AS [tbl_Document]
LEFT OUTER JOIN
        [dbo].[tbl_Account] AS [Account] ON [Account].[ID] = [tbl_Document].[AccountID]
LEFT OUTER JOIN
        [dbo].[tbl_Invoice] AS [tbl_Invoice] ON [tbl_Invoice].[DocumentID] = [tbl_Document].[ID]
LEFT OUTER JOIN
        [dbo].[tbl_Contact] AS [EmployrContact] ON [EmployrContact].[ID] = [tbl_Document].[EmployrContactID]

Перенес этот запрос в Террасофт и с помощью построителя запросов создал запрос в террасофте, используя одну колонку - колонку с текстом SQL. Потом сделал датасет, фастрепорт и добавил фильтр на дату - т.е. на это самое поле - DateFinished. Вот фильтр -

В итоге, если фильтровать значения по этому полу - результат - НИЧЕГО. Если фильтр не включать запрос отрабатывает нормально - все строки выдаются.
Ладно, решил это поле построить тоже с помощью построителя запросов, а старое переименовать - вот что получилось

Просматривая запрос видим, что поля абсолютно одинаковые -

Используя новую колонку и отключая старую все работает и фильтр тоже нормально фильтрует....
Вопрос - почему так происходит?????
Почему я не могу использовать нормально колонку SQL запроса, и мне приходится постоянно прописывать в построителе запросов???? А если запрос будет сложнее???? Тогда как????

Нравится

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

Добрый день, Евгений!
Для колонки типа CustomSQLColumn и поля набора данных, построенного на его основе, на данный момент (3.3.1.х) есть ограничения по использованию, в частности, пока недоступна фильтрация. В будущих версиях разработчики обязательно учтут это замечание. В вашем случае выходом их положения является использование SubSelectColumn, для которых доступна фильтрация и сортировка.

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

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

Как построить запрос к таблице tbl_Contact и tbl_ContactLog, таким образом, чтоб извлечь информацию в нужном мне разрезе.
Мне необходимо подсчитать, сколько полей каждого типа(Должность, РОль, Средство связи и т.д.), за период заполнил Сотрудник?
1. Если запись в справочнике Сотрудники только создана?
2. Если запись в справочнике Сотрудники была изменена? Ведь в лог-таблице хранятся данные, предыдущих значений основной таблицы, но как из этой таблицы извлечь количество записей, где за заданный период поле изменило свое значение с «нулл» на «ненулл»? И как построить запрос, если мне нужно в одной таблице получить количество таких переходов, по нескольким типам записей, например, Должность, Роль, Департамент и т.д.
Или может быть проще написать свой триггер, для контроля, если извлечение информации из лог-таблицы будет слишком сложным?

Нравится

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

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

select "CreatedByID", 
       sum(decode("JobID",null,0,decode("prevJobID",null,1,0))) "JobSetCount" -- считаем количество записей, где предыдущее значение null, а новое не null
  from(select "RecordID", "CreatedOn", "CreatedByID", "JobID", 
              (select min("JobID") from "Log" l2 -- извлекаем предыдущую запись лога
                where l2."RecordID" = l1."RecordID" 
                  and l2."CreatedOn" = (select max("CreatedOn") from "Log" l3 
                                         where l3."RecordID" = l1."RecordID"
                                           and l3."CreatedOn" < l1."CreatedOn")
              ) "prevJobID" 
         from "Log" l1)
group by "CreatedByID";

Только надо учесть, что текущего состояния записи в "tbl_ContactLog" нет, поэтому стоит сделать view:

create view "Log" as select "ID", "RecordID", "CreatedOn", "CreatedByID", "JobID"
     from "tbl_ContactLog"
    union all
   select "ID", "ID", "ModifiedOn", "ModifiedByID", "JobID" 
     from "tbl_Contact";

Ну а сложно это или нет смотрите сами. Также как и быстродействие на ваших объемах.

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

Прошу прощения сразу не пояснил, что запросы нужны для MS SQL :-(. Функции decode в T-SQL нет, насколько я понял это аналог CASE. А зачем поля брать в кавычки, это так в Оракле положено? Ну и т.к. не указаны алиасы полей, то на GROUP BY тоже вываливается ошибка, если не затруднит пользователей более продвинутых в вопросе особенностей реализации первести этот запрос на MS SQL.

"Черных Руслан" написал:Функции decode в T-SQL нет, насколько я понял это аналог CASE.

Да, это аналог CASE.

"Черных Руслан" написал:А зачем поля брать в кавычки, это так в Оракле положено?

Для работы с Terrasoft (из-за того, что используются "case sensitive" имена) так положено.

"Черных Руслан" написал:если не затруднит пользователей более продвинутых
Я не настолько "продвинут", чтобы написать это вслепую, а mssql, чтобы проверить под рукой нет. Может быть кто-то другой поможет.

Эксперементирую с запросом, но никак не пойму странного поведения CASE. Буду благодарен за подсказку, что я делаю не так. См. приложенный рисунок

"Черных Руслан" написал:Эксперементирую с запросом, но никак не пойму странного поведения CASE. Буду благодарен за подсказку, что я делаю не так.

Это типичная ошибка. Дело в том, что прямые сравнения с null никогда не дают true. Для проверку на null надо использовать is null. То есть

CASE 
WHEN "Communication3" IS NULL OR "Communication3"='' THEN 0
ELSE 1
END

Спасибо большое, Андрей. Если кому интересно, вот так выглядит запрос для MS SQL:

SELECT "CreatedByID", 
sum(CASE when "JobID" is NULL  then 0 else
 (CASE  when "prevJobID" is NULL then 1 else 0 END) END) "JobSetCount" 
-- считаем количество записей, где предыдущее значение null, а новое не null
FROM
(SELECT "RecordID", "CreatedOn", "CreatedByID", "JobID",
  (SELECT l2.JobID FROM dbo.vw_ContactLog as l2 -- извлекаем предыдущую запись лога
                WHERE l2."RecordID" = l1."RecordID" 
                  AND l2."CreatedOn" = (SELECT max("CreatedOn") FROM dbo.vw_ContactLog as l3 
                                         WHERE l3."RecordID" = l1."RecordID"
                                           AND l3."CreatedOn" < l1."CreatedOn")
              ) "prevJobID"  
 
         FROM dbo.vw_ContactLog as l1) as T
Group By "CreatedByID"

Из характерных особенностей - обязательное указание алиаса для конструкции select ... from(select ...) as T
И так как я буду контролировать не только ключевые поля, на предмет заполнения , но и строковые(средства связи), то в CASE будет еще добавлена проверка на не пустое значение, как показал Андрей выше.

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

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

(TSCRM 3.2, MSSQL Server 2008)

При открытии датасета ds_MPlan.Open() в профайлере отлавливается запрос, который возвращается несколько строк, но ds_MPlan.RecordsCount при этом равен 0.
Если дело не в версии SQL Server, как тогда справиться с этой проблемой?

...
ApplyDateFilter(ds_MPlan, 'ShowsDate', StartShow.getVarDate(), EndShow.getVarDate());
        ApplyDatasetFilter(ds_MPlan, 'ScreenID', ScreenID, true);  
        ds_MPlan.Open();
        ShowInformationDialog(ds_MPlan.RecordsCount);
...

// На SQL Server 2005 работает. И на 2008 работало до какого-то момента.

Нравится

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

Количество записей в датасете ds_MPlan.PageRecordsCount. Может стоит им пользоваться.
ds_MPlan.RecordsCount шлет еще один запрос типа
select count(*) from (select ...)

А Вы запросы выполняете под одним пользователем?

количество запросов в данном случае не так важно.
да, под одним пользователем.

А вот эти два запроса
1)Который идет на Open
2)Который идет на RecordsCount в середине select count(*) from (...)
они одинаковые?

да, и при этом запрос на RecordsCount возвращает нормальное число, а не 0.

Да ну, не бывает такого :)

Берем первый запрос на Open. Он ничего не возвращает. Дописываем в начало
select count(*) from (
и в конец
)
Выполняем. Получаем значение отличное от 0. Так у Вас получается?

Я бы еще поверил что какой-то запрос неправильно генерится террасофтом. Но так выходит мегабага в MSSQL.

Сами в это не можем поверить.

Не совсем так. Запрос, посланный на Open, возвращает значения, но если его в MSSQL запустить;
а датасет при этом пустой почему-то.
Связи сервисов в терасофте проверены сто раз.

Один и тот же код на 2005 сервере нормально отрабатывает, а на 2008 вот такое чудо :(

А, так MS честно работает. Это, наверное, компоненты доступа к нему подглючивают.
Разве TS совместим с 2008 MSSQL? Я думал только 2000 и 2005

официально не поддерживается. наверно он и дает сбои.

Добавлю свои комментарии:

Мы тестировали 3.3.0 и 3.3.1 с MS SQL 2008 - проблем не было замечено.

В одном из наших проектов клиент купил MS SQL 2008 и ТС 3.2.1.14 - с январе 2009г. ошибок со стороны MS SQL 2008 не было замечено!

--
www.it-sfera.com.ua
Terrasoft Solution Partner

Может это зависит от конкретной машины? MDAC, допустим, не тот.

"Kat" написал:// На SQL Server 2005 работает. И на 2008 работало до какого-то момента.

Меня смущает последняя строчка... Значит что-то изменилось...

--
www.it-sfera.com.ua
Terrasoft Solution Partner

а посмотреть, какой запрос приходит на сервер в Profiler?

Все оказалось совсем просто: было запущено два терасофта, смотрящие на разные базы, поэтому сервер БД путался.

всем спасибо ;)

+1

Бывает :) У меня однажды фильтр забыли отключить по-умолчанию... 3 часа рыли где проблема ;)

--
www.it-sfera.com.ua
Terrasoft Solution Partner

"Kat" написал:сервер БД путался

Так это все его происки :)

Со всеми наверное было. Да, когда по ошибке правишь в одном месте , а тестишь в другом на чинаешь верить в потусторонние силы и высший разум :)

"Kat" написал:Мы тестировали 3.3.0 и 3.3.1 с MS SQL 2008 - проблем не было замечено.

MSSQL 2008 поддерживается официально, начиная с версии 3.3.1. Были замечены проблемы при соединении с базой из Vista (64-бита) и Windows Server 2008 (32-бита). Сейчас разбираемся.

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

Вот некоторые примеры динамического создания компонентов в Terrasoft Administrator. Может кому пригодится...
Для того, чтобы создать сервис динамически, Вам необходимо создать скрипт, где прописать следующую функцию:

function CreateServiceInDynamic(ServiceCode, TemplateName, USI) {
    var Win = Services.CreateItem(ServiceCode);
    Win.TemplateWindowUSI = TemplateName;
    Win.USI = USI;
    var Code = ExtractUSICodeEx(USI);
    Services.SaveItem(Win,1);        
    var Win2 = Services.GetNewItemByUSI(Code);
    Win2.IsDesigning = false;
    Win2.Show();    
}

function Main() {
    var ServiceCode = 'Window';
    var TemplateName = 'wnd_BaseDBEdit';
    var USI =
'Common\\Workspaces\\Custom\\Structure\\Details\\TestDir\\wnd_Test';
    CreateServiceInDynamic(ServiceCode, TemplateName, USI); }

Для создания сервиса окна Вам необходимо использовать функцию Services.CreateItem и для сохранения данного сервиса Services.SaveItem.
Необходимо указать сервис родительского окна и USI.

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

var Component = Window.CreateComponent('LookupControl', 'edtTestLookupControl');

Вы можете создать FrameGroup-и, а далее в них создавать и размещать контролы:

scr_SurveyUtils: function CreateFrame.
     var Component = Window.CreateComponent(ComponentType,
          FrameName + '_1');
     Frame.Add(Component);
     Component.AlignHorizontal = alhClient;

Нравится

Поделиться

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

Спасибо за пример!



--

www.it-sfera.com.ua

Terrasoft Solution Partner

Тогда уже не CreateServiceInDynamic, а CreateWindowInDynamicи параметр ServiceCode убрать. Потому что если туда передать не 'WIndow',  то получим свал.

Вообще если хочется разобраться с созданием окон на лету, можно посмотреть создание окна Интервью.

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

На примере, обьяснен общий принцип работы с XML в TestComplete (DelphiScript).

Для понимания XML советую предварительно просмотреть:
ru.wikipedia.org/wiki/XML - короткая и понятная статья по XML
www.w3schools.com/xml/ - руководство по XML

К примеру есть XML файл (сохранен по адресу ‘C:\Test.xml’):






Из него необходимо получить значение атрибутов Login, Password, Role.

Для этого необходимо выгрузить XML-файл в объект, понятный для среды разработки, который можно обрабатывать в скрипте.
TestComplete позволяет работать с любыми XML, используя DOM XML (msdn.microsoft.com/ru-ru/library/2bcctyt8.aspx - XML документы и данные).

Пример: Функция загружает указанный XML файл, если структура XML нарушена - выдает сообщение об ошибке XML-парсера, в результате работы функция возвращает объект.

function InitializeXML(APath: WideString): OleVariant;
var File: OleVariant;
 Doc: OleVariant;
 s: OleVariant;
begin
 File := APath;
 Doc := Sys.OleObject('Msxml2.DOMDocument.6.0');
 Doc.async := false;
 Doc.load(File);
 if Doc.parseError.errorCode &lt;&gt; 0 then
  begin
   s := 'Reason:' + #9 + Doc.parseError.reason + #13#10 +
   'Line:' + #9 + VarToStr(Doc.parseError.line) + #13#10 +
   'Pos:' + #9 + VarToStr(Doc.parseError.linePos) + #13#10 +
   'Source:' + #9 + Doc.parseError.srcText;
   Log.Error('Cannot parse the document.', s);
   Exit;
   end;
 Result := Doc;
end;

Для получения данных из XML используем средства DOM XML и XPath.
Для этого необходимо у объекта XML-документа (либо объекта XML-ноды), вызвать функцию selectNodes(), и передать в функцию XPath-запрос.
Пример:

XMLDoc.selectNodes('//User')

Функция selectNodes - возвращает коллекцию объектов, т.е. если даже результат запроса всего одна запись, возвращается коллекция с одним объектом. НЕЛЬЗЯ прямо обращаться к нодам атрибутам, полученным из selectNodes. т.е. выражение XMLDoc.selectNodes('//User').getAttribute('Login') - ошибочно. Правильно будет XMLDoc.selectNodes('//User').item(0).getAttribute('Login');
Чтобы узнать количество элементов (item) коллекции необходимо вызвать свойство length.
Пример: XMLDoc.selectNodes('//User').length - возвращает количество нод User в XML документе.
Для обработки всех элементов коллекции, необходимо пробежаться по каждому из них обращаясь по индексу элемента, причем обработку необходимо проводить в соответствии с возвращаемым типом данных (ноды, атрибуты, текст)

Пример: Получение данных

procedure TestDOMXML();
var XMLDoc: OleVariant;
 i: Integer;
 User: WideString;
 Password: WideString;
 Role: WideString;
begin
 XMLDoc := InitializeXML('C:\Test.xml');
 for i:=0 to XMLDoc.selectNodes('//User').length-1 do
  begin
   User:= XMLDoc.selectNodes('//User/@Login').item(i).text;
   Password:= XMLDoc.selectNodes('//User/@Password').item(i).text;
   Role:= XMLDoc.selectNodes('//User/@Role').item(i).text;
   Log.Message('User: ' + User + ' Password: ' + Password + ' Role: ' + Role);
  end;
end;

возможен другой вариант цикла обработки
for i:=0 to XMLDoc.selectNodes('//User').length-1 do
  begin
   User:= XMLDoc.selectNodes('//User').item(i).getAttribute('Login').text;
   Password:= XMLDoc.selectNodes('//User').item(i).getAttribute('Login').text;
   Role:= XMLDoc.selectNodes('//User').item(i).getAttribute('Login').text;
   Log.Message('User: ' + User + ' Password: ' + Password + ' Role: ' + Role);
  end;

XPath - выражение //User/@Login - выбирает из XML все ноды User, у каждой из них выбирает указанный атрибут (Login, Password, Role). Если запрошенного атрибута нет, то возвращается Null (или если привести к строке '0')

Если есть задача получить ноды, или получить значение атрибута ноды определенного типа, условия фильтрации удобно вмещать в выражение XPath:
Пример: Получить логины пользователей, которые являются администраторами:

//User[@Role='Administrator']/@Login

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

Нравится

Поделиться

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

привет, твоя статья мне очень помогла:) А есть пример как добавлять инфу в хмл? иль хоть линка где рпо это почитать?:)

советую обращаться к первоисточникам MSDN

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

Здравствуйте!!
когда делала отчет "Акт" в разделе документов, использовала для отображения продуктов акта query "sq_OfferingInDocument", но у меня по акту отображались не конкретные продукты по этому акту, а все продукты (по всем актам). Таким образом я поставила галочку в разделе Where на фильтре DocumentID и у меня все заработало как надо, НО теперь возникла другая проблема: не отображаются продукты на детали продуктов в разделе документов. Попробовала убрать галочку, которую поставила в запросе - не помогло...причем продукты в базу добавляются,так как при формировании отчета "Акт", все вновь добавленные продукты там присутствуют.
Подскажите пожалуйста в чем дело?
С уважением, Гашникова Екатерина

Нравится

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

Здравствуйте, Екатерина.
Для решения возникшей проблемы можем Вам посоветовать следующее.
Во-первых, для отчета советуем Вам использовать запрос на выборку sq_ReportDocumentOfferings вместо sq_OfferingInDocument. В том случае если каких-либо полей в данном запросе на выборку нет, Вы можете добавить необходимые.
Во-вторых, что касается активации фильтра DocumentID, то наиболее вероятной причиной возникновения данной проблемы является отсутствие перезапуска рабочего приложения после внесенных изменений, сохранений внесенных изменений в кэше. Советуем Вам после внесения изменений в Terrasoft CRM Administrator перезапустить рабочее приложение, протестировать работоспособность системы.

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

Добрый день! Пытаюсь подключить OLAP-куб к TSCRM и в ответ получаю ошибку. После нажатия кнопки "Установить информацию о соединении" через пол-минуты появляется диалог настройки. В качестве поставщика данных я использую "Microsoft OLE DB Provider for OLAP Services 8.0". Далее настраиваю подключение, выбираю куб, жму "Проверить подключение", проверка проходит успешно. После завершения настройки в иерархическом дереве OLAP-кубов появляется вновь добавленный куб. А в разделе "Таблица" появляется табличка, в детальной части которой находится запись "Запрос не может быть обработан: Поставщик данных не предоставил дополнительных сведений об ошибке". Пытался подключать кубы, построенные как на базе СУБД MS Access, так и на базе Oracle. Сначала я думал, что проблема заключается в MS Office WEB Components. Для её решения скачал с сайта Microsoft пакет обновления и установил его, но это не изменило ситуации. Не подскажете, в чём тут может быть дело?

Нравится

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

Похоже не установлен Microsoft Query - входит в состав MS Office.
Попробуйте установить его - должно появиться другое окно настройки соединения с кубом.

Судя по всему, проблема заключается не в MS Query, потому что получить данные в Excel из OLAP-куба удаётся, а вот получить их в CRM не получается... Но на всяких случай переустановил MS Query, не помогло.

Отправьте мне скриншоты по почте, начиная с окна настройки соединения и заканчивая окном с ошибкой
a.popov@tscrm.com

Выслал

Ответил. MS Query все-таки не установлен.

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