David Park's profileDavid's spaceBlogLists Tools Help

David's space

April 09

How to: Set Up Visual Studio for Enterprise Portal Development

To set up Visual Studio for Enterprise Portal development

  1. You must be using Visual Studio 2008 Standard Edition, Visual Studio 2008 Professional Edition, or Visual Studio 2008 Team System.

  2. When selecting the components to install, be sure to include the following:

    • Visual C#

    • Visual Web Developer

  3. Run the Microsoft Dynamics AX Setup and choose to install the Enterprise Portal Tools. These tools include the following components:

    • Visual Studio add-in

    • Project templates

    • Toolbox components

    NoteNote

    When Enterprise Portal Tools are installed for Visual Studio, the Visual Studio environment is automatically reconfigured to make the tools available. This process may take several moments to finish.

April 08

Creating a run-time lookup form

 

A standard lookup form is created through relations on the database table, and on the Extended Data Type. To learn about the standard lookup form, click .

If, however, you need to create a runtime lookup form that looks up other database fields than the ones offered by the standard lookup form, use the application class SysTableLookup and override the lookup method on the relevant form control.

How to use the SysTableLookup class

 

  1. Create a new instance of SysTableLookup where 'this' is the current form control

   SysTableLookup    sysTableLookup = SysTableLookup::newParameters(tableNum(custTable), this);

  1. Add the fields to be shown in the lookup form

    sysTableLookup.addLookupField(fieldNum(custTable, accountNum));

  1. Limit the data selection.

    queryBuildDataSource = query.addDataSource(tableNum(custTable));

    queryBuildRange      = queryBuildDataSource.addRange(fieldNum(custTable, accountNum));

    queryBuildRange.value('A..B');

    sysTableLookup.parmQuery(query);

  1. Perform the lookup, and delete super().
    super() will create an autogenerated lookup form.

    sysTableLookup.performFormLookup();

//    super()

}

 

A complete example of overriding the lookup method on a form control

 

void lookup()

{

    Query                   query          = new Query();

    QueryBuildDataSource    queryBuildDataSource;

    QueryBuildRange         queryBuildRange;

 

// Create an instance of SysTableLookup where 'this' the current Form control.

 

    SysTableLookup          sysTableLookup = SysTableLookup::newParameters(tableNum(custTable), this);

    ;

 

// The field to be shown in the lookup form.

 

    sysTableLookup.addLookupField(fieldNum(custTable, accountNum));

    sysTableLookup.addLookupField(fieldNum(custTable, name));

 

// Limit and arrange data selection.

 

    queryBuildDataSource = query.addDataSource(tableNum(custTable));

    queryBuildRange      = queryBuildDataSource.addRange(fieldNum(custTable, accountNum));

    queryBuildRange.value('A..B');

    sysTableLookup.parmQuery(query);

 

// Perform lookup

 

    sysTableLookup.performFormLookup();

 

// do not call super().

 

//    super()

}

 

 

 

 

 

void lookup()

{

  Query query = new Query();

  QueryBuildDataSource queryBuildDataSource;

  QueryBuildRange queryBuildRange;

  SysTableLookup sysTableLookup;

  TableId tableId;

  FieldId fieldId;

  ;

  tableId = tablename2id('myTable');

  sysTableLookup.parmTableId(tableId);

  fieldId = fieldname2id(tableId, 'MyField_1');

  sysTableLookup.addLookupfield(fieldId);

  fieldId = fieldname2id(tableId, 'MyField_2');

  sysTableLookup.addLookupfield(fieldId);

 

  queryBuildDataSource = query.addDataSource(tableId);

  queryBuildDataSource.orderMode(OrderMode::GROUPBY);

  queryBuildDataSource.addSortField(fieldId));

 

  sysTableLookup.parmQuery(query);

 

  this.performFormLookup(sysTableLookup.formRun());

}

This manner of creating a lookup form is used in the DocuType form in the application. The full path to the modified lookup method is:

\Forms\DocuType\Designs\Design\[Tab:Tab]\[TabPage:Overview]\[Grid:Grid]\StringEdit:ActionClassName\Methods.

Note

The SysTableLookup is limited to making lookups on fields in a table.

February 27

Functions of Container

FROM AX DOCUMENT

ConIns

Syntax 

container ConIns (container container, 
             int start, 
             anytype element,... ) 

Exception message 

Inserts elements in the container specified by container. The elements are inserted at the position specified by start. If several elements are specified, they must be separated by commas. To insert an element as the first in a container, specify start as 1. To insert after the nth element specify start as n+1.

Example 

static void Job_Examples_conins_conlen_conpeek(Args _args)
{
  container c;
  int i;
  ;

  c = conins(["item1", "item2"], 1);  // conins to insert into container
  for (i = 1 ; i <= conlen(c) ; i++)  // conlen for the size of the container
  {
    print conpeek(c, i);              // conpeek to get contents of container item without removing it
  }

  pause;
}
 

Notes

You can also assign elements to a container:
container c = ["Hello World", 1, 3.14];

You can use the += operator to add values of any type into a container. For example, to create a container containing the first ten square numbers:
int i;
container c;
;

for (i = 1; i < = 10; i++)
{
    c += i+i;
}

See Also 

ConFind, ConDel, ConLen, ConNull, ConPeek, ConPoke

ConFind

Syntax 

int ConFind (container container, anytype element,... ) 

Exception message 

Locates the first occurrence of a specific sequence of elements in the container specified by container. If several elements are specified in the sequence, they must be separated by commas and given in the correct sequence. The elements can be of any data type.

Return Value 

The function returns 0 (the item was not found) or the item’s sequence number. 

Example 

static void Job_Examples_confind(Args _args)
{
  container c = ["item1", "item2", "item3"];
  int i;
  ;
 
  i = confind(c, "item2");
  print @"Position of 'item3' in container is " + int2str(i);
  pause;
}

See Also 

ConDel , ConIns , ConLen , ConNull , ConPeek , ConPoke

 

ConLen

Syntax

[HEADER]

Exception message  

Returns the number of elements in the container specified by container.

Example 

static void Job_Examples_conins_conlen_conpeek(Args _args)
{
  container c;
  int i;
  ;

  c = conins(["item1", "item2"], 1);  // conins to insert into container
  for (i = 1 ; i <= conlen(c) ; i++)  // conlen for the size of the container
  {
    print conpeek(c, i);              // conpeek to get contents of container item without removing it
  }

  pause;
}

See Also

ConFind, ConDel, ConIns, ConNull, ConPeek, ConPoke

 

ConNull

Syntax 

[HEADER]

Exception message 

Returns a null container. Use the function to explicitly dispose of the contents of a container.

Example 

