Tuesday, September 25, 2012

CreateSalesLine form in Dynamics AX(just like SalesCreateOrder form)

This post is about creating a New CreateSalesLine form which is just going to work like SalesCreateOrder form except that this out of box form creates SO and the new one create SalesLine.

Let's start with the reasons why we may need this form - In out of box SalesTable form
1.  whenever user clicks on the new button from toolbar all the user filters and sort expressions are lost and the active line item that user wants to refer will loose the focus, this is all because the datasource query re-executes. I tried to persist the user filters using my earlier post technique, but it didn't work for me in the case of new button.
2. since it is about referring an existing lineItem we can always use out of box "Copy from lines" function, but the issue with this one is - it doesn't allow user to change the ItemId of the line ( my user wants to use the values from existing lineItem for a new lineItem with different ItemId). i tried to solve this one by making ItemId field editable in the Salestable form, but it was causing chain of other errors - let me know if you have any luck with this.
So i came up with this approach where i provide a new button named "New LineItem" in the salesLine section of SalesTable form(Fig 1), on click i will pop-up the new form that i call CreateSalesLine (Fig 2)

Fig 1                                                                                                        Fig 2:
SalesLine section in SalesTable form (Parent)


New form - CreateSalesLine (Child)
this form will create a new line item record and pre-populates it with values from the active lineItem record in Parent form(SalesTable). User can change the item number if needed and once OK button is clicked the newly created lineItem record is commited to DB , child form closes and the SalesLine grid in the parent form refreshes with the focus on newly Created LineItem.

Here are the things i did to achieve this :
1. To simplify i have created this simple data flow diagram

2. within the new button's click event , i added :

   CreateSalesLineClass obj;
    ;   
    obj = new CreateSalesLineClass();
    obj.init(salesTable, salesTableForm, inventDim, salesLine);  // passing current records info
    obj.create();  // we will discuss this method in subsequent steps
     SalesTable_ds.research(true);
     SalesLine_ds.research(true);
     SalesLine_ds.findRecord(obj.newSalesLineRec());   // to focus the newly created record
     super();

3. CreateSalesLineClass:  
 a)  variables declared at class level scope:
SalesTable salesTable;
  SalesTableForm salesTableForm;
  InventDim  inventDim;
  SalesLine copyFromSalesLine;
  Int64 newLineRecId;
  SalesLine newSalesLineRec;

b) init method:
void init(SalesTable _currRec, SalesTableForm _salesTableForm, InventDim _inventDim, SalesLine _copyFromSalesLine)
{
  ;
  salesTable = _currRec;
  salesTableForm = _salesTableForm;
  inventDim = _inventDim;
  copyFromSalesLine = _copyFromSalesLine;
}

c) Create method:
boolean create()
{
   Args args = new Args();
   FormRun CreateSalesLine;
   SalesLine salesLine;
   boolean result = false;
   SalesTable localSalesTable;
   ;

   args.name(formStr(CreateSalesLine) );
   args.caller(this);
   CreateSalesLine = classFactory.formRunClass(args);
   CreateSalesLine.init();
   CreateSalesLine.run();

   if(!CreateSalesLine.closed())
        CreateSalesLine.wait();

   if(CreateSalesLine.closedOk())
         result = true;
   else
          result = false;

   return result;  // indicating if the new record is created or not
}


4. CreateSalesLine form:

a) Add SalesLine datasource , add simple design tab control with some salesLine fields (Fig 2) and add Command buttons - OK and Cancel

b) CreateSalesLine form class declaration:

   CreateSalesLineClass  CreateSalesLineClass;
   SalesTable salesTable;
   SalesTableForm salesTableForm;
   InventDim copyFromInventDim , inventDim;
   SalesLine copyFromSalesLine, newlyCreatedSalesLine ;


c) init method:
public void init()
{
    super();
     if (!element.args().caller())  // throw error if called directly
        throw error("@SYS22539");

    CreateSalesLineClass = element.args().caller();
    salesTable = CreateSalesLineClass.getSalesTable();
    salesTableForm = CreateSalesLineClass.getSalesTableForm();
    copyFromInventDim = CreateSalesLineClass.getInventDim();
    copyFromSalesLine = CreateSalesLineClass.getCopyFromSalesLine();
}


d) run method:
public void run()
{
    super();
    SalesLine_ds.create(); 
    CreateSalesLineClass.newSalesLineRec(SalesLine);   // pass the newly created salesline ref to caller
}

