Showing posts with label Ax. Show all posts
Showing posts with label Ax. Show all posts

Wednesday, 6 February 2013

Running batch report in AX2009 (batch printing)


Since the change of AX2009 batch framework, printing of report through batch can be done through two methods:
- Printing from server (to printer or file)
- Printing from client (the legacy batch processing)

Prerequisites: For both of the method, there're some mandatory setup:
  1. Enable any of the AOS that you want to make it as batch AOS
    (Administration -> Setup -> Server configuration)
  2. Create batch group for printing.
    Eg. One for client printing and one for server printing.
    (Administration -> Setup -> Batch groups)
  3. Add the batch group created in #2 into the AOS enabled for batch processing (in #1)

Printing from server (print to printer)
  • Enable the 'Allow clients to connect to printers on this server' settings at Dynamics AX Server Configuration Utility under the 'Application Object Server' tab.
  • Enable the 'Connect to printers on the server' settings at Dynamics AX (client) Configuration Utility under the 'Connection' tab
  • When printing report
    > At the 'Batch' tab, check the 'Batch processing' and select the batch group created for server printing
    > Click on the "Option" and select the server printer. Server printers are prefix with 'AOS:'


Printing from server (print to file)
  • Enable the 'Allow clients to connect to printers on this server' settings at Dynamics AX Server Configuration Utility under the 'Application Object Server' tab.

    *Although you're printing to file, but you still need to enable this.
  • When printing report
    > At the 'Batch' tab, check the 'Batch processing' and select the batch group created for server printing
    > Click on the "Option" and select 'File', then ensure the file name is a UNC path (Eg.\\serverName\directory) else you'll get an error.

    *After this job is added to the batch, you can check it at 'Basic > Inquiries > Batch job'. Click on the 'View tasks', notice that the 'Run location' is "Server". If your 'Run location' is "Client", then you might have selected a client printer or your report class method 'runsImpersonated()' has been overwritten to return "false".

Printing from client (through legacy batch processing)
  • When printing report
    > At the 'Batch' tab, check the 'Batch processing' and select the batch group created for client printing
    > Click on the "Option" and select the client printer
  • Run the legacy batch processing (Basic > Periodic > Batch > Processing)

    *After this job is added to the batch, you can check it at Basic > Inquiries > Batch job. Click on the 'View tasks', look at the printing job/task, it will change from "Waiting" to "Ready", then to "Executing". Also notice that the 'Run location' is "Client". The legacy batch processing require the task to be in "Ready" status, this field cannot be changed manually, it is automatically changed if you've done step #3 in the prerequisites (it might not change immediately, give it some time then refresh it and look at the status again).
Some common errors:
  • Use of printers on the server is not allowed





















  • Resolution: Ensure the 'Allow clients to connect to printers on this server' settings at Dynamics AX Server Configuration Utility under the 'Application Object Server' tab is checked.
  • Target file must be in UNC format

  • Resolution: Ensure when you select 'File' as the output, the 'File name' is UNC path. Eg. \\serverName\directory
  • @"\\SON15882\D$\Test\Cust.txt" 
  • Selected printer is not an AOS printer




















Resolution: Install the printer driver on the server.
Sometimes there're confusion regarding AOS printer and shared printer.
For printing to works in batch mode (running from server), the printer has to be AOS printer, which the printer is "Installed" as printer on the AOS host server, not "Connect" as shared printer.

Thursday, 10 January 2013

How change the color of a single colum not the entire row


You can override the displayOption() method of the form's datasource 
to change appearance of any row in the grid. U can set either the 
foreground or the background color of the row.

public void displayOption(Common _record, FormRowDisplayOption _options)
{
   _options.backColor(WinApi::RGB2int(0,255,0)); 
} 

For highlighting one or more cells, override the 

To highlight one or more individual cells, use 
the ._options.affectedElementsByControl() method. 

public void displayOption(Common _record, FormRowDisplayOption _options)
{
  _options.backColor(WinApi::RGB2int(0,255,0));
  _options.affectedElementsByControl(Control1.id());
  _options.affectedElementsByControl(Control2.id());
} 

Remember one thing, u can change multiple cells this way but u cant 
assign different colors to different cells. U have to use only one 
color for any of the cells in the row.