static void Job_Examples_connull(Args _args)
{
  container c = ["item1", "item2", "item3"];
  ;

  print "Size of container is " + int2str(conlen(c));
  c = connull();  // set container to null
  print "Size of container after connull() is " + int2str(conlen(c));
  pause;
}

See Also 

ConFind, ConDel, ConIns, ConLen, ConPeek, ConPoke

 

ConPeek

Syntax 

[HEADER]

Exception message 

Returns a specific element from the container specified by container. The element is specified by number. Specify 1 to get the first element.

The conpeek function automatically converts the peeked item into the expected return type. Strings can automatically be converted into integers and reals, and vice versa.

Example

static void Job_Examples_conins_conlen_conpeek(Args _args)
{
  container c;
  int i;
  ;

  c = conins(["item1", "item2"], 1);  // conins to insert into container
  for (i = 1 ; i <= conlen(c) ; i++)  // conlen for the size of the container
  {
    print conpeek(c, i);              // conpeek to get contents of container item without removing it
  }

  pause;
}

Note 

You can extract more than one element from a container with a composite assignment:
void myMethod()
{
    str 10 s;
    int i;
    real r;
    container c = [1,3.14,"Hello"];
    ;

    // Extracts 1 into i, 3.14 into r and "Hello" into s
    [i,r,s] = c;
}

See Also 

ConFind, ConDel, ConIns, ConLen, ConNull, ConPoke

 

ConPoke

Syntax 

container ConPoke (container container, 
                  int start, 
                  anytype element,... ) 

Exception message 

Modifies the container specified by container by replacing one or more of the existing elements with the specified elements. The position of the first element to be replaced is specified by start. If several elements are specified, they must be separated by commas. To replace the first element, specify start as 1.

Example 

static void Job_Examples_conpoke(Args _args)
{
  container c1 = ["item1", "item2", "item3"];
  container c2;
  int i;
  void conPrint(container c)
  {
      for (i = 1 ; i <= conlen(c) ; i++)
      {
        print conpeek(c, i);
      }
  }
  ;
  conPrint(c1);
  c2 = conpoke(c1, 2, "PokedItem");
  print "";
  conPrint(c2);
  pause;
}

ConFind , ConDel , ConIns , ConLen , ConNull , ConPeek


 

March 26

Starting the code profiler from code

original link: http://axstart.spaces.live.com/

 

The code profiler is a tool that tells you how many milliseconds a line of x++ code need to be executed. This tool is most of time used for analyses of defined scenario of your process. This code profiler runs the code in debug mode. So a process of 10 minutes can take 30 minutes when the code profiler is active. 

So a large run of 1 hour, reproduce with the code profiler, makes no since. The trick is to profile small part of the process. We can do this with existing macros in AX.

#profileBegin('myrun')

//mycode

#profileEnd

 

Later on you have to calculate the sum and line totals for that run. Give this run a new name, otherwise you don’t get the line, method and class totals. I think this is a bug.

March 14

Send Mail through code

AX uses CDO.Message component to send a email and Here's a example.
 
static void SendMailQuick(Args _args)
{
    SysMailer   mailer;
    ;
   
    mailer = new SysMailer();
   
    mailer.quickSend("david@dickiesmedical.com", "david@dickiesmedical.com", "test subject", "test body", "sjin@dickiesmedical.com", "c:\\testfile.txt");
}
 
 
or other way
 
 before using this way you need to setup email parameters through Administration - Setup - E-mail parameters