e) SalesLine_DS -> Create method:
public void create(boolean _append = false)
{
    super(_append);
    newlyCreatedSalesLine.data(SalesLine); // back up the newly created record
    newlyCreatedInventDim.data(inventDim); // back up the newly created record
   // we can use both the back-up buffers later if User changes the ItemId

    // set the basic Line info
    SalesLine.SalesId = salesTable.SalesId;
    SalesLine.ItemId = copyFromSalesLine.ItemId;

    inventDim.InventColorId = copyFromInventdim.InventColorId;
    inventDim.InventLocationId = copyFromInventDim.InventLocationId;
    inventDim = InventDim::findOrCreate(inventDim);    // create new inventDim rec

    SalesLine.InventDimId = inventDim.inventDimId;

    SalesLine.CustAccount = salestable.CustAccount;
    SalesLine.CurrencyCode = salesTable.CurrencyCode;
    SalesLine.CustGroup  = salesTable.CustGroup;
    SalesLine.SalesType  = salesTable.SalesType;

    // copy data from active salesLine record in parent form, i just copied the data that my client needs
    SalesLine.ReceiptDateRequested  = copyFromSalesLine.ReceiptDateRequested;
    SalesLine.ShippingDateRequested = copyFromSalesLine.ShippingDateRequested;

    salesLine.SalesQty           =  copyFromSalesLine.SalesQty;
    salesLine.SalesUnit          = copyFromSalesLine.SalesUnit;
    salesLine.QtyOrdered         = copyFromSalesLine.QtyOrdered;

    salesLine.SalesPrice         = copyFromSalesLine.SalesPrice;
    salesLine.PriceUnit          = copyFromSalesLine.PriceUnit;
    salesLine.LinePercent        = copyFromSalesLine.LinePercent;
    salesLine.LineDisc           = copyFromSalesLine.LineDisc;
    salesLine.MultiLnDisc        = copyFromSalesLine.MultiLnDisc;
    salesLine.MultiLnPercent     = copyFromSalesLine.MultiLnPercent;
    salesLine.SalesMarkup        = copyFromSalesLine.SalesMarkup;
    salesLine.LineAmount         = copyFromSalesLine.LineAmount;
    // you can copy more data as per your need , refer class SalesLineType.initFromSalesLine() - used by out of box copy line functionality
}


f) Override form's salesLine datasource's Write method:
public void write()
{
   ; 
     if (!element.closedOk())
          return;
     SalesLine.createLine(NoYes::Yes, NoYes::No, NoYes::No, NoYes::No);
     super();
}

so far whatever i coded is good as long as User doesn't want to change the ItemId in the new form, my user like the ability to change the ItemId so i coded the following...


g) override the modified method for ItemId field of SalesLine datasource:

public void modified()
{
    ItemId itemId;
    ;
    super();

    //info(strFmt('Old ItemId - %1',  copyFromSalesLine.ItemId ));
    //info(strFmt('New ItemId - %1',  SalesLine.ItemId ));

    itemId = SalesLine.ItemId;
    SalesLine.data(newlyCreatedSalesLine);  // restore from the back-up buffer
    inventDim.data(newlyCreatedInventDim);
    SalesLine.ItemId = itemId;
    SalesLine_ds.refresh();  // refresh form data from the cache

    info(strFmt('Old ItemId - %1',  copyFromSalesLine.ItemId ));

    inventDim = InventDim::findOrCreate(inventDim);//inventDim.data(InventDim::find(salesLine.InventDimId));
    salesLine.modifyItemDim(inventDim, fieldnum(InventDim,InventLocationId),  InventTable::find(salesLine.ItemId).DimGroupId);
    InventMovement::bufferSetTransQtyUnit(salesLine);

    salesLine.setDeliveryDateControlType(InventDim::findOrCreate(inventDim).InventDimId);
    SalesLine::modifySalesQty(salesLine,inventDim);
    SalesLine_ds.refresh();

    //info( strFmt('AfterChange Invent Color and Location: %1 and %2', inventDim.InventColorId, inventDim.InventLocationId) );

    // copying back
    SalesLine.SalesId = salesTable.SalesId;
    SalesLine.CustAccount = salestable.CustAccount;
    SalesLine.CurrencyCode = salesTable.CurrencyCode;
    SalesLine.CustGroup  = salesTable.CustGroup;
    SalesLine.SalesType  = salesTable.SalesType;

    SalesLine.ReceiptDateRequested  = copyFromSalesLine.ReceiptDateRequested;
    SalesLine.ShippingDateRequested = copyFromSalesLine.ShippingDateRequested;

    salesLine.SalesQty           =  copyFromSalesLine.SalesQty;
    salesLine.SalesUnit          = copyFromSalesLine.SalesUnit;
    salesLine.QtyOrdered         = copyFromSalesLine.QtyOrdered;

    SalesLine_ds.refresh();
}

Comments, questions are always welcome, happy DAXing :)

No comments:

Post a Comment