Thursday, 3 January 2013

How to get the current active company in AX

By running the curExt() function the current active company is returned. 

static void curExtExample(Args _arg)
{
str s;
;

// Sets s to the extension of the current company.
s = curExt();
print "Current extension is " + s;
pause;
}

Copying data of one table to another table across the company in AX

//Copying data of one table to another table across the company in AX2009
static void CopyDataFromOneTableToAnother(Args _args)
{
    TableA                  tableA;
    TableB                  tableB;
    DataArea                dataArea;
  
    ;

  while select dataArea
     {

        changeCompany(dataArea.id)
        {
            tableA= null;
            tableB= null;
            while select tableA
            {
              tableB.CustAccount        = tableA.CustAccount;
              tableB.ItemCode           = tableA.ItemCode;
              tableB.insert();
            }
        }
     }

      info(strfmt("Mission Accomplished"));

}

Tuesday, 6 November 2012

Run a form automatically when opening dynamics ax


create menu item for the form and call:

new MenuFunction(menuitemDisplayStr(YourMenuItemName), MenuItemType::Display).run(); 

Place this code to Info.startupPost() (if you want it to start when the client starts) or Info.workspaceWindowCreated() (if you want it to start every time new workspace is opened).

Tuesday, 16 October 2012

Building Lookups - Using a form for lookup building

In numerous situations, standard, automatic, or even dynamic runtime lookups cannot display the required data. For example, it might be a lookup with tab pages or a search field. In such cases, Dynamics AX offers the possibility to create an AOT form and use it as lookup.
In this recipe, we will demonstrate how to create a lookup using an AOT form. As an example, we will modify the standard customer account lookup to display only active customers.

