Showing posts with label Axapta. Show all posts
Showing posts with label Axapta. Show all posts

Some useful papers regarding AX 2012 structure and migration

This post is to share with you some whitepapers(available on MSDN) regarding AX 2012 restructuring and the class level modifications done :
AX whitepapers available on MSDN regarding 2012 AXapta restructuring

This would be useful when you write customized code for PO posting, SO posting, Inventory management etc in AX 2012

Partial posting of purchase order in AX 2012

In my previous post
we created code to post product receipt via x++. 
Now this code would post the lines of the PO appropriately as per the quantity specified on the line , provided the ReceiveNow quantity on the purchParmLine is set appropriately. The inventory transfer would work fine for a normal item,with the deduction of the partial quantity alone from the ordered quantity. Yet when you have a serial number tracked or lot item , while creation of receipt say you do not enter the sl no or lot id , yet during posting if you have your lot ID /sl no (inserted through some process via an interfacing application)  you may face problems as the transactions would not have the batch number or serial number specified on them and in 2012 Axapta does all posting on the basis of transactions. (This may lead to negative inventory(ordered qty) against the particular sl no or lot ID.

So to make the whole thing work, you would need to split the transactions via some logic through code by calling the InventTrans.updateSplit method and then register the inventory with the transaction that you created via the split. The registering of inventory would make use of code from class InventTransWMS_Register, method updateInvent which would encompass this piece of code in particular:
 registered = InventUpd_Registered::newParameters(movement,inventDim,inventDimParm,inventDim,inventDimParm,_tmpInventTransWMS.InventQty, _tmpInventTransWMS.PdsCWQty);
      registered.parmSkipQualityOrderCreation(true);  // We handle quality order creation later in this method
      registered.parmInventReceiptForTestRegistrator(inventReceiptForTestRegistrator);

       if (movement.mustBeQuarantineControlled())
            {
                    registered.parmInventDimQuarantine(inventDimQuarantine);
            }

        if (_tmpInventTransWMS.InventTrans)
            {
                    inventTransPrefered = InventTrans::findRecId(_tmpInventTransWMS.InventTrans);
                    if (inventTransPrefered.RecId)
                    { // Only use if sign/direction matches specified quantity.
                        if ((_tmpInventTransWMS.InventQty > 0 && inventTransPrefered.StatusReceipt == StatusReceipt::Ordered)
                         || (_tmpInventTransWMS.InventQty < 0 && inventTransPrefered.StatusReceipt == StatusReceipt::Registered))
                        {
                            registered.parmPreferedInventTransRecId(_tmpInventTransWMS.InventTrans);
                        }
                    }
             }
        movement.parmPDSBaseValue(_tmpInventTransWMS.pdsBaseValue);

        if (inventDim.InventBatchId)
        {
                    registered.pdsSetBatchAttributeValue(inventDim.InventBatchId);
        }

        registered.updateNow();
        inventReceiptForTestRegistrator = registered.parmInventReceiptForTestRegistrator();
        if (inventDim.InventBatchId)
        {
                    inventBatch = InventBatch::find(inventDim.InventBatchId, _tmpInventTransWMS.ItemId, true);
                    inventBatch.pdsInitFromVendBatchDetails(_tmpInventTransWMS.bufferPdsVendBatchInfo());
                    inventBatch.update();
         }
         inventQtySumCW += _tmpInventTransWMS.PdsCWQty;

         inventQtySum += _tmpInventTransWMS.InventQty;

         if (inventReceiptForTestRegistrator)
         {
                InventQualityManagementCreateHandler::createInventoryRegistration(
                    inventTransOriginId,
                    inventReceiptForTestRegistrator);
         }


        if (inventTransWMSRegistrationObserver
            && inventQtySum)
        {
            inventTransWMSRegistrationObserver.notifyArrivalRegistered(inventQtySum, inventQtySumCW, inventDim);
        }

        _tmpInventTransWMS.ttscommit();



Posting of product receipt via code in AX 2012.

The posting of product receipt/packing slip for purchase order is primarily done by using the class: PurchFormLetter. The functions of PurchFormLetter class of 2009 AX are done by the code of two classes in 2012 : One is PurchFormLetter and the other: purchFormLetterParmData.
The class purchFormLetterParmData is responsible for creation of data which would be used by PurchFormLetter.

We could use the PurchFormLetter update method for posting packing slip in most cases, a typical code snippet will look like:

static void Dev_CreatePO_and_Invoice(Args _args)
{
NumberSeq numberSeq;
Purchtable Purchtable;
PurchLine PurchLine;
PurchFormLetter purchFormLetter;

;

ttsbegin;
numberSeq = NumberSeq::newGetNumFromCode(purchParameters::numRefPurchaseOrderId().NumberSequence,true);

// Initialize Purchase order values
Purchtable.initValue();
Purchtable.PurchId = numberSeq.num();
Purchtable.OrderAccount = '3000';
Purchtable.initFromVendTable();

if (!Purchtable.validateWrite())
{
throw Exception::Error;
}
Purchtable.insert();

// Initialize Purchase Line items
PurchLine.PurchId = Purchtable.PurchId;
PurchLine.ItemId = 'B-R14';
PurchLine.createLine(true, true, true, true, true, false);
ttscommit;

purchFormLetter = purchFormLetter::construct(DocumentStatus::PackingSlip);
purchFormLetter.update(purchtable, // Purchase record Buffer
"PO_"+purchTable.PurchId, // Invoice Number
systemdateget()); // Transaction date


if (PurchTable::find(purchTable.PurchId).DocumentStatus == DocumentStatus:: PackingSlip)
{
info(strfmt("Posted invoiced journal for purchase order %1",purchTable.PurchId));
}
}


However in case you would be using run, the code structure would look similar to the one in the PurchFormLetter.update method. To create parmData we would use

purchFormLetterParmData = PurchFormletterParmData::newData(DocumentStatus::PackingSlip,VersioningUpdateType::Initial);
            purchFormLetterParmData.createData_IN(CustomsImportOrderType_IN::PurchaseOrder,false);

The piece of code would look like:
         //create our purchParmTable record to post against
         purchFormLetter = PurchFormLetter::construct(DocumentStatus::PackingSlip);
         //insert data into table PurchParamUpdate
purchFormLetterParmData = PurchFormletterParmData::newData(DocumentStatus::PackingSlip,VersioningUpdateType::Initial);
        purchFormLetterParmData.createData_IN(CustomsImportOrderType_IN::PurchaseOrder,false);
        purchParmTable.clear();
        purchParmTable.TransDate = SystemDateGet();
        purchParmTable.Ordering = DocumentStatus::PackingSlip;
        purchParmTable.ParmJobStatus = ParmJobStatus::Waiting;
        purchParmTable.Num = strfmt('%1', ILSGatewayQueue.RecId);
        purchParmUpdate = purchFormLetterParmData.parmParmUpdate();
        purchParmTable.ParmId = purchParmUpdate.ParmId;
        // fill in fields from PurchTable
        purchParmTable.PurchId = purchTable.PurchId;
        purchParmTable.PurchName = purchTable.PurchName;
        purchParmTable.DeliveryName= purchTable.DeliveryName;
        purchParmTable.DeliveryPostalAddress  = purchTable.DeliveryPostalAddress;
        purchParmTable.OrderAccount = purchTable.OrderAccount;
        purchParmTable.CurrencyCode = purchTable.CurrencyCode;
        purchParmTable.InvoiceAccount = purchTable.InvoiceAccount;
        purchParmTable.insert();

// here we would insert lines into purchParmLine add some logic etc
         purchFormLetter.transDate(systemDateGet());
        purchFormLetter.proforma(false);
        purchFormLetter.specQty(PurchUpdate::All);

        purchFormLetter.purchTable(purchTable);
        purchFormLetter.parmParmTableNum(purchParmTable.ParmId);// this is the ID we hard code as the product receipt ID, if we do the posting via UI,
                                                                    // user would have the option to manually enter this value
        purchFormLetter.parmId(purchParmTable.ParmId);
        purchFormLetter.purchParmUpdate(purchFormLetterParmData.parmParmUpdate());

        purchFormLetter.run();






Lookup issue in AX and EDT relationships

Recently I came across an issue with populating the datagrid for a  combobox in AX.  (during migration of our project from 2009 to 2012). The lookup grid was empty though the view details opened up a form with two detail records.
So I added the following code by overriding the lookup method :
public void lookup()
{
    SysTableLookup          sysTableLookup;
    Query                   query;
    QueryBuildDataSource    queryBuildDataSource;
    ;

    sysTableLookup = SysTableLookup::newParameters(tablenum(tableName), this);
    query = new Query();
    queryBuildDataSource = query.addDataSource(tablenum(tableName));
    sysTableLookup.addLookupfield(fieldnum(tableName, NameOfColumnThatYouWantToSeeInTheLookup));
    sysTableLookup.parmQuery(query);
    sysTableLookup.performFormLookup();
}

The lookup then displayed the list of details , yet when I selected a particular value I got the following error: “Table ‘table name’ does not contain an index with id ’65535′
This was because of the relationship on the EDT because the value selected disobeyed it. EDT migration was the step missed out in the whole process, when I ran the EDT migration tool the problem was resolved- I was able to see the lookup without even writing code in the overridden method ‘lookup.’