2012年7月14日星期六

How to get Occenabled table from AOT

static void THK_tableOccenabledFromAOT01(Args _args)
{
    #AOT
    #properties
    TreeNode        TreeNode = TreeNode::findNode(#TablesPath);
    str             tableName;
    str             temporaryProperty;
    sysdictTable    m_dictTable;
    int             i,records,m;
    dictionary      m_dictionary = new dictionary();
    #File
    #WinAPI
    #Avifiles
    CommaIo                 commaIO;
    SysOperationProgress    sysProgress;
    fileName                fileName;
    FileIOPermission        permission;
    int                     start;
;
    if(!Box::yesNo("Are your sure lookup Occenabled table infomation?",dialogButton::No,"Occenabled Table"))
        return;
    fileName = WinAPI::getFolderPath(#CSIDL_Personal) + "\\" + "A02AvgPrice.csv";// + date2str(systemdateget(),321,2,0,2,0,2) + ".CSV";
    permission  = new FileIOPermission(fileName,#io_write);
    permission.assert();
    commaIo = new CommaIo(fileName,#io_write);
    if(!commaIo)
    {
        warning("Please close your csv file and try again. Thanks!");
        return;
    }
    if(commaIo.status() == IO_Status::Ok)
        CommaIo.outFieldDelimiter(",");
    else
        return;
    start = timeNow();
    records = m_dictionary.tableCnt();
    sysProgress = SysOperationProgress::newGeneral(#AviUpdate,"Waitting ... write CSV file",records);
    CommaIo.writeExp(["Id","Table Id","Table Name","Table Label","Occenabled","utilElementType"]);
    for (i = 1; i < records; i++)
    {
        m_dictTable = new sysdictTable(m_dictionary.tableCnt2Id(i));
        sysProgress.incCount(1);
        sysProgress.setText(strfmt("completed %1 [%2]: %3",i/ records * 100, i, m_dictTable.name()));
        if(!m_dictTable.occEnabled())
        {
            m++;
            CommaIo.writeExp([m,m_dictTable.id(),m_dictTable.name(),m_dictTable.label(),m_dictTable.occEnabled(),m_dictTable.utilElementType()]);
        }
        sysProgress.update(true);
    }
    if(m)
    {
        if (winAPI::fileExists(fileName))
        {
            winAPI::shellExecute(fileName);
        }
    }
    CodeAccessPermission::revertAssert();
    info(strfmt("It takes time %1",global::time2StrHMS(timenow() - start)));
    info(strfmt("completed %1 records",m));
}
------------------
static void THK_tableOccenabledFromAOT02(Args _args)
{
    #AOT
    #properties
    TreeNode        TreeNode = TreeNode::findNode(#TablesPath);
    str             tableName;
    AnyType         occenabledProperty;
    sysdictTable    m_dictTable;
    int             i,records,m;
    Dictionary      dictionary = new dictionary();
    #File
    #WinAPI
    #Avifiles
    CommaIo                 commaIO;
    SysOperationProgress    sysProgress;
    fileName                fileName;
    FileIOPermission        permission;
    int                     start;
    ;
    if(!Box::yesNo("Are your sure lookup Occenabled table infomation?",dialogButton::No,"Occenabled Table"))
        return;
    fileName = WinAPI::getFolderPath(#CSIDL_Personal) + "\\" + "A02AvgPrice.csv";// + date2str(systemdateget(),321,2,0,2,0,2) + ".CSV";
    permission  = new FileIOPermission(fileName,#io_write);
    permission.assert();
    commaIo = new CommaIo(fileName,#io_write);
    if(!commaIo)
    {
        warning("Please close your csv file and try again. Thanks!");
        return;
    }
    if(commaIo.status() == IO_Status::Ok)
        CommaIo.outFieldDelimiter(",");
    else
        return;
    start = timeNow();
    records = dictionary.tableCnt();
    sysProgress = SysOperationProgress::newGeneral(#AviUpdate,"Waitting ... write CSV file",records);
    CommaIo.writeExp(["Id","Table Id","Table Name","Table Label","Occenabled"]);
    TreeNode = TreeNode.AOTfirstChild();
    while (TreeNode)
    {
        i++;
        tableName = findProperty(TreeNode.AOTgetProperties(), #PropertyName); //table - > Name
        sysProgress.incCount(1);
        sysProgress.setText(strfmt("completed %1 [%2 / %3]: %4",i/ records * 100, i,records, tableName));
        occenabledProperty = findProperty(TreeNode.AOTgetProperties(),"Occenabled");
        if(occenabledProperty == "No")
        {
            m++;
            CommaIo.writeExp([m,"",tableName,"",occenabledProperty]);
        }
        TreeNode = TreeNode.AOTnextSibling();
        sysProgress.update(true);
    }
    if(m)
    {
        if (winAPI::fileExists(fileName))
        {
            winAPI::shellExecute(fileName);
        }
    }
    CodeAccessPermission::revertAssert();
    info(strfmt("It takes time %1",global::time2StrHMS(timenow() - start)));
    info(strfmt("completed %1 records",m));
}

2012年7月13日星期五

How to resolve to Number sequnec lock

While working in multi user environment, if you are creating journal/journal line through code then you might the have the number sequence locking problem. For understanding the problem you can use following code.
X++ code with posting jounral
----------------------------------------------------------
static void  checkNumberSeq(Args _args)
{
     NumberSeq  numberSeq;
     Voucher  releaseVoucher;
      ;

      ttsbegin;

     numberSeq  =  NumberSeq::newGetVoucher(VendParameters::numRefVendPaymentVoucher());
      releaseVoucher = numberSeq.voucher();
      NumberSeq::release(VendParameters::numRefVendPaymentVoucher().NumberSequence,releaseVoucher);

      //ttscommit;
      //ttsbegin;
     numberSeq  =  NumberSeq::newGetVoucher(VendParameters::numRefVendPaymentVoucher());
      releaseVoucher = numberSeq.voucher();

      ttscommit;
}
---------------------------------------------------------
If you uncomment ttscommit/ttsbegin in the middle of this job first, it will work fine. If you comment them and run the job again, one NumberSequenceTable record gets locked, and the system hangs during second newGetVoucher() call.

This happens because standard Ax use pessimistic lock while generating new number.
To resolve this problem either you have to use newGetVoucher() out of ttsbegin/ttscommit scope.
Another way to resolve it (not thoroughly tested) is to modify method \Classes\NumberSeq\release as follows:
x++ code
-----------------------------------------------------------------------------------------------
public static  boolean release(NumberSequenceCode  _numberSequenceCode,
                         Num                 _num
                         //#N, 1001 10/01/2008- -->                          ,userConnection     userConnection = null
                         //#N, 1001 10/01/2008-       //numberSequenceTable = NumberSequenceTable::find(_numberSequenceCode,true);
     if  (prmIsDefault(userConnection))
      {
          userConnection = New userConnection();
      }

      userConnection.ttsbegin();
      numberSequenceTable.setConnection(userConnection);
      numberSequenceList.setConnection(userConnection);

     select  forUpdate firstOnly numberSequenceTable
         index  hint SeriesIdx
         where  numberSequenceTable.numberSequence == _numberSequenceCode;
     //#N, 1001 10/01/2008-           userConnection.ttsabort();
         //#N, 1001 10/01/2008- >--          throw  error(strFmt("@SYS26271",_numberSequenceCode));
      }

     if  (!numberSequenceTable.checkBlocked())
      {
         //#N, 1001 10/01/2008- -->           userConnection.ttsabort();
         //#N, 1001 10/01/2008- >--          throw  error("@SYS18447");
      }

     if  (numberSequenceTable.continuous == NoYes::No)
         ok =  TRUE;
      else
      {
          select forupdate firstonly numberSequenceList
              index hint StatIdx
              where numberSequenceList.numberSequence == _numberSequenceCode  &&
                    numberSequenceList.num            == _num;

         if  (numberSequenceList)
          {
              if (numberSequenceList.status == NumStatus::Free)
              {
                  ok = TRUE;
              }
              else if (numberSequenceList.status == NumStatus::Active || numberSequenceList.status == NumStatus::Reserved) { if (numberSequenceList.transId)
                  {
                      numberSequenceList.status   = NumStatus::Free;
                      numberSequenceList.transId  = 0;
                      numberSequenceList.update();

                      select forupdate firstonly numberSequenceTTS
                          index hint TransIdIdx
                          where numberSequenceTTS.transId ==  numberSequenceList.transId;

                      if (numberSequenceTTS)
                          numberSequenceTTS.delete();

                      ok = TRUE;
                  }
                  else
                  {
                      xSession = new xSession(sessionId());

                      if (numberSequenceList.sessionId        == xSession.masterSessionId() ?  xSession.masterSessionId() : sessionId() &&
                          numberSequenceList.sessionLoginDate == xSession.loginDate()     &&
                          numberSequenceList.sessionLoginTime ==  xSession.loginTime())
                      {
                          numberSequenceList.status   = NumStatus::Free;
                          numberSequenceList.update();
                          ok = TRUE;
                      }
                      else
                      {
                          if (NumberSeqCleanUp::isProcessDead(numberSequenceList))
                          {
                              numberSequenceList.status   =  NumStatus::Free;
                              numberSequenceList.update();
                              ok = TRUE;
                          }
                          else
                          {
                              ok = FALSE;
                          }

                      }
                  }
              }
          }
          else
          {
              numberSequenceList.numberSequence    =  _numberSequenceCode;
              numberSequenceList.transId           = 0;
              numberSequenceList.status            = NumStatus::Free;
              numberSequenceList.num               = _num;
              numberSequenceList.insert();
              ok = TRUE;
          }
      }

     //#N, 1001 10/01/2008- -->       userConnection.ttscommit();
     //#N, 1001 10/01/2008- >--       ttscommit;

     return  ok;
}
-------------------------------------------------------------------------------------------

2012年7月12日星期四

How to fix to report scaling and the report is empty message

When a report doesn't fit on a page, depending on it's properties Ax will resize the report. This is a powerful and very useful feature.
Now Ax will inform you that the report has been rescaled (Report is scaled xx percent to fit to page) and this message is generally not well received by users.




Users are annoyed by the message, they get it every time they run the report, they cannot do anything about it, they have to click to close the infolog, ...

Ax has a builtin feature to suppress this scaling message. You can modify the init method of your report, and add something like this:

this.printJobSettings().suppressScalingMessage(true);
this.printJobSettings().clientPrintJobSettings().suppressScalingMessage(true);

This is very effective and will do the job.
Only, this requires you to modify every report with these kind of messages.

A nicer way would be if we could switch it off in one place for all reports. Fortunately, this is possible as well.
Go to class SysReportRun, in the Run method, place following code before the call to super:

if(this.printJobSettings())
this.printJobSettings().suppressScalingMessage(true);

//this.printJobSettings().clientPrintJobSettings().suppressScalingMessage(true);
// VAR Changed on 06 Feb 2012 at 14:14:30 by JXie7519
void run(boolean onlyReport = false)
{
    // If this report is a webReport the run a webReport.
    if (webSession() && runBaseReport)
    {
        runBaseReport.runWebReport();
    }
    else
    {
        // When running the report and onlyReport = true then run the report.
        if (!onlyReport && runBaseReport)
        {
            if (runBaseReport.prompt())
            {
                runBaseReport.run();
            }
            // If the prompt returns false the do not run the report.
            // The RunBaseReport.Run method calls the ReportRun.run method with the parm onlyReport = true.
            return;
        }
    }

    this.buildPrintGrandTotal();
    this.buildPrintOnlyTotals();

    // VAR Changed on 06 Feb 2012 at 14:14:30 by JXie7519 Begin
    if(this.printJobSettings().clientPrintJobSettings())
    {
        this.printJobSettings().clientPrintJobSettings().suppressScalingMessage(true);
        this.suppressReportIsEmptyMessage(true);
    }
    else
    {
        this.printJobSettings().suppressScalingMessage(true);
        this.suppressReportIsEmptyMessage(true);
    }
    // VAR Changed on 06 Feb 2012 at 14:14:30 by JXie7519 End

    super();
}

Now we don't have to modify each and every report and our users are happy.

Note that you can still override the settings in your report. In some reports this is done by default, like SalesInvoice and SalesConfirm.

2012年7月5日星期四

How to using infolog.globalCache in Class

1)
// USR Changed on 05 七月 2012 at 17:07:36 by Admin
static void THK_SysGlobalCacheSetFromClass(Args _args)
{
    SysGlobalCache  globalCache = infolog.objectOnServer() ? appl.globalCache() : infolog.globalCache();
;
    if (!globalCache.isSet(curUserId(), sessionId()))
    {
        infoLog.globalCache().set(curUserId(), sessionId(), "Split");
        appl.globalCache().set(curUserId(), sessionId(), "Split");
    }
    try
    {
        //do something ... ...
    }
    catch(Exception::Error)
    {
        if (globalCache.isSet(curUserId(), sessionId()))
            globalCache.remove(curuserid(), sessionid());
    }
    if (globalCache.isSet(curUserId(), sessionId()))
        globalCache.remove(curuserid(), sessionid());
}
2)
// USR Changed on 05 七月 2012 at 17:07:40 by Admin
static void THK_SysGlobalCacheGetFromClass(Args _args)
{
    anyType                 cacheValue;
    SysGlobalCache          globalCache = infolog.objectOnServer() ? appl.globalCache() : infolog.globalCache();
;
    if(globalCache.isSet(curuserid(),sessionid()))
    {
        cacheValue = globalCache.get(curuserid(),sessionid());
    }
}

How to using infolog.globalCache in Form

1)AOT\Forms\Jimmy_LabelLists\init()
public void init()
{
    super();
    LabelModuleId.text("SYS");
    infolog.globalCache().set(FormStr(Jimmy_LabelLists),"RefreshCurrentForm",element);
}
2) AOT\Forms\Jimmy_LabelLists\canClose()
public boolean canClose()
{
    boolean ret;
    ret = super();
    infolog.globalCache().remove(FormStr(Jimmy_LabelLists),"RefreshCurrentForm");
    return ret;
}
3)AOT\Forms\Jimmy_LabelLists\button\
void clicked()
{
;
    Jimmy_Label::Jimmy_FindUnUsedLabels();
    //super();
}
AOT\Classes\Jimmy_Label\Jimmy_FindUnUsedLabels()
static void Jimmy_FindUnUsedLabels()
{
    str 50              labelId;
    int                 m,NumModule;
    int                 maxLabel,LookModule,initTimeNow;
    LabelModuleId       labelModuleId;
    LanguageId          LanguageEN,LanguageCN,LanguageFR;
    Dialog              dialog  = new Dialog("Update Labes");
    DialogField         dlgMax  = dialog.addField(types::Integer,"Max Lable Num.","127510");
    Container           conValue;
    #Macrolib.AviFiles
    SysOperationProgress    progress;
    Jimmy_LabelLists        LabelLists;
    Jimmy_LabelModuleTable  labelModuleTable,UpLabelTable;
    FormRun                 fr;
    FormStringControl       formStringControl;
    FormDataSource          formDataSource;
int findLabelFromModuleId(LabelModuleId    _LabelModuleId)
{
    str                 LabelCN,LabelEN,LabelFR;
    int                 i,numbers;
    int                 maxLabelKey;
    xRefNames           xRefNames;
    XRefReferences      XRefReferences;
;
    maxLabelKey = Jimmy_LabelModuleTable::find(_LabelModuleId).MaxLabelKey;
    while (i <= maxLabel)
    {
        labelId = "@" + _LabelModuleId + int2str(i);
        progress.incCount(1);
        progress.setText(strfmt("正在更新 - %1%2 %3 -  进度 - %4%  [已完成 %5 / %6]",
                        "@",
                         _LabelModuleId,int2str(i),
                         i / (labelModuleTable.MaxLabelKey ? labelModuleTable.MaxLabelKey : maxLabel) * 100,
                         m,
                         LookModule)
                         );
        //this will show the operation progress
        /*
        //Find if the label id has an cross reference record
        select recid from xRefNames
            where xRefNames.Name == labelid
        exists join XRefReferences
            where xRefNames.RecId == XRefReferences.xRefNameRecId;*/
        LabelCN = SysLabel::labelId2String(labelId,LanguageCN);
        LabelEN = SysLabel::labelId2String(labelId,LanguageEN);
        LabelFR = SysLabel::labelId2String(labelId,LanguageFR);
        if(LabelFR == labelId)
            LabelFR = "";
        if(LabelEN == labelId)
            LabelEN = "";
        if(LabelCN == labelId)
            LabelCN = "";
        //If there is no record in cross reference then log it
        if (//! xRefNames.RecId &&
           (LabelCN || LabelEN || LabelFR) &&
        //avoid logging labels that are already deleted (This is because of sequential check like IFC1, IFC2, IFC3 etc...)
            LabelCN != labelId)
        {
            numbers++;
            LabelLists.clear();
            LabelLists.LabelId          = labelId;
            LabelLists.LabelEN          = LabelEN;
            LabelLists.LabelCN          = LabelCN;
            LabelLists.LabelFR          = LabelFR;
            LabelLists.KeyId            = i;
            LabelLists.LabelType        = "@" + _LabelModuleId;
            LabelLists.LabelModuleId    = _LabelModuleId;
            LabelLists.doInsert();
        }
        i++;
        if(maxLabelKey && i > maxLabelKey)
            break;
       // progress.incCount();
        progress.update(true);
    }
    return numbers;
}
;
    dialog.doInit();
    conValue = xSysLastValue::getValue(curext(),curuserid(),UtilElementType::ClassStaticMethod,funcname(),"");
    if(conValue == conNull())
        dlgMax.value(0);
    else
        dlgMax.value(conpeek(conValue,1));
    if(!dialog.run())
        return;
    global::startLengthyOperation();
    initTimeNow     = timeNow();
    maxLabel        = dlgMax.value();
    LanguageCN      = "ZH-HANS";
    LanguageEN      = "EN-US";
    LanguageFR      = "FR";
    select count(RecId) from labelModuleTable
        where labelModuleTable.isActived == Noyes::Yes;
    LookModule = int642int(labelModuleTable.RecId,false);
    while select labelModuleTable
        where labelModuleTable.isActived == Noyes::Yes
    {
        m++;
        delete_From LabelLists
            where LabelLists.LabelModuleId == labelModuleTable.LabelModuleId &&
                  LabelLists.KeyId <= maxLabel;
        progress = new SysOperationProgress();
        progress.setCaption(strfmt("Update[%1] - %2 - %3" ,
                            labelModuleTable.LabelModuleId,
                            labelModuleTable.MaxLabelKey ? labelModuleTable.MaxLabelKey : maxLabel,
                            funcname()));
        progress.setAnimation(#AviUpdate);
        if(labelModuleTable.MaxLabelKey)
            progress.setTotal(labelModuleTable.MaxLabelKey);
        else
            progress.setTotal(maxLabel);
        NumModule = findLabelFromModuleId(labelModuleTable.LabelModuleId);
        info(strfmt("%1 - %2 [have %3 Labels]",m < 10 ? "0" + int2str(m) : int2str(m),labelModuleTable.LabelModuleId,NumModule));
    }
    ttsbegin;
    while select forupdate UpLabelTable order by UpLabelTable.LabelModuleId
    {
        select firstonly LabelLists order by LabelLists.KeyId desc
            where LabelLists.LabelModuleId == UpLabelTable.LabelModuleId;
        if(LabelLists.KeyId > UpLabelTable.MaxLabelKey)
        {
            UpLabelTable.MaxLabelKey = LabelLists.KeyId;
            UpLabelTable.update();
        }
    }
    ttscommit;
    if(infolog.globalCache().get(FormStr(Jimmy_LabelLists),"RefreshCurrentForm"))
    {
        fr = infolog.globalCache().get(FormStr(Jimmy_LabelLists),"RefreshCurrentForm");
        formStringControl = fr.design().controlName("LabelModuleId");
        formStringControl.text("SYS");
        formDataSource = fr.dataSource("Jimmy_LabelLists");
        if(formDataSource)
        {
            formDataSource.reread();
            formDataSource.refresh();
            formDataSource.executeQuery();
            //fr.dataSource("Jimmy_LabelLists").research();
            formDataSource.findRecord(Jimmy_LabelLists::find("SYS",127239));//127510
        }
    }
    global::endLengthyOperation();
    xSysLastValue::putValue([maxLabel],curext(),curUserId(),UtilElementType::ClassStaticMethod,funcname(),"");
    info(strfmt("Total took to [ %1 ] times",global::time2StrHMS(timenow() - initTimeNow)));
}

How to handling Key strokes in AX

The task method in a form will serve the purpose the handling keystroke events to some extent.
Though we cannot catch all the keystrokes, we can handle a few of them:

For example,
 
public int task(int _taskId)
{
    int ret;
    #task
    ;
    if (_taskId == #taskesc)
    {
        Box::okCancel("I am not going to listen to you and u can’t close me with a silly Escape button", DialogButton::Ok);
    }
    else
    {
        ret = super(_taskId);
    }
    return ret;

}

All the tasks defined in the task macro can be handled. Apart from those, other events are:
F1, Ctrl + N, Ctrl + C, Ctrl +v, etc … just put the following line in the task method to get these values:

Info (int2str (_taskId));

Global Cache

Many times because of flawed implementation designs, we often need global variables. We may use a table with a key field and a container field for this purpose, but the simplest way of doing this will be using a Global Cache.
A global cache is an instance of class – SysGlobalCache, which is nothing but a Map containing Maps with Userid as Key. These Maps contain the actual value and a key.
In Ax, we have three(infact 4) Global Caches – Infolog.globalCache(), Appl.globalCache(), ClassFactory.GlobalCache().
How to use:
To Set a Value:
static void GlobalCacheSet(Args _args)
{
SysGlobalCache globalCache;
;
globalCache = ClassFactory.globalCache();
globalCache.set(curuserid(), 1, "One");
}
To Get the Value:
static void GlobalCacheGet(Args _args)
{
SysGlobalCache globalCache;
;
globalCache = ClassFactory.globalCache();
print globalCache.get(curuserid(), 1);
globalcache.remove(curuserid(), 1);
pause;
}
In the above the example, we can also use Infolog.globalCache() or Appl.globalCache().
Why do we have three Caches?
Its simple, the term "Caching" comes when there is something to do with Performance.
Infolog Object resides in Client and Application Object resides in Server.
To share your global variable across the network and accept the performance penalty of a client/server call, use the infolog variable (Info class) or appl variable (Application class) instead of ClassFactory.
ClassFactory is a special class, which has instances residing at both server and client. At run time, two instances of the ClassFactory class exist, and they share name classFactory. However confusing the implementation details might sound, this is a powerful concept. When you call a method on classFactory from code running on the client, you are calling the classFactory object on the client tier; when you call a method on classFactory from code running on the server, you are calling the classFactory object on the server tier. Remember this when debugging the ClassFactory class.
These four objects along with an instance of VersionControl will sustain for a duration of Dynamics Ax client session.

How to using query Multiple value

// USR Changed on 05 七月 2012 at 15:19:34 by Jimmy Xie
static void THK_queryMultipleValue(Args _args)
{
    query                   q = new query();
    queryRun                qr;
    queryBuildRange         qbr;
    queryBuildDataSource    qbds;
    TransDate               AsofDate    = systemdateget();
    projId                  projId      = "30001";
    str                     criteria;
    name                    projAdjustRefId  = "01130850_001";
    InventTrans             InventTrans;
    int                     i;
;
    qbds = q.addDataSource(tablenum(InventTrans));
/*
    qbds.addRange(fieldnum(InventTrans,DateFinancial)).value(queryRange(dateNull(),AsofDate));
    qbds.addRange(fieldnum(InventTrans,TransType)).value(queryValue(InventTransType::Project));
    qbds.addRange(fieldnum(InventTrans,ProjId)).value(projId);
    qbds.addRange(fieldnum(InventTrans,projAdjustRefId)).value(projAdjustRefId);
*/
    qbds.addSelectionField(fieldnum(InventTrans, Qty),SelectionField::Sum);
    qbds.orderMode(orderMode::GroupBy);
    qbds.addSortField(fieldnum(InventTrans,DateFinancial));
    qbds.addSortField(fieldnum(InventTrans,ItemId));
    //注意:
    //条件务必保持有值,不然记录会为空;如果是字符串值必须加上双引号,枚举,日期值不需要
    qbr = SysQuery::findOrCreateRange(qbds,fieldnum(InventTrans,DataAreaid));
    criteria = strFmt(
                        '(%1) && (%1<=%2) && (%3==%4) && (%5=="%6") && (%7=="%8")',
                        fieldStr(InventTrans, DateFinancial),       // %1  - ok!
                        Date2StrXpp(AsofDate),               // %2  - ok!
                        fieldStr(InventTrans, TransType), //%3
                        any2int(InventTransType::Project), //%4
                        fieldStr(InventTrans, ProjId), //%5
                        projId ,                        //6
                        fieldStr(InventTrans, projAdjustRefId),//%7 字符串
                        projAdjustRefId //8
                        );
    qbr.value(criteria);
    qr = new queryRun(q);
    info(qr.query().dataSourceNo(1).toString());
    while (qr.next())
    {
        InventTrans = qr.getNo(1);
       
        i++;
        info(strfmt("%1 - %2 - %3 [%4]",i,InventTrans.ItemId,InventTrans.DateFinancial,InventTrans.Qty));
    }
}