How to do it...

  1. 1. In AOT, create a new form called CustLookup. Add a new data source with the following properties:
    PropertyValue
    NameCustTable
    TableCustTable
    AllowCreateNo
    AllowEditNo
    AllowDeleteNo
    AllowCheckNo
    OnlyFetchActiveYes
    IndexAccountIdx

  1. 2. Change the form's following design properties:
    PropertyValue
    FrameBorder
    WindowTypePopup

  1. 3. Add a new Grid to the form's design:
    PropertyValue
    NameCustomers
    ShowRowLabelsNo
    DataSourceCustTable

  2. 4. Add a new StringEdit control to the grid:
    PropertyValue
    NameAccountNum
    DataSourceCustTable
    DataFieldAccountNum
    AutoDeclarationYes

  1. 5. Add another StringEdit control to the grid, right after AccountNum:
    PropertyValue
    NameName
    DataSourceCustTable
    DataFieldName

  1. 6. Add one more StringEdit control to the grid, right after Name:
    PropertyValue
    NamePhone
    DataSourceCustTable
    DataFieldPhone

  1. 7. Add a new ComboBox control to the end of the grid:
    PropertyValue
    NameBlocked
    DataSourceCustTable
    DataFieldBlocked

  1. 8. Override the form's init() method with the following code:
    public void init()
    
    {;
    super();
    element.selectMode(AccountNum);
    }
    
  2. 9. Override the form's run() with the following code:
    public void run()
    
    {
    FormStringControl callingControl;
    boolean filterLookup;
    ;
    callingControl = SysTableLookup::getCallerStringControl(
    element.args());
    filterLookup = SysTableLookup::filterLookupPreRun(
    callingControl,
    AccountNum,
    CustTable_ds);
    super();
    SysTableLookup::filterLookupPostRun(
    filterLookup,
    callingControl.text(),
    AccountNum,
    CustTable_ds);
    }
    
  3. 10. Finally, override init() of the CustTable data source with the following code:
    public void init()
    
    {
    Query query;
    QueryBuildDataSource qbds;
    QueryBuildRange qbr;
    ;
    query = new Query();
    qbds = query.addDataSource(tablenum(CustTable));
    qbr = qbds.addRange(fieldnum(CustTable, Blocked));
    qbr.value(queryvalue(CustVendorBlocked::No));
    this.query(query);
    }
    
  4. 11. The form in AOT should look like this:






















    1. 12. Locate the extended data type CustAccount in AOT, and change its property:
      PropertyValue
      FormHelpCustLookup

    1. 13. To test the results, open Accounts receivable | Sales Order Details and start creating a new sales order. Notice that now Customer account lookup is different, and it includes only active customers:





















      How it works...
      The newly created CurrencyLookup form will replace the automatically generated customer account lookup. It is recommended to append text Lookup to the end of the form name, so that lookups can be easily distinguished from other AOT forms.
      In order to make our form lookup looks exactly like a standard lookup, we have to adjust its layout. So, we set form design Frame and WindowType properties respectively to Border and Popup. This removes form borders and makes the form lookup like a true lookup. By setting the grid property ShowRowLabels to No, we hide grid row labels, which are also not a part of automatically generated lookups. Then, we create a grid with four controls, which are bound to the relevant CustTable table fields.
      Next, we change the data source properties. We do not allow any data change by setting AllowEdit, AllowCreate, and AllowDelete properties to No. Security checks should be disabled by setting AllowCheck to No. To increase performance, we set OnlyFetchActive to Yes, which will reduce the size of the database result set to only the fields that are visible. We also set the data source index to improve lookup performance.
      Now, we need to define which form control will be returned as a lookup value to the calling form upon user selection. We need to specify it manually in the form's init() by calling element.selectMode() with the name of the AccountNumcontrol as argument.
      In the form's run(), we simulate standard lookup filtering, which allows user to user * symbol to search for records in the lookup. For example, if the user types 1* into the Customer account control, the lookup will open automatically with all customer accounts starting with 1. To achieve that, we use the filterLookupPreRun() and filterLookupPostRun() methods of the standard SysTableLookup class. Both methods requires calling a control, which we get by using getCallerStringControl() method of SysTableLookup. The first method reads user input and returns true if a search is being performed, otherwise, false. It must be called before the super() in the form's run() and accepts four arguments:
      1. 1. The calling control on the parent form.
      2. 2. The returning control on the lookup form.
      3. 3. The lookup data source.
      4. 4. An optional list of other lookup data sources.
      The filterLookupPostRun() must be called after the super() in the form's run() and also accepts four arguments:
      1. 1. A result value from previously called filterLookupPreRun().
      2. 2. The user text specified in the calling control.
      3. 3. The returning control on the lookup form.
      4. 4. The lookup data source.
      The code in the CustTable data source's init() replaces the data source query created by its super() with the custom one. Basically, here we create a new Query object, add a range to show only active customers, and set this object as the new CustTable data source query. This ensures that there are no dynamic links from the caller form's data source.
      The value CustLookup in the FormHelp property of the CustAccount extended data type will make sure that this form is opened every time the user opens Customer account lookup.

Sunday, 14 October 2012

X++ code to create a customized lookup on form

Override the lookup method on Formdatasource field(on which you want to show lookup) , and copy the following code to your method.
Comment the super() method in the lookup.

public void lookup(FormControl _formControl, str _filterStr)
{

SysTableLookup sysTableLookup; // systemclass to create //customlookup
Query query;
QueryBuildDataSource qbd;

;
sysTableLookup = SysTableLookup::newParameters(
tablenum(InventTable),
_formcontrol);

// Construct query on the table,
// whose records you want to show as lookup.
query = new Query();
qbd = query.addDataSource(tablenum(InventTable));
qbd.addRange(fieldnum(InventTable,ItemType)).value(SysQuery::value(enum2str
(ItemType::Item)));

// add the fields to the lookup list
sysTableLookup.addLookupfield(fieldnum(InventTable,ItemId));
sysTableLookup.addLookupfield(fieldnum(InventTable,ItemName));

// pass the query as parameter
// system will show the records in the lookup
// as per your query
sysTableLookup.parmQuery(query);
sysTableLookup.performFormLookup();

}

Wednesday, 29 August 2012

Sales orders – commissions in Ax 2009