static void sendMail(Args _args)
{
    #SysMailer
    #DEFINE.SenderName('David Park')
    #DEFINE.Sender('david@dickiesmedical')
    #DEFINE.Recipient('david@dickiesmedical.com')
    #DEFINE.Recipient2('sjin@dickiesmedical.com')
    #DEFINE.Subject('test mail subject')
    #DEFINE.MessageBody('test mail message body')
   
    SysMailer   mailer;
    SysMailerAddressField   tos;
   
    FileIOPermission fileIOPermission;
    InteropPermission interopPermission;
   
    str relayServer;
    int portNumber;
    str userName;
    str password;
    boolean NTLM;
   
    str filePathName;
    str tempPath;
   
    SysEmailParameters parameters = SysEmailParameters::find();
    ;
   
    if (parameters.SMTPRelayServerName)
        relayServer = parameters.SMTPRelayServerName;
    else
        relayServer = parameters.SMTPServerIPAddress;
       
    portNumber = parameters.SMTPPortNumber;
    userName = parameters.SMTPUserName;
    password = SysEmailParameters::password();
    NTLM = parameters.NTLM;
   
    //fileIOPermission = new FileIOPermission('','r');
    //fileIOPermission.assert();
   
    //tempPath = WinAPIServer::getTempPath();
   
    //CodeAccessPermission::revertAssert();
   
    interopPermission = new InteropPermission(InteropKind::ComInterop);
    interopPermission.assert();
    mailer = new SysMailer();
    CodeAccessPermission::revertAssert();
   
    mailer.SMTPRelayServer(relayServer, portNumber, userName, password, NTLM);
   
    mailer.fromAddress(#Sender,#SenderName);
 
    tos = mailer.tos();
    //tos.appendAddress("david@dickiesmedical.com,sjin@dickiesmedical.com");
    tos.appendAddress(#Recipient);
 
    mailer.priority(1);
    mailer.subject(#Subject);
    mailer.htmlBody(#MessageBody);
   
    mailer.sendMail();
}
 
Or
 
If you are up and running E-mail distributor batch, then simple insert apropriate data into SysOutgoingEmailTable and SysOutgoingEmailData(Optional). batch will get the record from the table and try to send out.
 
March 12

Create Progress Indicators

Use a progress indicator during operations that take more than 2 seconds.

  • Use an hourglass mouse pointer if the process takes 2-7 seconds.

  • Use a progress bar if the process takes 8 seconds or more.

Display an Hourglass Pointer

  1. Call the startLengthyOperation method when the operation starts.

  2. Call the endLengthyOperation method when the operation ends.

Both of these methods are on the Global application class.

The hourglass pointer is displayed until you call the endLengthyOperation method.

Note
You do not have to declare a variable when you use the Global class.

Example: Display an Hourglass Pointer

The following example overrides the clicked method on a form control to refresh the database log.

void clicked()

{

    super();

    startLengthyOperation();

    sysDatabaseLog_ds.research();

    endLengthyOperation();

}

Display a Progress Bar

  1. Initialize a SysOperationProgress variable.

  2. Set a caption for the form by using the SysOperationProgress.setCaption method.

  3. Set an animation to run while the operation is in progress by using the SysOperationProgress.setAnimation method.

    A number of animations are provided with Microsoft Dynamics AX. To view them, run the Tutorial_ShowAVIFiles class. If you use one of these animation files, you need to declare the AviFiles macro at the top of your code.

  4. Specify the total number of operation steps.

    This is needed for the time-remaining calculation. If you do not set the total number of operation steps, the progress indicator is not shown. The total is often a count of the number of records, and may be time-consuming to calculate. Don't specify the total if the time is taken to calculate the records is comparable to the total time taken for the operation.

  5. Perform the operation. For each step, specify a description and a step number. For example:

    for (i = 1; i <= 100; i++)

    {

        progress.setText(strfmt("Step %1", i));

        progress.incCount();

    }

    The description must be short and informative because it might change quickly during the execution.

    As an alternative to incCount() you can use setCount(int i).

During the execution, the progress indicator is updated accordingly. The estimated time remaining is calculated and displayed.

The default update interval is 3 seconds. If the task of updating the display takes more than 10% of the update interval due to latency on the network connection, the update interval is increased by 1 second.

Tip
Separate an operation into as many steps as possible. This gives the user the best information and the best remaining-time estimate. The time spent informing the user has no impact because the form is updated only once a second—even less frequently on low bandwidth connections.

Example: Use a Single Progress Indicator

The following example creates a simple progress indicator.

static void operationProgress(Args _args)
{
    #AviFiles
    SysOperationProgress progress = new SysOperationProgress();
    int i;

    ;

    progress.setCaption("My Task");
    progress.setAnimation(#AviUpdate);
    progress.setTotal(30000);
    for (i = 1; i <= 30000; i++)
    {
        progress.setText(strfmt("Step %1", i));
        progress.setCount(i, 1);
    }
}

Use More Than One Progress Indicator

If you have a more complex operation, you can have more than one progress indicator. All of the previously described methods have a default parameter that indicates which progress indicator (or bar) is being referred to.

Note
The time-remaining calculation is done solely on the first bar. The first bar must always show the overall progress.

Example: Use Three Progress Indicators

static void SysOperationProgress(Args _args)
{
    #define.count1(10)
    #define.count2(5)
    #define.count3(200)
    #AviFiles
    
    // 3 bars.
    SysOperationProgress progress = new SysOperationProgress(3);
    int i, j, k;
    ;
 
    progress.setCaption("My Task");
    progress.setAnimation(#AviUpdate);
 
    // Bar 1.
    progress.setTotal(#count1, 1);
    // Bar 2.
    progress.setTotal(#count2, 2);
    // Bar 3.
    progress.setTotal(#count3, 3);
 
    for (i=0; i<#count1; i++)
    {
        // Bar 1.
        progress.setText(strfmt("Bar 1 - Step %1 of %2", i, #count1), 1);
        progress.setCount(i, 1);
        for (j=0; j<#count2; j++)
        {
            // Bar 2.
            progress.setText(strfmt("Bar 2 - Step %1 of %2", j, #count2), 2);
            progress.setCount(j, 2);
            for (k=0; k<#count3; k++)
            {
                // Bar 3.
                progress.setText(
                    strfmt("Bar 3 - Step %1 of %2", k, #count3), 3);
                progress.setCount(k, 3);
                sleep(20);      // Time-consuming task.
            }
        }
    }
}

User Input During an Operation

Following are progress indicator options for user input during an operation:

  • SysOperationProgress.hide – hides the progress form and pauses the time-remaining calculation. The progress indicator reappears when setCount, incCount, or setText is subsequently called.

  • SysOperationProgress.kill – terminates the progress form and starts the progress again. You don't have to reset the caption, animation, and total.

Using Progress Indicators with the Runbase Framework

Runbase is the standard framework for job execution, and must have a progress indicator. In the standard application, most progress indicators are used from the RunBase framework.

Use the RunBase.progressInit method to initialize the progress indicator:

public void progressInit(

    str caption,

    int total,

    Filename animation,

    int updateInterval = 1,

    int numOfBars = 1)

Indicating progress during the actual operation is similar to the standard Operation Progress framework. Use the member variable progress:

progress.incCount();

If you have more than one progress bar, use the progress variable defined in RunBase. It points to the Progress class initialized in the progressInit method. For example:

{
    this.progressInit("Caption",10,#AviFileCopy,1,2);
    progress.setCount(bar1count);
    progress.setTotal(maxBar2Count,bar2count);
    ...
    ...
    progress.setCount(bar2count,2);
}

February 27

selection of several records in a grid (Multi select)

To unnecessary code to avoid duplications, we can even with a for-loop work:

void clicked() Void clicked ()
{ (
CustTable custTable; CustTable custTable;
;
for (custTable = CustTable_ds.getFirst(true) ? For (custTable = CustTable_ds.getFirst (true)?
CustTable_ds.getFirst(true) : CustTable_ds.getFirst (True):
CustTable_ds.cursor(); CustTable_ds.cursor ();
custTable; CustTable;
custTable = CustTable_ds.getNext()) CustTable = CustTable_ds.getNext ())
{ (
//do something with custTable / / Do something with custTable
info(custTable.accountNum); Info (custTable.accountNum);
} )
} )

CustTable_ds.getFirst() gibt nur etwas zurück, wenn mehrere Datensätze markiert sind. CustTable_ds.getFirst () is only slightly back when several records are marked. Man kann sich also die Abfrage nach CustTable_ds.anyMarked() sparen. It is also the query () CustTable_ds.anyMarked save.
February 18

how to init the form in the maximized mode?

I think it depends on design properties of your form.

My form has following properties
Width - Column width
Height - Column height

WindowResize - Dynamic

and move code I suggested in run() method
--
Ruslan Goncharov

http://rusgon.blogspot.com/


"M Elsawah" wrote:

> Hi Ruslan
>
> Thanks alot for your help
> but
> It works but over the width only, i want the form to look like the action of
> clicking on Maximize button, have you got what i mean?
>
> Thanks.
>
> "Ruslan Goncharov" wrote:
>
> > Try to do following lines in the init() method on the form
> > public void init()
> > {
> >     #WinApi
> >     ;
> >     super();
> >
> >     WinApi::showWindow(this.hWnd(), #SW_SHOWMAXIMIZED);
> > }
> > --
> > Ruslan Goncharov
> >
> > http://rusgon.blogspot.com/
> >
> >
> > "M Elsawah" wrote:
> >
> > > Hi all,
> > > I just ask about how to make the form open in the maximized mode in both
> > > width and height? also i want to ask how to make the controls resized
> > > relative to the form size?
> > > Please try to reply me ASAP
> > > Thanks.
January 31

save and open word document

static void WordTemplateOpen()
{
  COM wordApp;
  COM wordDocuments;

  wordApp = new COM("word.application");
  wordDocuments = wordApp.Documents();

  wordDocuments.Open("c:\\MyBelovedTemplate.dotx");
  wordApp.visible(true);
}


static void WordSaveAs()
{

  COM wordApp;
  COM wordDocuments;
  COM wordDocument;
  COM wordRng;

  wordApp = new COM("word.application");
  wordApp.visible(false);
  wordDocuments = wordApp.Documents();
  wordDocument = wordDocuments.add();
  wordRng = wordDocument.range(0,0);
  wordRng.insertafter("Hallo Microsoft Word");

  wordDocument.SaveAs("C:\\MyBelovedWordDocument.doc");

  wordApp.visible(true);
}

"mason" wrote:
November 30

How to send data to Excel

static void Send_toExcel(Args _args) {

   SysExcelApplication         ExcelApplication;
   SysExcelWorkBooks           ExcelWorkBooks;
   SysExcelWorkBook            ExcelWorkBook;
   SysExcelWorkSheets          ExcelWorkSheets;
   SysExcelWorkSheet           ExcelWorkSheet;
   SysExcelRange               ExcelRange;
   CustTable                   ct;
   int                         row;
   ;
   ExcelApplication = SysExcelApplication::construct();
   ExcelApplication.visible(true);
   ExcelWorkBooks = ExcelApplication.workbooks();
   ExcelWorkBook = ExcelWorkBooks.add();
   ExcelWorkSheets = ExcelWorkBook.worksheets();
   ExcelWorkSheet = ExcelWorkSheets.itemFromNum(1);
   while select * from ct
   {
       row++;
       ExcelWorkSheet.cells().item(row,1).value(ct.AccountNum);
       ExcelWorkSheet.cells().item(row,2).value(ct.Name);
   }

} --Gundhawk 02:30, 15 September 2007 (EDT)

 

=================================================================

static void CreateExcelDokument(Args _args)
{
   SysExcelApplication xlsApplication;
   SysExcelWorkBooks xlsWorkBookCollection;
   SysExcelWorkBook xlsWorkBook;
   SysExcelWorkSheets xlsWorkSheetCollection;
   SysExcelWorkSheet xlsWorkSheet;
   SysExcelRange xlsRange;
   CustTable custTable;
   int row = 1;
   str fileName;
   ;
   // Name des Exceldokuments.
   fileName = "C:\\test.xsl";

   // Excel initalisieren und öffnen.
   xlsApplication = SysExcelApplication::construct();
   xlsApplication.visible(true);

   // Neues Excel Worksheet erzeugen.
   xlsWorkBookCollection = xlsApplication.workbooks();
   xlsWorkBook = xlsWorkBookCollection.add();
   xlsWorkSheetCollection = xlsWorkBook.worksheets();
   xlsWorkSheet = xlsWorkSheetCollection.itemFromNum(1);

   // Zellenüberschriften in das Worksheet schreiben.
   xlsWorkSheet.cells().item(row,1).value('Account Num');
   xlsWorkSheet.cells().item(row,2).value('Name');

   row++;

   // Excel Worksheet mit Daten füllen (Excel-Zellen füllen).
   while select custTable
   {
      xlsWorkSheet.cells().item(row,1).value(custTable.AccountNum);
      xlsWorkSheet.cells().item(row,2).value(custTable.Name);
      row++;
   }

   // Prüfen ob das Dokument schon existiert.
   if(WinApi::fileExists(fileName))
   {
      WinApi::deleteFile(fileName);
   }

   // Excel Dokument speichern.
   xlsWorkbook.saveAs(fileName);

   // Excel schließen.
   xlsApplication.quit();
   xlsApplication.finalize();
}

November 14

reset temporary table

From Kashperuk Ivan
=================
 
You can use 3 ways: 1 is slow, 2 are fast.

Slow way:
delete_from tmpTable;

Fast way 1:
tmpTable = null; // Use this only if you are experienced in using tmpTables
and handling objects

Fast way 2:
void method()
{
    TmpTable tmpTable;

    void clearTmpTable()
    {
        TmpTable tmpTableLocalEmpty;
        ;
        tmpTable.setTmpData(tmpTableLocalEmpty);
    }

    tmpTable.SetTmpData(Class::getData());
    //here tmpTable has data
    clearTmpTable()
    //here tmpTable doesn't have data
}

Which way to use is for you to decide

--
Kashperuk Ivan (Vanya), Dynamics AX MCBMSS
My blog - http://kashperuk.blogspot.com
MorphX IT in Russian - http://www.lulu.com/content/723888


"HY Choo" wrote:

> Hi all,
>
> how to reset the temp table to initial state after settmpdata?
>
> TIA
>
> Choo
September 12

CAS (Code Access Security)

=======================================================\
 
Code Access Security

Code Access Security (CAS) helps protect APIs that have potential security risks when the APIs are running on the server.

CAS-enabled APIs called on the server require the use of a permission class—one of the classes derived from CodeAccessPermission. If permission to use the API is not asserted, the following error is generated:

Request for the permission of type '%1' failed.

This error is also generated if permission is asserted, but the code is running on the client. Permission is required only for CAS-enabled APIs that run on the server. The string supplied in the error message is the name of one of the following permission classes:

  • ExecutePermission
  • FileIoPermission
  • InteropPermission
  • RunAsPermission
  • SkipAOSValidationPermission
  • SqlDataDictionaryPermission
  • SqlStatementExecutePermission
  • SysDatabaseLogPermission

For a list of CAS-enabled APIs, see Secured APIs.

You can CAS-enable your own APIs. For more information, see Secure an API that Executes on the Server Tier.

Call a CAS-enabled APICall a CAS-enabled API
  1. Declare a variable for the relevant permission class.
  2. Create a new instance of the class.
  3. Request permission by using the assert method on the permission class.
  4. Revert the assertion (to limit the scope of the permission) after the CAS-enabled API has been used; optional. Permission is automatically reverted when the method finishes executing.

Example

{
    DictClass dictClass;
    anytype   retVal;
    str       resultOutput;
    // Variable for the permission class.
    ExecutePermission perm;
    ;
    
    perm = new ExecutePermission();
 
    // Grants permission to execute the DictClass.callObject method.
    // DictClass.callObject is protected by code access security.
    perm.assert();
 
    dictClass = new DictClass(classidget(infolog));
    if (dictClass != null)
    {
        retVal       = dictClass.callObject("toString", infolog);
        resultOutput = strfmt("Return value of is %1", retVal);
        print resultOutput;
        pause;
    }
    
    // Closes the code access permission scope.
    CodeAccessPermission::revertAssert();
}

August 03

unlocking object

when you got "error: Unable to save. xxx is locked by xxx", you need to check Tools -> Development Tools -> Application objects -> Locked application objects.
you may delete any of record in it.
July 30

How to setup Axapta batch server running as user defined windows service

check this out.
 
 
another article
translated
 
//**********************************************************
// Tipo: Implantation/Consultancy
// I title: Processes by Lots and services of Windows
// Code: TC0007
// Keywords: Configuration, Consultancy, Axapta 3.0
// Sub-keywords: Processes by Lots, Batch, Service, SrvAny, InstSrv
// nssm, Windows Resource Kit
//
//**********************************************************

As all you know, Axapta incorporates a management of batch jobs. It is more, is relatively easy to us to create a class so that it simply makes a process by means of the system of programmed lots doing that this inherits of RunBaseBatch.

The small problem is that it is that so that the programmed processes are executed (lots or batch) we must maintain an instance of client of Axapta in march executing the option “Periodic Base//Lot/Processing”. This is recommendable to to this end do it in a machine destined and with an installation of type “fat-client”.

But… and if I have n groups of lots to process… I must initiate n instances of client of Axapta and execute option commented in each one of them changing the lot group to execute. Up to here, but… to reinitiate this machine well it is an odyssey, because it implies to have to conduct the same operation when this starting for each one of the instances of Axapta. To part the fact can suppose a security problem to have a machine with an open session the 24 hours of day… mmm and…

Cannot become by means of services of Windows?

Of this form, they take single when initiating the machine, is not necessary to initiate a session, if it fails something is reinitiated the service (on this we will speak in the end)… everything are advantages… because we do.

I have been looking for information on the subject and next I will propose a solution to you that is the mixture of which I have read by the network. The only thing that I can say is that I believe that she is the optimal one and in fact she is the one that I have used this same one behind schedule in the facilities of a client.

Axapta as so it does not have this functionality (at least in its version 3.0), and therefore we must throw of other resources.

We are going to need a pair called programs SrvAny.exe and InstSrv.exe that are in the Windows NT Resource Kit (also I have put them in the zone downloads/utilities)

We are going to do it in 5 steps:

1. First it will be to publish something of code in Axapta to allow the parametrizada execution of a certain work group by lots.

For it, we must touch the InfoRun method of the SysStartupCmdBatchRun class as it follows:

x++:

void infoRun ()
{
    batchRun batchRun = new batchRun ();
;
    batchRun.parmUseForm (true);
    batchRun.parmGroupId (parm); // TrucosAx.com - the group like parameter Accepts: batch_xxx
    batchRun.run ();
}


2. Now we must create a user in Axapta that will be in charge of the execution of all our processes by lots. We will call it for example: Batch.

From user options him we must associate a network account. This is, to understand to us, a user of Windows. In my case I will associate the account to him of “Administrator”, but you can create a user of Windows for this task. This user will be the one that will be in charge, as we see more ahead, of the execution of the services of processes by lots.

3. Now we must create a configuration of Axapta for the user of Windows that we have decided in the previous step (that is to say, initiated session in the system with the previous user and executed the “Axapta Configuration Utility”.

We created the configuration as if a normal user one was (we will call ConfigBatch), but must add a pair of additional parameters:

• In “StartupCommand” we wrote: “Batch_xxx” (without the comiles and where xxx are the code of the group of lots that we want to execute)
• In “Advanced” we wrote: “- windowsauth=1” (without the comiles)


4. We are going to create the service. First we copied the files srvany.exe and instsrv.exe in a folder of the machine like for example “C:\Services”.

In order to create the service we executed the InstSrv.exe program with the name of our service like parameter. We are going it to call “Axapta Service Batch”. We opened the MSDOS symbol (to execute… cmd) and wrote:

C:\Services\InstSrv.exe “Axapta Batch Service” C:\Services\SrvAny.exe

When pressing intro will create the service to us with the name that we have given him.

Now it is necessary to be called on some parameters of this new service to indicate to him that it executes our processes by lots. For it we opened the publisher of the registry of Windows (to execute… regedit.exe)

We went to the route:

HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Services \ Axapta Service Batch
Now button of the right, new key and we called “Parameters”.



Now we must add 3 alphanumeric values to the new key that we have created:
Application: As value will have the complete route until the feasible one of the client of Axapta, in my case “C:\Archivos de programa\Navision\Axapta Client \ Bin \ ax32.exe”.
AppDirectory: As value will have the folder in which is the client of Axapta, in my case “C:\Archivos de programa\Navision\Axapta Client \ Bin”
AppParameters: As value has: “- regconfig = ConfigBatch” where ConfigBatch is the name of the configuration which we have created in step 3.

Note: All the values without the comiles.



5. It is necessary to associate an identity of Windows to our new service. For it we go to the Control Panel/administrative Tools/Services and looked for ours.

We entered to publish its properties and in the lapel “To initiate session” we associated the user to him of Windows that we had thought to execute all these processes (in my case “Administrator”).



Finally, if we have made all the steps correctly… we can initiate the service (if it is in automatic way begins single whenever it takes the machine) and our batch jobs are already executed from a service of Windows Wink

We have seen that in the step one we have modified a method of Axapta to pass the group of lots to execute by parameter (that soon we passed it from the configuration which we have created). Therefore, if we want to execute diverse groups of lots we can create so many services as we love with its configurations indicating the different groups to execute.


IMPORTANT

Making diverse reasonable tests with the services and the processes by lots… we have observed that srvany.exe seems to have some problem to control the errors or exceptions that can cause our session of execution by lots. For example: If from another session we go away in line to users, we selected one of the sessions of our usuary Batch and we give the button to finish to session… this finishes, but the service does not find out and continuous executing itself.

After looking for and searching carefully by the network, we have found a species of substitute of “srvany”, “nssm” is called and seems to control better the possible errors than they can arise in our session while this is executed like service.

Therefore, there are including also this other manager of services in the section of downloads/utilities in case you want to use it instead of “srvany”.

The only thing which there is to do is to copy it in a directory, for example “C:\Services” and we must publish the registry in the key:

HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Services \ Axapta Service Batch


And to replace the value of “ImagePath” that it has by value “C:\Services\srvany.exe”, by this other: “C:\Services\nssm.exe run”

This new manager of the service, monitoriza our application and in case of error reinitiates it.


Up to here this trick, I hope that it is to you of aid.

Greetings,

Mkz.
 
July 27

modify text on check stub

check on \Classes\CustVendCheque\fillslipText(...) line100, line168
 
July 26

Configuration in Title bar

Thanks
============================================
 
Configuration in title bar

One common complain with Dynamics Ax 4.0 is that it is not possible for the user to determine in which environment they are working.

Particularly for consultants/developers who often have multiple Ax applications open at one time, this is very frustrating and can easily lead to errors (developing in the wrong application). In Axapta 3.0 information was displayed in the title bar allowing users to determine the application, but this is missing in Ax 4.0

This simple fix will display the configuration name in the main Dynamics Ax window title bar. You need to simply override the workspaceWindowCreated() method in the info() class (special class nearly at the bottom of the Classes node in the AOT) and add a single line of code. After the change, the method should appear as follows:

void workspaceWindowCreated(int _hWnd)
{
;
    // Put workspace window specific initialization here.
    // Show config in title bar
    WinAPI::setWindowText(_hWnd, strFmt("%1 - %2", WinAPI::getWindowText(_hWnd), xInfo::configuration()));
}

Another option is shown below. This will put the current AOS and server name, as well as the logged in development layer, after the standard title bar text.

void workspaceWindowCreated(int _hWnd)
{
    SqlSystem       sqlSystem = new SqlSystem();
    LoginProperty   loginProperty = sqlSystem.createLoginProperty();
    ;
 
    // Put workspace window specific initialization here.
 
    // Show application details in title bar
    if (loginProperty)
    {
        WinAPI::setWindowText(_hWnd, strFmt("%1 - %2@%3 (%4)", WinAPI::getWindowText(_hWnd), loginProperty.getDatabase(), loginProperty.getServer(), this.currentAOLayer()));
    }
}

The details for these fixes came originally from posts to the microsoft public axapta newsgroup.

[edit] Ax 3.0 solution

from AxForum

\Classes\Info\onEventGoingIdle

//Event fired by kernel when the client goes idle.
//It is not fired during CTRL-Break dialog.
void onEventGoingIdle()
{
    this.operationProgressClear();
    this.endLengthyOperation(true);
    // there should be a user who will work if error occured
    if(strLwr(curUserID())!='admin')
        TitleChanger::changeTitle();
}

TitleChanger::changeTitle:

static void changeTitle()
{
    #define.WM_SETTEXT(0x000C)
    str         caption;
    str prefix=new Session().AOSName()+": ";
    int  defWindowProc(
                               int              _handle,
                               int              _msg,
                               int              _wParam,
                               str              _lParam )
    {
        int         ret;
        DLL         _DLL             = new DLL('USER32');
        DLLFunction _defwproc        = new DLLFunction(_DLL, 'DefWindowProcA');
        ;
 
        _defwproc.returns(ExtTypes:: DWord); // LRESULT
        _defwproc.arg(ExtTypes:: DWord);     // handle window
        _defwproc.arg(ExtTypes:: DWord);     // message
        _defwproc.arg(ExtTypes:: DWord);     // wparm
        _defwproc.arg(ExtTypes::String);     // lparm
 
        return  _defwproc.call(_handle, _msg, _wParam, _lParam);
    }
    ;
    caption = winapi::getWindowText(infolog.hWnd());
    if(!StrUtils::startsWith(caption, prefix))
        defWindowProc(infolog.hWnd(), #WM_SETTEXT, 0, prefix + caption);
}

StrUtils::startsWith:

// string _s begins with _suffix
static boolean startsWith(str _s, str _prefix)
{
    return
        strLen(_s)>=strLen(_prefix)
        &&
        subStr(_s, 1, strLen(_prefix))==_prefix;
}
 
July 06

[QNA] Createing a user in AD from AX

Thanks for the tip, but I figured out it was just as easy to create a code
library (DLL) in C# and use it in Ax. It works fine for me at least.

Here is the C# code I used:
------------------------------------
using System;
using System.Collections.Generic;
using System.Text;
using System.DirectoryServices;

namespace Microsoft.Dynamics.CreateADUser
{
    public class NewUser
    {
        string defaultNC;
        string alias;
        string fullName;
        string password;
        string ou;

        public void setDomain(string _defaultNC)
        {
            defaultNC = "DC=" + _defaultNC;
        }
        public void setAlias(string _alias)
        {
            alias = _alias;
        }
        public void setFullName(string _fullName)
        {
            fullName = _fullName;
        }
        public void setPassword(string _password)
        {
            password = _password;
        }
        public void setOu(string _ou)
        {
            ou = _ou;
        }

        public string execute()
        {
            DirectoryEntry container, user;
            string ret;

            try
            {
                //This creates the new user in the "users" container.
                //Set the sAMAccountName and the password
                container = new DirectoryEntry("LDAP://OU=" + ou + ", " +
defaultNC + ",DC=NO");
                user = container.Children.Add("cn=" + fullName, "user");
                user.Properties["sAMAccountName"].Add(alias);
                user.CommitChanges();
                user.Invoke("SetPassword", new object[] { password });

                //This enables the new user.
                user.Properties["userAccountControl"].Value = 0x200;
//ADS_UF_NORMAL_ACCOUNT
                user.CommitChanges();
                ret = "OK";
            }
            catch (Exception e)
            {
                ret = e.ToString();
            }
            return ret;
        }
    }
}

------------------------------------

Just compile and put the dll-file in the client\bin folder of Ax and create
a reference from the aot and use the class like this in Ax:

------------------------------------
static void Job1(Args _args)
{
    Microsoft.Dynamics.CreateADUser.NewUser user;
    ;
    user = new Microsoft.Dynamics.CreateADUser.NewUser();
    user.setDomain(/*yourdomain*/);
    user.setAlias(/*alias of the user you want to create*/);
    user.setFullName(/*Full name of the user*/);
    user.setPassword(/*Password*/);
    user.execute();
}
------------------------------------

Regards,
Erlend

"Max Belugin [MVP]" wrote:

> I think, you can find and example in C# and port it to X++
>
> http://www.google.com/search?q=create+user+active+directory+C%23
>
> --
> my blog: http://axcoder.blogspot.com
> freeware sidebar for ax: http://belugin.info/sidax
>
>
> "Erlend Dalen" wrote:
>
> > Is it possible to create a new user in AD from Ax 4.0?
> > I have been looking into the System.DirectoryServices, but I haven't been
> > able to figure out to make it work yet....can anyone help?
> >
> > Regards,
> > Erlend

Integrated with Visual source safe(VSS)

Notes about my experience in setup of Microsoft Dynamics AX 4.0 integrated with Microsoft Visual Source Safe.
 
 
June 27

Accessing the active query forms

Setting the AutoQuery property on a form data source to Yes causes the system to automatically generate the query that retrieves data to display in the form.

Modify the system-generated query to display a different set of data as the result of user input in the form.

  1. Declare a variable of the type QueryBuildRange.

  2. Initialize the QueryBuildRange variable.

  3. Modify the form data source executeQuery method.

Declare a Variable of the Type QueryBuildRange

Declare a variable of the type QueryBuildRange in the class declaration method on your form. Following is an example.

class FormRun extends ObjectRun
{
    QueryBuildRange   criteriaOpen;
    ...
}

QueryBuildRange is a system class used to specify the search scope of a query.

Initialize the QueryBuildRange Variable

Initialize the QueryBuildRange variable in the init method on the form data source. To override the init method, right-click the Methods node within the data source node, and then click Override Method > init.

The initialization must be after the super() call that generates the query. Following is an example.

Copy Code
void init()
{
    super();
    criteriaOpen = this.Query().DataSourceNo(1).addRange(
        fieldNum(CustTrans,Open));
    ...
}

Placing the initialization after the super() call in init is equivalent to placing it before the super() call in the run method.

The code sets the criteriaOpen variable to point to the Open field in the CustTrans table.

Following is an explanation of the code:

  • this – a reference to the current form data source.

  • Query() – the member method on the form data source used to retrieve a reference to the form’s query.

    An instance of the Query system class is always available on a form data source. The class is automatically instantiated when the form is opened.

  • DataSourceNo(1) – the method on the Query class used to identify the data source that takes an integer as parameter to identify the data source. The first data source is 1.

    This call returns a QueryBuildDataSource MorphX object.

  • addRange – the method on the QueryBuildDataSource class used to add a search range to the active criteria. addRange takes a field ID as parameter, and then returns a QueryBuildRange object.

  • fieldNum(CustTrans,Open) – a built-in function that returns the field ID (in this case, the ID of the Open field in the CustTrans table).

Note
The previously described code pattern enables you to reference the active query to add a range to it. The only values that vary are the parameters to the DataSourceNo method and to the fieldnum function.

Modify the executeQuery Method

The executeQuery method on the form data source is activated when the form is opened to display data. When overriding the method, you must add your modifications prior to the super() call.

In the following example, the switch statement performs a test on the IncludeAll variable. The QueryBuildRange variable (criteriaOpen) is set to 1 or 0..1, depending on the value of the member variable Value. The query can now be executed.

Copy Code
void executeQuery()
{
    switch (IncludeAll.Value())
    {
        case (1) :
            criteriaOpen.Value('1'); 
            break;
        case (0) :
            criteriaOpen.Value('0..1');
            break;
    }
    super();
}

FROM developer help of AX

Use Expressions in Query Range

copy from developer help
 
==================
 

Query range value expressions can be used in any query where you need to express a range that is more complex than is possible with the usual dot-dot notation (such as 5012 .. 5500).

For example, to create a query that selects the records from MyTable where field A equals x or field B equals y, do the following.

  1. Add a range on field A.

  2. Set the value of that range to the expression (if x = 10 or y = 20), as a string: ((A == 10) || (B == 20))

The rules for creating query range value expressions are:

  • Enclose the whole expression in parentheses.

  • Enclose all subexpressions in parentheses.

  • Use the relational and logical operators available in X++.

  • Only use field names from the range's data source.

  • Use the dataSource.field notation for fields from other data sources in the query.

Values must be constants in the expression, so any function or outside variable must be calculated before the expression are evaluated by the query. This is typically done by using strFmt.

The example above will then look like the following code example.

strFmt('((A == %1) || (B == %2))',x,y)

To get complete compile-time stability you should use intrinsic functions to get the correct field names, as shown in the following code example.

strFmt('((%1 == %2) || (%3 == %4))',

    fieldStr(MyTable,A), x,

    fieldStr(MyTable,B), y)

Note
Query range value expressions are evaluated only at run time, so there is no compile-time checking. If the expression cannot be understood, a modal box will appear at run time that states "Unable to parse the value".

Example using "inter table" relations

Query  q = new Query();

QueryBuildDataSource qbr1, qbr2, qbr3;

;

 

qbr1 = q.addDataSource(tablenum(ProjCategory));

qbr1.orderMode(OrderMode::GroupBy);

qbr1.addSortField(fieldNum(ProjCategory,CategoryId));

...

qbr2 = qbr1.addDataSource(tableNum(ProjValEmplCategorySetUp),

                          'ProjValEmplCategorySetUp1');

qbr2.addRange(fieldNum(ProjValEmplCategorySetUp,CategoryId)).value(

    strFmt('(%1) || (%2)',fieldStr(ProjValEmplCategorySetUp,CategoryId),

    fieldStr(ProjValEmplCategorySetUp,groupId)));

qbr3 = qbr2.addDataSource(tableNum(ProjValEmplCategorySetUp),

                          'ProjValEmplCategorySetUp2');

qbr3.addRange(fieldNum(ProjValEmplCategorySetUp,CategoryId)).value(

    strFmt('(%1 == %4.%3) && (((%1 == %5.%1) && (%1)) || ((%2 == %5.%2) && (%2)))',

    fieldStr(ProjValEmplCategorySetUp,CategoryId),

    fieldStr(ProjValEmplCategorySetUp,GroupId),

    fieldStr(ProjCategory,CategoryId),

    qbr1.name(),

    qbr2.name()));

passing dynalink from form to form

// regenerate the record from cartonInquiry to onhand form
void clicked()
{
    WMSPallet wMSPallet;
    Args args = new Args();
    ;
    args.caller(this);
    wMSPallet = WMSPallet::find(sunWMSCartonInquiryView.wMSPalletId);
    args.record(wMSPallet);
    new MenuFunction(menuitemdisplaystr(InventOnhandItem), MenuItemType::Display).run(args);
}
June 26

report design section

Section:
    Prolog
    PageHeader : print on all pages in case multi-paged report
    Header : print only one page on first
    Section Group
    Footer : print only last page right after body section. you may control position of page with TOP, BOTTOM properties.
    PageFooter : print on all pages on the bottom
    Epilog : add new page next to the last page and print on top of it.
    ProgrammableSection
June 24

AX4.01 print PDF using EP

Hi,

I dont feel that there is any problem with the image path or location, since
company logo is stored and passed as container.

As per my research there is some problem in executing image class (run on
client ) initilization which may require some thing from client side. As EP
runs on server its not able to initilize it.

Even i tried the diffrent way of passing refrence to bitmap field
(Resources, file path, hardcoded server file path) but nothing seems working.
Infact Image class is not able to load the the bitmap data provided by any
mean.

Any thought on same will be appreciated.

Thanks,
JJ

"Chris (Toronto)" wrote:

> Hey JJ,
>
> The "Publish Images" routine just moves the images onto the web server...
> The problem still remains if the files are located on the web server, or on a
> share on another server... Same GDI Error.
>
> I am still struggling with this, no solution found yet.
>
> Chris
>
> P.S. It was interesting to see my slightly modified post today!
>
>
> "Alpesh" wrote:
>
> > ...sorry correct AX path should be:
> > Administration>>Setup>>Internet>>Enterprise portal>>Publish
> > Images.
> >
> > "Alpesh" wrote:
> >
> > > Hello,
> > >
> > > I have not been able to get to the bottom of this yet.  However, I have
> > > determined that if you navigate to Administration>>Setup>>Internet>>Publish
> > > Images.  And run the process, all the images against the following tables:
> > > "CompanyImage"
> > > "EmplTable"
> > > "ECPPresentation"
> > > "EPParameters"
> > >
> > > will be copied to:
> > >  "..\Program Files\Common Files\ Microsoft Shared\ web server
> > > extensions\60\TEMPLATE\LAYOUTS\ep\images"
> > >
> > > The challenge I have now is how to get my, say for example, AX report, to
> > > resolve this when in EP. 
> > >
> > > Please note that I am not sure if this line of investigation is correct, but
> > > it seems logical at the moment!
> > >
> > > Can anybody out there help?
> > >
> > > I will update you if I get further.
> > >
> > > Alpesh
> > >
> > > "jj" wrote:
> > >
> > > > I am also facing the same issue ... is there any solution or alternative you guys find out ?
> > > >
> > > > EggHeadCafe.com - .NET Developer Portal of Choice
> > > > http://www.eggheadcafe.com
> > > >
 
Hello Chris,

We have got the same issue.  I am testing one or two thoughts on resolving
this.  Will let you know when I am done.

Alpesh

"Chris (Toronto)" wrote:

> Alpesh,
>
> Thanks for your guidance on the Event log... Your right.
>
> The error I'm getting is coming from the PDF Writer class, and it's because
> of an image.... I've read a few posts that talk about removing images from
> reports before printing... But this is not solution... we need the images.
>
> Any ideas on how to fis this error? (Application Error Log listed below)
>
> Chris
>
>
> The Microsoft Dynamics session get object call runWebletItem failed.
>
> GDI+ generic error.
> (C)\Classes\Image\new
> (C)\Classes\PDFViewer\writeBitmap - line 76
> (C)\Classes\ReportOutputUser\writeField
> (C)\Classes\ReportOutputUser\printViaClass
> (C)\Classes\ReportOutput\printPDF
> (C)\Classes\ReportOutput\printToTarget
> (S)\Classes\ReportRun\print
> (S)\Classes\ReportRun\run
> (S)\Classes\ioSendDocument\printSpecSheetPDF - line 43
> (C)\Web\Web Forms\cmooTest\Methods\init
> (C)\Classes\WebFormHtml\run - line 5
> (C)\Classes\WebFormHandler\new - line 45
> (C)\Classes\WebPortalExecutionEngine\processWebContentItem - line 70
> (C)\Classes\WebFormWeblet\run - line 8
> (C)\Classes\WebLet\runFrame - line 39
> (C)\Classes\WebAppWebLet\runFrame - line 4
> (C)\Classes\WebPortalExecutionEngine\runWebletItem - line 64
>
> Object 'Image' could not be created
> File Exists: true, \\itvaos2003\EpFiles\Spec(149545).pdf
>
> Microsoft.Dynamics.BusinessConnectorNet.XppException
>    at Microsoft.Dynamics.BusinessConnectorNet.AxaptaObject.Call(String
> methodName, Object[] paramList)
>
>
> "Alpesh" wrote:
>
> > Do you have a bitmap on your source file?  If so, remove it and then try
> > generating your report again.  Also, maybe you already know this, if there is
> > an error generated by EP it will be written to the Application Event lof on
> > the IIS Server.
> >
> > Alpesh
> >
> > "Chris (Toronto)" wrote:
> >
> > > Hello,
> > >
> > > I am attempting to create a PDF from a report on the fly on the Enterprise
> > > portal.
> > >
> > > The following code is being used to generate the report:
> > >
> > > args = new args();
> > >     args.name(reportStr(ioItemSpec));
> > >     args.record(ioTItleLine);
> > >     if (_showExtended)
> > >     {
> > >         args.parmEnum(1);
> > >     }
> > >     args.parm(num2str(_itemPrice,0,2,0,0));
> > >
> > >     reportRun = new reportRun(args);
> > >     reportRun.args().name('KeepSettings');
> > >     reportRun.query().interactive(False);
> > >     reportRun.report().interactive(False);
> > >     reportRun.setTarget(printMedium::File);
> > >     reportRun.printJobSettings().setTarget(PrintMedium::File);
> > >     reportRun.printJobSettings().format(PrintFormat::PDF);
> > >     reportRun.printJobSettings().warnIfFileExists(False);
> > >     reportRun.printJobSettings().suppressScalingMessage(True);
> > >     reportRun.printJobSettings().packPrintJobSettings();
> > >     reportRun.printJobSettings().fileName(filename);
> > > 
> > >     reportRun.run();
> > >
> > > This works great when the report is printed from a client machine.... But
> > > when the report is generated by a webform, or a class called from the EP, the
> > > PDF is corrupt (only 1 K)
> > >
> > > The PDF file created when opened in notepad is:
> > >
> > > %PDF-1.3
> > > % Quotation - Report
> > > % Generated by Guest on 3/21/2007 at 14:26:54
> > > 5 0 obj <<
> > >   /Creator (Axapta)
> > >   /Producer (Guest)
> > >   /Title (Quotation - Report)
> > >   /Author (Axapta  \(build2163\))
> > >   /Subject (Quotation - Report)
> > >  
> > >
> > > Any ideas on why the PDF report is not being generated correctly when the
> > > report is called from the Enterprise Portal?
> > >
> > > Any help would be greatly appreciated!
> > >
> > > Chris

print filtered list only

Thanks Mathias i'll prove and let you now you
--
Juan Manuel


"Mathias" wrote:

> Am Tue, 17 Apr 2007 15:16:00 -0700 schrieb Juan Manuel:
>
> > Hi, All I'm Newbie in Axapta and working in a developement where i fill a
> > form with a temporary table and the user look for information and make some
> > filters, when the user find the raight information he want to print the
> > information in a report.
> >
> > I'm al ready make an inform but when is calling bring the last filter used,
> > is there some way to know the filters used by the user and passing this
> > values to the class that make the inform ????
> >
> > Any one has/know some exemple of doing this???
> >
> > regadrs and thanks in advance
>
> Hi,
>
> you can use element.dataSource() to get the query with all ranges on your
> form.
> Here is an example that will show you all the ranges in a new dialog. Just
> use a button on your form and overload the method "clicked".
>
> void clicked()
> {
>     FormRun         fr;
>     FormDataSource  fds = element.dataSource();
>     queryRun        queryRUn;
>     ;
>     super();
>     queryRUn = fds.queryRun();
>     queryRun.prompt();
>
> }
>
>
> And you can use this query in your report to get all records based on it.
> To call your report you can use the following code to parse the query from
> your from to your report.
>
> void clicked()
> {
>     ReportRun       reportRun;
>     FormDataSource  fds = element.dataSource();
>     queryRun        queryRUn;
>     args            args = new Args();
>     ;
>     query = fds.query();
>     args.name(reportstr(yourReportName));
>     args.parmObject(fds.queryRun());
>     super();
>
>     reportRun = new ReportRun(args);
>     reportRun.init();
>     reportRun.run();
>     reportRun.wait();
> }
>
> after that you have to set up queryrun on your report.
>
> i hope this will help.
> --
> Mathias Füßler
> My blog: http://starside.eu
>
 

David Park

Occupation
Location
Microsoft Dynamics AX developer