Split purchase order line delivery schedules in X++ code

The schedule lines functionality of the Purchase Order details form is needed when the quantity received from the supplier (vendor) is less than the quantity ordered in the purchase line. This is normally due to multiple shipments, so you would need to split the original line in two: the (i) actual received quantity and the (ii) remaining quantity to be received with the next shipment(s). You might need to replicate this functionality in X++ code such as a D365 custom interface to receive items from a 3PL. The following post explains how you can easily achieve this.

The Delivery Schedule form can be opened from the Purchase Order Details form, Line view, click on “Purchase Order Line” > “Delivery Schedule” from the lines grid action pane. If you look at the code of the form that is opened PurchDeliverySchedule there a number of methods being called in the init, active, create and write methods of the datasource. The form actually creates two temporary purchase lines and only splits the original line on the closeOk method of the form.

If you try to replicate the code it might be a bumpy ride until you get it right. What really helped me was discovering that there is an ATL (Acceptance test library) class AtlCommandPurchaseOrderDeliverySchedule that already has this functionality in X++ code. You will find this class from D365 FO version 10.0.2 onwards. Below are some extracts of this code including some additional code and comments that I added myself.

// First create an instance of the PurchTableForm_DeliverySchedule class using the // original purchase line record.
PurchTableForm_DeliverySchedule purchTableForm_DeliverySchedule = new PurchTableForm_DeliverySchedule(originalPurchLine);

// Then add the two lines (or as many as you need) with the different quantity for // the various shipments. 
// This method is in class AtlCommandPurchaseOrderDeliverySchedule.
public final AtlCommandPurchaseOrderDeliverySchedule addDeliveryScheduleLine(
        PurchQty    _qty = 1,
        TransDate   _confirmDate = dateNull(),
        TransDate   _deliveryDate = DateTimeUtil::getToday(DateTimeUtil::getUserPreferredTimeZone()))
{
        tmpPurchaseLine.setTmp();

        if (isFirstScheduleLineFlag)
        {
            purchTableForm_DeliverySchedule.purchLine_Init(tmpPurchaseLine);
            isFirstScheduleLineFlag = false;
        }
        else
        {
            tmpPurchaseLine.clear();
            purchTableForm_DeliverySchedule.purchLine_InitValue(tmpPurchaseLine);
        }
                    purchTableForm_DeliverySchedule.interCompanySetLineAccess(tmpPurchaseLine);
        purchTableForm_DeliverySchedule.directDeliverySetLineAccess(tmpPurchaseLine);

        purchTableForm_DeliverySchedule.purchLine_CreatePreSuper(tmpPurchaseLine);

        tmpPurchaseLine.PurchQty = _qty;
        PurchLine::modifyPurchQty(tmpPurchaseLine, tmpPurchaseLine.inventDim());

        var inputContractPre = PurchLineWritePreSuperInputContract::construct();
        var inputContractPost = PurchLineWritePostSuperInputContract::construct();
        inputContractPre.parmPurchLine(tmpPurchaseLine);
        purchTableForm_DeliverySchedule.purchLine_WritePreSuper(inputContractPre);

        if (tmpPurchaseLine.RecId)
        {
            tmpPurchaseLine.doUpdate();
        }
        else
        {
            var numberSeq = NumberSeq::newGetNum(InventParameters::numRefInventTransId());
            tmpPurchaseLine.InventTransId = numberSeq.num();
            tmpPurchaseLine.sourceDocumentLine = this.getNextSouceDocumentNumber();

            tmpPurchaseLine.doInsert();
        }

        inputContractPost.parmPurchLine(tmpPurchaseLine);
        purchTableForm_DeliverySchedule.purchLine_WritePostSuper(inputContractPost);

        return this;
}

//Finally execute the method to create the new lines and update the original line.
// 1. First add the temporary lines to a list
List scheduleLines = new List(Types::Record);

while select tmpPurchaseLine
{
        scheduleLines.addEnd(tmpPurchaseLine);
}

// 2. Set the parameters and execute updatePurchLineTable on purchTableForm_DeliverySchedule

purchTableForm_DeliverySchedule.parmScheduleLines(scheduleLines);
purchTableForm_DeliverySchedule.parmMarkupConversionMode(DlvScheduleMarkupConversionMode::Copy);
purchTableForm_DeliverySchedule.updatePurchLineTable(false);

When the process runs successfully you will see the original line with the two new lines. The original line will have an inventory deliver remainder quantity of zero.