Setup Commission posting
Microsoft Dynamics® AX2009 calculates sales commissions at the sales line level upon invoicing a sales order.
The Commission posting form is used to specify a general ledger account for a commission posting to be debited only during an invoice update, and also as an offset account that is credited. Click Navigation Pane node: Accounts receivable -> Setup -> Commission -> Commission posting. Select ‘Commission’  to setup debit account and ‘Commission offset’ to setup credit account.
Customer groups for commissions
When calculating the commission of a sales line Microsoft Dynamics AX 2009 searches for the related sales representative, item, and customer combination set up in the Commission calculation form. Groups of sales representatives, items, and customers can also be set up in Commission relationships in the Commission calculation form.
Use Commission customer groups (CCG) to integrate groupings of customers into the calculation of commission transactions for sales orders. Notice that CCGs are not necessarily connected to customer groups. Click Navigation Pane node: Accounts receivable -> Setup -> Commission -> Customer groups for commission.
For a commission to be calculated with regard to a specific CCG, the CCG must be associated with a sales order. A CCG can be set up on:
- Customer – Specify the CCG on the Sales order tab in the Customer commission group field. This defaults to the sales order when creating a sales order with this customer.
- Sales order – Specify the CCG on the Setup tab in the Customer group field. Notice that a setting in the sales order overrides the setting from the Customers form.
To link CCG with the customer, click Navigation Pane node: Accounts receivable -> Customer Details. Switch to the Sales order tab on the Customers form. Change Commission Customer Group.
To link sales order with the CCG, click Navigation Pane node: Accounts receivable -> Sales Order Details.  Switch to the Setup tab and select ‘Sales group’, select CCG. If you already have selected the group for the customer, it will be populated automatically.
Commission item groups
Use Commission item groups (CIG) to integrate combinations of items into the commission calculation. Notice that although CIGs are completely independent of ordinary item groups Inventory management > Setup > Item groups it might be desirable to group items into CIGs under similar premises. You can’t change CIG in the order line.
To check existing Item groups, open Inventory management -> Setup -> Item groups.
To create new CIG Accounts receivable -> Setup -> Commission -> Item groups.
To link items with CIG, click Navigation Pane node: Inventory management -> Item details, switch to the General tab on the Item form.
Commission sales group
The purpose of grouping employees is broader than the purpose of grouping customers and items for commission agreements. Within the Commission sales group (CSG) is the specification of which employees may receive a commission when a customer associated with the relevant sales group buys certain items.
Click Navigation Pane node: Accounts receivable -> Setup -> Commission -> Sales groups.
Open Sales rep. form. to setup sales representatives (they need to be in the employee table).
You can allocate percentages of both less than 100% and more than 100%. If the amount is more than 100%, a warning message appears. However, the system correctly calculates the amount if this warning is ignored and an over allocation is required.
You can also associate employees with a sales group by clicking Human Resources > Employee Details. Select an employee and click Commission, select the sales group to attach the employee to.
Click Navigation Pane node: Human Resources -> Employee Details. Click the Commission -> Sales groups menu button.
The same effect as creating a Table relation for a sales rep can be created by only attaching one salesperson to the CSG (Contact).
To integrate CSGs into the calculation of sales order commissions, specify a CSG at one of the following levels:
- Customer – Specify the CSG on the Sales order tab in the Sales group field. This defaults to the sales order when creating a sales order with this customer.
- Sales order – Specify the CSG in the Sales group field. This overrides the setting specified on the customer.
- Sales order line – Specify the CSG in the Sales group field. This overrides the customer and sales order settings.
Commission calculation
Click Navigation Pane node: Accounts receivable -> Setup -> Commission -> Commission calculation.
Go to the Setup tab, specify the following options:
  • The period the commission calculation is valid.
  • When commission calculation occurs: Before line discount, After line discount or After the total discount.
  • Whether the commission calculation is based on the contribution Margin or on Revenue earned for the sales order
  • In the Find next field, specify whether Microsoft Dynamics AX 2009 is to continue searching for more commission agreements for a salesperson. If this option is not selected, any agreements on the Group or All levels, for example, will be suppressed.  Microsoft Dynamics AX 2009 searches to find whether there are employees associated with a Sales group who are to receive a commission.  It will accumulate commissions for all possible level. The search for a commission calculation agreement moves from specific relations to general – all relations:   Table level, Item and Customer combination;  Group level combination of Item and Customer combination; - All – which is the general level.
  • If during the search a specific table relation exists, this overrides a commission calculation setup at the group level and in the same manner as a group setting overrides a general All setting.
