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();