View commission transactions
To see created commission transactions, click Navigation Pane node: Accounts receivable -> Customer Details. Click the Inquiries -> Invoice menu button. Commission -> Commission transactions menu button.
To see all of commission transactions posted for employee, click Navigation Pane node: Human Resources -> Employee Details. Click the Commission -> Commission transactions menu button.
Source: Dynamics Ax training materials.

Tuesday, 7 August 2012

Creating a batch class in Dynamics AX 2009



For creating a batch class in Dynamics AX we can start from the Tutorial_RunbaseBatch class,
 but here is the explanation for understanding all its methods:

In the classDeclaration, we declare any variable that contains user's selection or preferences
 (for example, the customer account for launch a customer report). Also, we declare other
 variables like dialog fields, auxiliar variables, etc. But, the most important feature here, is the #localmacro.CurrentList declaration. This macro helps you to define which variables you want
 to save for running the class in batch mode (when not user interaction is available).

class Tutorial_RunbaseBatch extends RunBaseBatch
{
      //Packed variables
      TansDate           transDate;
      CustAccount      custAccount;

      // Dialog fields
      DialogField        dlgCustAccount;
      DialogField        dlgTransDate;

      // Current version
      // We can different versions for different purposes
      #define.CurrentVersion(1)
      #define.Version1(1)
      #localmacro.CurrentList        // Our current variable list for save user's selected values
        transDate,
        custAccount
   #endmacro
}

The macro #define.CurrentVersion(1) and #define.Version1(1) are useful for scaling our class
 when we need to change/upgrade it preserving compatibility.

Now two methods: pack and unpack. These methods are used for store and retrieve user's 
preferences and perform the batch operation when none user are available (in batch mode).
 Here is the point where we use the macro defined previously:



public container pack()
{
    return [#CurrentVersion,#CurrentList];
}

public boolean unpack(container packedClass)
{
    Version version = RunBase::getVersion(packedClass);
;
    switch (version)
    {
        case #CurrentVersion:
            [version,#CurrentList] = packedClass;
            break;
        //case #otherVersion:
            //[version,#CurrentList, #otherList] = packedClass;
            //break;
        default:
            return false;
    }

    return true;
}


Bacause we need to ask the user about preferences for run this object (report,
 process, query, etc.), we must implement some methods:

dialog: for build the custom dialog for prompting user's preferences.
dialogPostRun: after dialog was created, you can perform other UI tasks here.
getFromDialog: used for retrive user's preferences (dialog values).
validate: here, you can perform a validation for any data entered by the user.

public Object dialog()
{
    DialogRunbase       dialog = super();
    ;
 
    // Creation of our custom dialog fields/controls to ask user for preferences
    dlgTransDate = dialog.addFieldValue(typeid(TransDate),transDate);
    dlgCustAccount = dialog.addFieldValue(typeid(CustAccount),custAccount);

    return dialog;
}

public void dialogPostRun(DialogRunbase dialog)
{
;
    super(dialog);
}

public boolean getFromDialog()
{
    ;

    // Retrieving user's preferences
    transDate   = dlgTransDate.value();
    custAccount = dlgCustAccount.value();

    return super();
}

public boolean validate()
{
    // We can perform some validations here
    if (false)
        return checkFailed("");

    return true;
}


Finally, the main method constructs the object from this batch class, and after
 prompting user and validation was successful, the run method is called to perform 
some task (in batch mode if it was selected):

server static Tutorial_RunbaseBatch construct()
{
    return new Tutorial_RunbaseBatch();
}

public void run()
{
    try
    {
        // Obviously, this code is silly, why show something to nobody?
        // remember, this code is running in batch without user interaction
        info(strfmt("Parameters: %1 for %2", transDate, custAccount));
    }
    catch(Exception::Deadlock)
    {
        retry;
    }
    catch(Exception::UpdateConflict)
    {
        throw Exception::UpdateConflict;
    }
    catch
    {
        throw Exception::Error;
    }
}

static void main(Args args)
{
    Tutorial_RunbaseBatch    tutorial_RunBase;
    ;

    // Instanciating this batch class
    tutorial_RunBase = Tutorial_RunbaseBatch::construct();

    // Prompting user and run if all is ok
    if (tutorial_RunBase.prompt())
        tutorial_RunBase.run();
}