Showing posts with label Contract. Show all posts
Showing posts with label Contract. Show all posts

Tuesday, April 13, 2021

How to pass marked records from grid to sysoperations framework in AX

Controller class

/// <summary>
/// 
/// </summary>
class Sales_UpdateMoDSalesOrderController extends SysOperationServiceController
{
    public static Sales_UpdateMoDSalesOrderController construct(Args _args)
    {
        Sales_UpdateMoDSalesOrderController  controller = new Sales_UpdateMoDSalesOrderController(classStr( Sales_UpdateMoDSalesOrderService ),
                                                                                                        methodStr( Sales_UpdateMoDSalesOrderService, updateMoDSalesOrder ),
            SysOperationExecutionMode::Synchronous);

        controller.parmArgs( _args );

        return controller;
    }

    public static void main(Args _args)
    {
        Set                                     recordSet;
        SalesTable                              salesTable;
        MultiSelectionHelper                    multiselectionHelper;
        Sales_UpdateMoDSalesOrderContract    dataContract;
        Sales_UpdateMoDSalesOrderController  controller;

        controller = Sales_UpdateMoDSalesOrderController::construct( _args );

        // Get marked records from calling form
        if (_args && _args.caller() is FormRun)
        {
            recordSet = new Set(Types::Record);
            multiselectionHelper = MultiSelectionHelper::createFromCaller(_args.caller());
        
            salesTable = multiselectionHelper.getFirst();

            while (salesTable)
            {
                recordSet.add(salesTable);
            
                salesTable = multiselectionHelper.getNext();
            }
        }

        // Put records from calling datasource into dataContract
        dataContract = controller.getDataContractObject();

        if (dataContract is Sales_UpdateMoDSalesOrderContract)
        {
            dataContract.parmRecordCon(recordSet.pack());
        }

        controller.startOperation();
    }
}

Contract class

/// <summary>
/// The data contract for Intercompany update of Mode of delivery
/// </summary>
[
    DataContract,
    SysOperationGroup('GeneralGroup',"@SYS81043",'1',FormArrangeMethod::Vertical)
]
class Sales_UpdateMoDSalesOrderContract implements SysOperationInitializable, SysOperationValidatable
{
    UpsModeOfDelivery upsModeOfDelivery;
    container            recordCon;

    /// <summary>
    /// Indicates whether the contract is valid.
    /// </summary>
    /// <returns>
    /// A Boolean value that indicates whether the contract is valid.
    /// </returns>
    public boolean validate()
    {
        boolean isValid = true;
        return isValid;
    }

    /// <summary>
    /// Initailizes the default values to parameters.
    /// </summary>
    public void initialize()
    {
    }

    /// <summary>
    /// Method used to specify the data memmber attribute
    /// </summary>
    /// <param>
    /// <name>parmShipmentDate</name>is used to set and get the shipmentDate
    /// </param>
    [
         DataMember,
         SysOperationGroupMember('GeneralGroup'),
         SysOperationDisplayOrder('1'),
         SysOperationLabelAttribute(literalstr("@Warehouse:UpsModeOfDelivery"))
    ]
    public UpsModeOfDelivery parmUpsModeOfDelivery( UpsModeOfDelivery _upsModeOfDelivery = upsModeOfDelivery )
    {
        upsModeOfDelivery  =  _upsModeOfDelivery;

        return upsModeOfDelivery;
    }

    /// <summary>
    /// Method used to specify the data memmber attribute
    /// </summary>
    /// <param name = "_recordCon"></param>
    /// <returns></returns>
    [DataMemberAttribute, SysOperationControlVisibilityAttribute(false)]
    public container parmRecordCon(container _recordCon = recordCon)
    {
        recordCon = _recordCon;

        return recordCon;
    }

}

Service class

class Sales_UpdateMoDSalesOrderService
{
    /// <summary>
    /// Creates transfer order for from and to wareshouse combination
    /// </summary>
    /// <param name = "_contract">TransferOrderCreateContract from dialog</param>
    public void updateMoDSalesOrder(Sales_UpdateMoDSalesOrderContract _contract)
    {
        Set                     recordSet;
        Common                  record;
        SalesTable              salesTable;
        SetEnumerator           se;
        ModeOfDeliverySetup  smModeOfDeliverySetup;

        // Process records
        if (conLen(_contract.parmRecordCon()) > 0)
        {
            recordSet = Set::create(_contract.parmRecordCon());
            se = recordSet.getEnumerator();
            while (se.moveNext())
            {
                record = se.current();

                switch (record.TableId)
                {
                    case tableNum(SalesTable):
                        salesTable = record;
                        salesTable.DlvMode = _contract.parmUpsModeOfDelivery();

                        info(strFmt("%1 %2", salesTable.SalesId, salesTable.DlvMode));
                        break;

                    default:
                        break;
                }
            }
        }
    }

}

Sysoperations framework sample

UIBuilder class

/// <summary>
/// The <c>TransferOrderCreateProposalUIBuilder</c> The data contract for Transfer order creation.
/// </summary>
class TransferOrderCreateProposalUIBuilder extends SysOperationAutomaticUIBuilder
{
    DialogField                                 toDate;
    DialogField                                 timeFence;
    DialogField                                 fromSite;
    DialogField		                            fromWarehouse,toWarehouses;
    DialogField                                 proposalModel;
    DialogField                                 recalculateQuantities;
    DialogField                                 replaceCurrentProposals;
    

    SysLookupMultiSelectCtrl                    ctrlSources;
    
    /// <summary>
    /// 
    /// </summary>
    public void postBuild()
    {
        TransferOrderCreateProposalContract contract	= this.dataContractObject() as TransferOrderCreateProposalContract;

        toDate                       = this.bindInfo().getDialogField(contract, methodStr(TransferOrderCreateProposalContract, parmToDate));
        timeFence                    = this.bindInfo().getDialogField(contract, methodStr(TransferOrderCreateProposalContract, parmTimeFence));
        fromSite                     = this.bindInfo().getDialogField(contract, methodStr(TransferOrderCreateProposalContract, parmInventSiteId));
        fromWarehouse                = this.bindInfo().getDialogField(contract, methodStr(TransferOrderCreateProposalContract, parmInventLocationIdFrom));
        toWarehouses	             = this.bindInfo().getDialogField(contract, methodStr(TransferOrderCreateProposalContract, parmInventLocationIdTo));
        proposalModel	             = this.bindInfo().getDialogField(contract, methodStr(TransferOrderCreateProposalContract, parmProposalModel));
        recalculateQuantities        = this.bindInfo().getDialogField(contract, methodStr(TransferOrderCreateProposalContract, parmRecalculateQuantities));
        replaceCurrentProposals      = this.bindInfo().getDialogField(contract, methodStr(TransferOrderCreateProposalContract, parmReplaceCurrentProposal));
        
        replaceCurrentProposals.allowEdit(false);
        recalculateQuantities.visible(false);
        toWarehouses.lookupButton(FormLookupButton::Always);

        switch (this.controller().parmArgs().menuItemName())
        {
            case menuItemActionStr(TransferOrderCreateProposal):
                replaceCurrentProposals.value(true);
                recalculateQuantities.value(false);
                toDate.value(DateTimeUtil::getSystemDate(DateTimeUtil::getUserPreferredTimeZone()));
                break;
            case menuItemActionStr(TransferOrderEditProposal):
                TransferOrderProposal    transferOrderProposal = this.controller().parmArgs().record();

                fromSite.allowEdit(false);
                fromWarehouse.allowEdit(false);
                proposalModel.allowEdit(false);

                recalculateQuantities.value(false);
                replaceCurrentProposals.value(false);
                toDate.value(transferOrderProposal.ToDate);
                fromSite.value(transferOrderProposal.InventSiteIdFrom);
                timeFence.value(transferOrderProposal.timeFence);
                proposalModel.value(transferOrderProposal.ProposalModel);
                fromWarehouse.value(transferOrderProposal.InventLocationIdFrom);
                break;

            case menuItemActionStr(TransferOrderRecalculateQty):
                toDate.visible(false);
                fromSite.visible(false);
                fromWarehouse.visible(false);
                timeFence.visible(false);
                toWarehouses.visible(false);
                replaceCurrentProposals.visible(false);

                recalculateQuantities.value(true);
                replaceCurrentProposals.value(false);
                break;

            default:
                break;
        }
    }

    /// <summary>
    /// 
    /// </summary>
    public void postRun()
    {
        if ( this.controller().parmArgs().menuItemName() == menuItemActionStr(TransferOrderCreateProposal) )
        {
            this.WarehouseLookup(false);
        }
        else if ( this.controller().parmArgs().menuItemName() == menuItemActionStr(TransferOrderEditProposal) )
        {
            this.WarehouseLookup(true);
        }
    }

    /// <summary>
    /// Multi select lookup for warehouse
    /// </summary>
    /// <param name = "_preselectValues">true for preselect lookup values</param>
    public void WarehouseLookup(boolean _preselectValues)
    {
        Query		            query		    = new Query();
        QueryBuildDataSource    qbdsWarehouses  = query.addDataSource(tablenum(InventLocation));

        container selectedFields = [tableNum(InventLocation), fieldNum(InventLocation, InventLocationId)];

        qbdsWarehouses.fields().addField(fieldNum(InventLocation, InventLocationId));
        qbdsWarehouses.fields().addField(fieldNum(InventLocation, Name));

        ctrlSources = SysLookupMultiSelectCtrl::constructWithQuery(this.dialog().formRun(),
                                                                   toWarehouses.control(),
                                                                   query,
                                                                   true,
                                                                   selectedFields);
        if (_preselectValues)
        {
            TransferOrderProposal    transferOrderProposal, transferOrderProposalLoc = this.controller().parmArgs().record();
            InventLocation              inventLocation;

            container selectedRecIds;
            container selectedValues;

            while select InventLocationIdTo from transferOrderProposal group by InventLocationIdTo
                where transferOrderProposal.ProposalModel == transferOrderProposalLoc.ProposalModel
            {
                inventLocation = InventLocation::find(transferOrderProposal.InventLocationIdTo);

                selectedRecIds += inventLocation.RecId;
                selectedValues += inventLocation.InventLocationId;
            }

            ctrlSources.set([selectedRecIds, selectedValues]);
        }
    }

}

Service class

/// <summary>
/// The <c>TransferOrderCreationProposalService</c> is used to create Transfer order proposals
/// for planned orders.
/// </summary>
class TransferOrderCreateProposalService
{
    List                                    toWareHouse;
    Query                                   query;
    counter                                 timeFence, lineNum;
    boolean                                 replaceCurrentProposal;
    String20                                proposalModel;
    TransDate                               fromDate,toDate;
    InventTable                             inventTable;
    InventSiteId                            fromInventSite;
    InventLocationId                        fromWarehouse;
    Enumerator                              enumerator;
    TransferOrderCreateProposalContract  contract;

    /// <summary>
    /// Entry point for creating Transfer order propsal lines based on proposal model
    /// </summary>
    /// <param name = "_contract">TransferOrderCreateProposalContract passed from dialog</param>
    public void createTransferOrderProposal(TransferOrderCreateProposalContract _contract)
    {
        contract = _contract;
       
        this.initializeQuery();
        this.initializeParameters();
        
        ttsbegin;

        this.deleteTranferProposalsLines(proposalModel, replaceCurrentProposal);
        this.createTransferOrderProposalLines();

        ttscommit;
    }

    /// <summary>
    /// Method is used to delete the Transfer order propsal lines based on proposal model
    /// </summary>
    /// <param name = "_proposalModel">from dialog</param>
    /// <param name = "_replaceCurrentProposal">true if creating new transfer order proposal</param>
    public void deleteTranferProposalsLines(ProposalModelId _proposalModel, boolean _replaceCurrentProposal)
    {
        if (_replaceCurrentProposal)
        {
            TransferOrderProposal    transferOrderProposal;

            delete_from transferOrderProposal
                where transferOrderProposal.ProposalModel == _proposalModel;
        }
    }

    /// <summary>
    /// intilalize parameters
    /// </summary>
    public void initializeParameters()
    {
        toDate                  = contract.parmToDate();
        timeFence               = contract.parmTimeFence();

        fromDate                = contract.parmToDate() - timeFence;

        toWareHouse             = contract.parmInventLocationIdTo();
        fromWarehouse           = contract.parmInventLocationIdFrom();
        proposalModel           = contract.parmProposalModel();
        fromInventSite          = contract.parmInventSiteId();
        replaceCurrentProposal  = contract.parmReplaceCurrentProposal();
    }

    /// <summary>
    /// initialize query object
    /// </summary>
    public void initializeQuery()
    {
        query = contract.parmQuery();
    }

    /// <summary>
    /// Method is used to create the Transfer order propsal lines based on proposal model
    /// </summary>
    public void createTransferOrderProposalLines()
    {
        Real                        workingdays;
        InventLocation              inventLocation;
        InventQtyTotal              inventQty;
        RecordInsertList            transferOrderProposalInsertList = null;
        TransferOrderProposal    transferOrderProposal;

        QueryRun queryRun = new QueryRun(query);

        while (queryRun.next())
        {
            transferOrderProposalInsertList = new RecordInsertList(tablenum(TransferOrderProposal));

            inventTable =   queryRun.get(tablenum(InventTable));
            enumerator  =   toWareHouse.getEnumerator();

            try
            {         
                ttsbegin;

                while (enumerator.moveNext())
                {
                    if (!replaceCurrentProposal)
                    {
                        if (TransferOrderProposal::exist(inventTable.ItemId, enumerator.current(), proposalModel))
                        {
                            lineNum++;
                            continue;
                        }
                    }

                    transferOrderProposal.clear();

                    lineNum++;

                    transferOrderProposal.LineNum                   = lineNum;
                    transferOrderProposal.ItemId                    = inventTable.ItemId;
                    transferOrderProposal.ItemName                  = inventTable.itemName();
                    transferOrderProposal.ToDate                    = toDate;
                    transferOrderProposal.TimeFence                 = timeFence;
                    transferOrderProposal.InventSiteIdFrom          = fromInventSite;

                    inventLocation = InventLocation::find(fromWarehouse);

                    transferOrderProposal.InventLocationIdFrom      = inventLocation.InventLocationId;
                    transferOrderProposal.InventLocationIdFromName  = inventLocation.Name;
                    transferOrderProposal.InventSiteIdTo            = fromInventSite;

                    inventLocation = InventLocation::find(enumerator.current());

                    transferOrderProposal.InventLocationIdTo        = inventLocation.InventLocationId;
                    transferOrderProposal.InventLocationIdToName    = inventLocation.Name;
                    transferOrderProposal.ProposalModel             = proposalModel;
                    transferOrderProposal.CoverageGroup             = inventTable.ReqGroupId;
                    
                    transferOrderProposal.RefillAvailablePhysical   = this.getRefillItemAvailPhysicalBySiteAndWarehouse(transferOrderProposal.ItemId,
                                                                                                                        transferOrderProposal.InventSiteIdFrom,
                                                                                                                        transferOrderProposal.InventLocationIdFrom);
                    inventQty = 0;
                    inventQty = this.getItemTotalSalesBySiteAndWarehouse(transferOrderProposal.ItemId,
                                                                         transferOrderProposal.InventSiteIdFrom,
                                                                         transferOrderProposal.InventLocationIdFrom,
                                                                         fromDate,
                                                                         toDate);
                    workingdays = 0;
                    workingdays = this.getNumOfWorkingdays(transferOrderProposal.InventLocationIdFrom, fromDate, toDate);

                    transferOrderProposal.RefillSalesPerDay = workingdays ? inventQty / workingdays : 0;

                    transferOrderProposal.RefillCoverageDays = transferOrderProposal.RefillSalesPerDay ? transferOrderProposal.RefillAvailablePhysical / transferOrderProposal.RefillSalesPerDay : 0;

                    transferOrderProposal.DCAvailablePhysical   = this.getDCItemAvailPhysicalBySiteAndWarehouse(transferOrderProposal.ItemId,
                                                                                                                transferOrderProposal.InventSiteIdTo,
                                                                                                                transferOrderProposal.InventLocationIdTo);
                    inventQty = 0;
                    inventQty = this.getItemTotalSalesBySiteAndWarehouse(transferOrderProposal.ItemId,
                                                                         transferOrderProposal.InventSiteIdTo,
                                                                         transferOrderProposal.InventLocationIdTo,
                                                                         fromDate,
                                                                         toDate);
                    workingdays = 0;
                    workingdays = this.getNumOfWorkingdays(transferOrderProposal.InventLocationIdTo,fromDate, toDate);

                    transferOrderProposal.DCSalesPerDay = workingdays ? inventQty / workingdays : 0;

                    transferOrderProposal.DCCoverageDays = transferOrderProposal.DCSalesPerDay ? transferOrderProposal.DCAvailablePhysical / transferOrderProposal.DCSalesPerDay : 0;

                    transferOrderProposalInsertList.add(transferOrderProposal);
                }

                transferOrderProposalInsertList.insertDatabase();

                ttscommit;
                
            }
            catch(Exception::Error)
            {
                throw error( "@Planning:TransferOrderPropsalCreationError");
            }
            catch(Exception::Deadlock )
            {
                if(xSession::currentRetryCount() >= Planning_Constants::MaxOCCRetryCount)
                {
                    throw Exception::Deadlock;
                }
                else
                {
                    retry;
                }
            }
            catch(Exception::UpdateConflict)
            {
                if (appl.ttsLevel() == 0)
                {
                    if (xSession::currentRetryCount() >= Planning_Constants::MaxOCCRetryCount)
                    {
                        throw Exception::UpdateConflictNotRecovered;
                    }
                    else
                    {
                        retry;
                    }
                }
                else
                {
                    throw Exception::UpdateConflict;
                }
            }

            info(strFmt("@Planning:TransferOrderProposalCreationSuccess", lineNum, proposalModel));
        }
    }

    /// <summary>
    /// Calculate total sales between daterange for each item based on site and warehouse
    /// </summary>
    /// <param name = "_itemId"></param>
    /// <param name = "_inevntSiteId"></param>
    /// <param name = "_inventLocationId"></param>
    /// <returns></returns>
    public InventQtyTotal getItemTotalSalesBySiteAndWarehouse(ItemId _itemId, 
                                                              InventSiteId _inventSiteId,
                                                              InventLocationId _inventLocationId,
                                                              FromDate _fromDate,
                                                              ToDate _toDate)
    {
        InventDim           inventDim;
        InventTrans         inventTrans;
        InventTransOrigin   inventTransOrigin;


        select sum(Qty) from inventTrans
                where inventTrans.ItemId == _itemId
                    && inventTrans.DatePhysical >= _fromDate
                    && inventTrans.DatePhysical <= _toDate
                    exists join inventTransOrigin
                    where inventTransOrigin.RecId == inventTrans.InventTransOrigin
                        && inventTransOrigin.ReferenceCategory == InventTransType::Sales
                        exists join inventDim
                        where inventDim.inventDimId == inventTrans.inventDimId
                            && inventDim.InventSiteId == _inventSiteId
                            && inventDim.InventLocationId == _inventLocationId;

        return abs(inventTrans.Qty);
    }

    /// <summary>
    /// calculates number of working days based on warehouse or Legal entity base calender
    /// </summary>
    /// <param name = "_inventLocationId"></param>
    /// <returns></returns>
    public Real getNumOfWorkingdays(InventLocationId _inventLocationId, FromDate _fromDate, ToDate _toDate)
    {
        Real                workingdays;
        CalendarId          calenderId;
        WorkCalendarDate    WorkCalendarDate;

        if (InventLocation::find(_inventLocationId).ReqCalendarId)
        {
            calenderId = InventLocation::find(_inventLocationId).ReqCalendarId;
        }
        else
        {
            calenderId = CompanyInfo::find().ShippingCalendarId;
        }

        select count(RecId) from WorkCalendarDate
            where WorkCalendarDate.CalendarId       == calenderId
                && (WorkCalendarDate.TransDate      >= _fromDate
                && WorkCalendarDate.TransDate       <= _toDate)
                && WorkCalendarDate.WorkTimeControl == WorkTimeControl::Open;

        workingdays = any2Real(WorkCalendarDate.RecId);

        if (!calenderId)
        {
            Real noOfDays    = _toDate - _fromDate;

            //Formula for calculating working days if No base calender is present
            workingdays = (abs(noOfDays) / Planning_Constants::NumOfDaysInAWeek ) * Planning_Constants::MinWorkingDaysInAWeek;
        }
        
        return abs(workingdays);
    }

    /// <summary>
    /// Calculate avaialable physical inventory for each item based on site and warehouse
    /// </summary>
    /// <param name = "_itemId"></param>
    /// <param name = "_inevntSiteId"></param>
    /// <param name = "_inventLocationId"></param>
    /// <returns>Refill warehouse avail physical</returns>
    public InventQtyAvailPhysical getRefillItemAvailPhysicalBySiteAndWarehouse(ItemId _itemId, InventSiteId _inventSiteId, InventLocationId _inventLocationId)
    {
        InventDim       inventDim;
        InventDimParm   inventDimParm;
      
        inventDim = this.initInventDim(_inventSiteId, _inventLocationId);

        inventDimParm.initFromInventDim(inventDim);

        return abs(InventOnhand::newParameters(_itemId, inventDim, inventDimParm).availPhysical());
    }

    /// <summary>
    /// Calculate avaialable physical inventory for each item based on site and warehouse
    /// </summary>
    /// <param name = "_itemId"></param>
    /// <param name = "_inevntSiteId"></param>
    /// <param name = "_inventLocationId"></param>
    /// <returns>DC warehouse avail physical</returns>
    public InventQtyAvailPhysical getDCItemAvailPhysicalBySiteAndWarehouse(ItemId _itemId, InventSiteId _inventSiteId, InventLocationId _inventLocationId)
    {
        InventDim           inventDim;
        InventTrans         inventTrans;
        InventDimParm       inventDimParm;
        inventTransOrigin   InventTransOrigin;
        InventQty           totalAvailPhysical;
      
        inventDim = this.initInventDim(_inventSiteId, _inventLocationId);

        inventDimParm.initFromInventDim(inventDim);

        totalAvailPhysical = abs(InventOnhand::newParameters(_itemId, inventDim, inventDimParm).availPhysical());

        inventDim.clear();

        select sum(Qty) from inventTrans
                where inventTrans.ItemId == _itemId
                    && inventTrans.DatePhysical == dateNull()
                    && inventTrans.StatusIssue == StatusIssue::ReservPhysical
                    exists join inventTransOrigin
                    where inventTransOrigin.RecId == inventTrans.InventTransOrigin
                        && (inventTransOrigin.ReferenceCategory == InventTransType::TransferOrderShip
                            || inventTransOrigin.ReferenceCategory == InventTransType::TransferOrderReceive)
                            exists join inventDim
                            where inventDim.inventDimId   == inventTrans.inventDimId
                            && inventDim.InventSiteId     == _inventSiteId
                            && inventDim.InventLocationId == _inventLocationId;

        return totalAvailPhysical + abs(inventTrans.Qty);
    }

    /// <summary>
    /// Initilalize InevntDim by site and warehouse
    /// </summary>
    /// <param name = "_inevntSiteId">SiteId from proposal line</param>
    /// <param name = "_inventLocationId">LocationId from proposal line</param>
    /// <returns>InventDim buffer</returns>
    public InventDim initInventDim(InventSiteId _inevntSiteId, InventLocationId _inventLocationId)
    {
        InventDim inventDim;
      
        inventDim.InventSiteId      = _inevntSiteId;
        inventDim.InventLocationId  = _inventLocationId;

        inventDim = InventDim::findOrCreate(inventDim);
        
        return inventDim;
    }

    /// <summary>
    /// Recalculates and updates avail physical quanties
    /// </summary>
    /// <param name = "_contract">TransferOrderCreateProposalContract from dialog</param>
    public void recalculateQuanties(TransferOrderCreateProposalContract _contract)
    {
        TransferOrderProposal    transferOrderProposal;
        InventQtyTotal              inventQty;
        Real                        workingdays;

        while select forupdate transferOrderProposal
            where transferOrderProposal.ProposalModel == _contract.parmProposalModel()
        {
            try
            {
                ttsbegin;
                transferOrderProposal.RefillAvailablePhysical   = this.getRefillItemAvailPhysicalBySiteAndWarehouse(transferOrderProposal.ItemId,
                                                                                                                    transferOrderProposal.InventSiteIdFrom,
                                                                                                                    transferOrderProposal.InventLocationIdFrom);

                inventQty = 0;
                inventQty = this.getItemTotalSalesBySiteAndWarehouse(transferOrderProposal.ItemId,
                                                                     transferOrderProposal.InventSiteIdFrom,
                                                                     transferOrderProposal.InventLocationIdFrom,
                                                                     transferOrderProposal.ToDate - transferOrderProposal.TimeFence,
                                                                     transferOrderProposal.ToDate);
                workingdays = 0;
                workingdays = this.getNumOfWorkingdays(transferOrderProposal.InventLocationIdFrom, transferOrderProposal.ToDate - transferOrderProposal.TimeFence, transferOrderProposal.ToDate);

                transferOrderProposal.RefillCoverageDays = transferOrderProposal.RefillSalesPerDay ? transferOrderProposal.RefillAvailablePhysical / transferOrderProposal.RefillSalesPerDay : 0;

                transferOrderProposal.DCAvailablePhysical   = this.getDCItemAvailPhysicalBySiteAndWarehouse(transferOrderProposal.ItemId,
                                                                                                            transferOrderProposal.InventSiteIdTo,
                                                                                                            transferOrderProposal.InventLocationIdTo);
                inventQty = 0;
                inventQty = this.getItemTotalSalesBySiteAndWarehouse(transferOrderProposal.ItemId,
                                                                     transferOrderProposal.InventSiteIdTo,
                                                                     transferOrderProposal.InventLocationIdTo,
                                                                     transferOrderProposal.ToDate - transferOrderProposal.TimeFence,
                                                                     transferOrderProposal.ToDate);
                workingdays = 0;
                workingdays = this.getNumOfWorkingdays(transferOrderProposal.InventLocationIdFrom, transferOrderProposal.ToDate - transferOrderProposal.TimeFence, transferOrderProposal.ToDate);


                transferOrderProposal.DCCoverageDays = transferOrderProposal.DCSalesPerDay ? transferOrderProposal.DCAvailablePhysical / transferOrderProposal.DCSalesPerDay : 0;

                transferOrderProposal.update();

                ttscommit;
            }
            catch(Exception::Error)
            {
                throw error( "@Planning:TransferOrderPropsalCreationError");
            }
            catch(Exception::Deadlock )
            {
                if(xSession::currentRetryCount() >= Planning_Constants::MaxOCCRetryCount)
                {
                    throw Exception::Deadlock;
                }
                else
                {
                    retry;
                }
            }
            catch(Exception::UpdateConflict)
            {
                if (appl.ttsLevel() == 0)
                {
                    if (xSession::currentRetryCount() >= Planning_Constants::MaxOCCRetryCount)
                    {
                        throw Exception::UpdateConflictNotRecovered;
                    }
                    else
                    {
                        retry;
                    }
                }
                else
                {
                    throw Exception::UpdateConflict;
                }
            }
        }
    }

}

Controller class

/// <summary>
/// The controller for handling bulk Transfer order proposal creation.
/// </summary>
class TransferOrderCreateProposalController  extends SysOperationServiceController
{
    /// <summary>
    /// Constructs a new instance of the <c>TransferOrderCreateProposalController</c>.
    /// </summary>
    /// <param name = "_args">A set of arguments that is used for Transfer order proposal creation.</param>
    /// <returns>A new instance of the <c>TransferOrderCreateProposalController</c>.</returns>
    public static TransferOrderCreateProposalController construct(Args _args)
    {
        TransferOrderCreateProposalController  controller;

        switch (_args.menuItemName())
        {
            case menuItemActionStr(TransferOrderEditProposal):
            case menuItemActionStr(TransferOrderCreateProposal):
                controller = new TransferOrderCreateProposalController(classStr( TransferOrderCreateProposalService ),
                                                                          methodStr( TransferOrderCreateProposalService, createTransferOrderProposal ),
                                                                          SysOperationExecutionMode::Synchronous);
                break;

            case menuItemActionStr(TransferOrderRecalculateQty):
                controller = new TransferOrderCreateProposalController(classStr( TransferOrderCreateProposalService ),
                                                                          methodStr( TransferOrderCreateProposalService, recalculateQuanties ),
                                                                          SysOperationExecutionMode::Synchronous);
                break;

            default:
                break;
        }

        controller.parmArgs( _args );

        return controller;
    }

    public static void main(Args _args)
    {
        TransferOrderCreateProposalController  controller = TransferOrderCreateProposalController::construct( _args );

        controller.startOperation();
        
        //Only run if in Non batch mode
        if (controller.startResult == SysOperationStartResult::Started)
        {
            controller.RefreshCallingForm(_args);
        }
    }

    /// <summary>
    /// Refresh TransferOrderProposal from when not running in batch
    /// </summary>
    /// <param name = "_args"></param>
    private void RefreshCallingForm(args _args)
    {
        if (_args && _args.caller() && _args.caller() is formRun)
        {
            TransferOrderCreateProposalContract contract = this.getDataContractObject();

            FormStringControl proposalModel;

            FormRun callerFormRun = _args.caller() as formRun;

            proposalModel = callerFormRun.design().controlName(formControlStr(TransferOrderProposal, ProposalModel));

            proposalModel.Text(contract.parmProposalModel());
            proposalModel.modified();

            callerFormRun.dataSource(1).research(true);
            callerFormRun.dataSource(1).refresh();

            callerFormRun.dataSource(2).research(true);
            callerFormRun.dataSource(2).refresh();
        }
    }

    protected boolean canRunInNewSession()
    {
        return true;
    }

}

Contract class

/// <summary>
/// The data contract for Transfer order creation.
/// </summary>
[
    DataContract,
    SysOperationGroup("HistoricalHorizoGroup","@:HistoricalHorizon","1",FormArrangeMethod::Vertical),
    SysOperationGroup("ReplaceCurrentProposalGroup","@:ReplaceCurrentProposal","2",FormArrangeMethod::Vertical),
    SysOperationContractProcessing(classStr(TransferOrderCreateProposalUIBuilder))
]
public class TransferOrderCreateProposalContract implements SysOperationInitializable, SysOperationValidatable
{
    Query                   query;
    str                     packedQuery;
    TransDate               fromDate,toDate;
    NoYesId                 replaceCurrentProposal;
    NoYesId                 recalculateQuantities;

    InventSiteId            inventSiteId;
    InventLocationId        inventLocationIdFrom;
    Integer                 timeFence;
    ProposalModelId      proposalModel;

    List                    inventLocationIdTo;
    
    /// <summary>
    /// Indicates whether the contract is valid.
    /// </summary>
    /// <returns>
    /// A Boolean value that indicates whether the contract is valid.
    /// </returns>
    public boolean validate()
    {
        boolean isValid = true;        

        if (!proposalModel)
        {
            isValid = checkFailed(strFmt("@SYS135209", "@:ProposalModel"));
        }
        else if (!this.confirmReplaceCurrentProposal(proposalModel, replaceCurrentProposal) && !recalculateQuantities)
        {
            isValid = false;
        }

        if (!replaceCurrentProposal && !recalculateQuantities)
        {
            isValid = this.checkProposalExists();
        }

        return isValid;
    }

    /// <summary>
    /// check for transfer order proposals exists
    /// </summary>
    /// <returns>A Boolean value that indicates whether the contract is valid</returns>
    public boolean checkProposalExists()
    {
        Query                       queryVal;
        Counter                     lineNum;
        container                   results;
        Enumerator                  enumerator;
        InventTable                 inventTable;
        
        boolean                     isValid =  true;

        if (query == null && packedQuery)
        {
            queryVal = new Query( SysOperationHelper::base64Decode( packedQuery ) );
        }
        else
        {
            queryVal = query;
        }

        QueryRun queryRun   = new QueryRun(queryVal);

        while (queryRun.next())
        {
            inventTable =   queryRun.get(tablenum(InventTable));
            enumerator  =   inventLocationIdTo.getEnumerator();

            while (enumerator.moveNext())
            {
                if (TransferOrderProposal::exist(inventTable.ItemId, enumerator.current(), proposalModel))
                {
                    lineNum++;

                    results += strFmt("@:ProposalExistsValidation", inventTable.ItemId, enumerator.current());

                    if (lineNum >= 100)
                    {
                        break;
                    }
                }
            }
        }

        if (conLen(results) > 0)
        {
            warning(con2Str(results));

            DialogButton    diagBut;

            str strMessage  = "@:TransferOrderAddlinesConfirmation";
            str strTitle    = "@ApplicationPlatform:SystemNotificationTitle";

            diagBut = Box::okCancel(strMessage,
                                    DialogButton::Cancel, // Initial focus is on the Cancel button.
                                    strTitle);

            if (diagBut == DialogButton::Cancel)
            {
                isValid = false;
            }
            else
            {
                isValid = true;
            }
        }

        return isValid;
    }

    /// <summary>
    /// confirm from user to replace Marked/modifeied transfer order proposals
    /// </summary>
    /// <param name = "_proposalModel">user selected value</param>
    /// <param name = "_replaceCurrentProposal">true</param>
    /// <returns>A Boolean value that indicates whether the contract is valid </returns>
    public boolean confirmReplaceCurrentProposal(ProposalModelId _proposalModel, boolean _replaceCurrentProposal)
    {
        boolean ret = true;
        
        if(_proposalModel && _replaceCurrentProposal)
        {
            TransferOrderProposal    transferOrderProposal;

            select firstonly RecId from transferOrderProposal
                    where transferOrderProposal.ProposalModel   == _proposalModel
                        && (transferOrderProposal.AddToShipment == NoYes::Yes
                            || transferOrderProposal.CaseQty != 0);

            if (transferOrderProposal.RecId)
            {
                DialogButton    diagBut;

                str strMessage  = "@:PropsalReplaceConfirmationText";
                str strTitle    = "@ApplicationPlatform:SystemNotificationTitle";

                diagBut = Box::okCancel(strMessage,
                                        DialogButton::Cancel, // Initial focus is on the Cancel button.
                                        strTitle);

                if (diagBut == DialogButton::Cancel)
                {
                    ret = false;
                }
                else
                {
                    ret = true;
                }
            }
        }
        else
        {
            ret = true;
        }

        return ret;
    }

    /// <summary>
    /// Initailizes the default values to parameters.
    /// </summary>
    public void initialize()
    {
        query = new Query( queryStr( InventTableQuery ) );
    }

    /// <summary>
    /// load query from syslast value
    /// </summary>
    /// <param name = "_packedQuery"></param>
    /// <returns> packed query string</returns>
    [
         DataMember,
         AifQueryTypeAttribute('_packedQuery', queryStr( InventTableQuery ) )
    ]
    public str parmPackedQuery( str _packedQuery = packedQuery)
    {
        packedQuery = _packedQuery;

        return packedQuery;
    }

    /// <summary>
    /// Encode/decode the query and set or get
    /// </summary>
    /// <param name = "_query"></param>
    /// <returns>query object</returns>
    public Query parmQuery( Query _query = query )
    {
        if ( prmisDefault( _query ) && query == null )
        {
            query = new Query( SysOperationHelper::base64Decode( packedQuery ) );
        }
        else
        {
            packedQuery  = SysOperationHelper::base64Encode( _query.pack() );
            query = _query;
        }

        return query;
    }

    /// <summary>
    /// Method used to specify the data memmber attribute
    /// </summary>
    /// <param>
    /// <name>parmFromDate</name>is used to set and get the FromDate
    /// </param>
    [
         DataMember,
         SysOperationGroupMember("HistoricalHorizoGroup"),
         SysOperationDisplayOrder("1"),
         SysOperationLabelAttribute(literalstr("@:TransferProposalModel"))
    ]
    public ProposalModelId parmProposalModel( ProposalModelId _proposalModel = proposalModel )
    {
        proposalModel  =   _proposalModel;

        return proposalModel;
    }

    /// <summary>
    /// Method used to specify the data member attribute
    /// </summary>
    /// <param>
    /// <name>parmTimeFence</name>is used to set and get the historical time
    /// </param>
    [
         DataMember,
         SysOperationGroupMember("HistoricalHorizoGroup"),
         SysOperationDisplayOrder("2"),
         SysOperationLabelAttribute(literalstr("@SYS315566"))
    ]
    public Integer parmTimeFence(Integer _timeFence = timeFence  )
    {
        timeFence  =   _timeFence;

        return timeFence;
    }

    /// <summary>
    /// Method used to specify the data member attribute
    /// </summary>
    /// <param>
    /// <name>parmToDate</name>is used to set and get the ToDate
    /// </param>
    [
         DataMember,
         SysOperationGroupMember("HistoricalHorizoGroup"),
         SysOperationDisplayOrder("3"),
         SysOperationLabelAttribute(literalstr("@:ToDate"))
    ]
    public TransDate parmToDate( TransDate _toDate = toDate )
    {
        toDate  =   _toDate;

        return toDate;
    }

    /// <summary>
    /// Method used to specify the data member attribute
    /// </summary>
    /// <param>
    /// <name>parmReplaceCurrentProposal</name>is used to set and get the parameter value
    /// </param>
    [
         DataMember,
         SysOperationGroupMember("ReplaceCurrentProposalGroup"),
         SysOperationDisplayOrder("4"),  
         SysOperationLabelAttribute(literalstr("@:ReplaceCurrentProposal"))
    ]
    public NoYesId parmReplaceCurrentProposal( NoYesId _replaceCurrentProposal = replaceCurrentProposal )
    {
        replaceCurrentProposal  =   _replaceCurrentProposal;

        return replaceCurrentProposal;
    }

    /// <summary>
    /// Method used to specify the data memmber attribute
    /// </summary>
    /// <param>
    /// <name>parmToDate</name>is used to set and get the ToDate
    /// </param>
    [
         DataMember,
         SysOperationGroupMember("ReplaceCurrentProposalGroup"),
         SysOperationDisplayOrder("5"),
         SysOperationLabelAttribute(literalstr("@SYS106955"))
    ]
    public InventSiteId parmInventSiteId( InventSiteId _inventSiteId = inventSiteId )
    {
        inventSiteId  =   _inventSiteId;

        return inventSiteId;
    }

    /// <summary>
    /// Method used to specify the data memmber attribute
    /// </summary>
    /// <param>
    /// <name>parmToDate</name>is used to set and get the ToDate
    /// </param>
    [
         DataMember,
         SysOperationGroupMember("ReplaceCurrentProposalGroup"),
         SysOperationDisplayOrder("6"),
         SysOperationLabelAttribute(literalstr("@SYS25253"))
    ]
    public InventLocationId parmInventLocationIdFrom( InventLocationId _inventLocationIdFrom = inventLocationIdFrom )
    {
        inventLocationIdFrom  =   _inventLocationIdFrom;

        return inventLocationIdFrom;
    }

    /// <summary>
    /// Method used to specify the data memmber attribute
    /// </summary>
    /// <param>
    /// <name>parmToDate</name>is used to set and get the ToDate
    /// </param>
    [
         DataMember,
         SysOperationGroupMember("ReplaceCurrentProposalGroup"),
         SysOperationDisplayOrder("7"),
         SysOperationLabelAttribute(literalstr("@SYS8519")),
         AifCollectionType('return', Types::String)
    ]
    public List parmInventLocationIdTo( List _inventLocationIdTo = inventLocationIdTo )
    {
        inventLocationIdTo  =   _inventLocationIdTo;

        return inventLocationIdTo;
    }

    /// <summary>
    /// Method used to specify the data memmber attribute
    /// </summary>
    /// <param>
    /// <name>parmReplaceCurrentProposal</name>is used to set and get the parameter value
    /// </param>
    [
         DataMember,
         SysOperationGroupMember("ReplaceCurrentProposalGroup"),
         SysOperationDisplayOrder("8"),
         SysOperationLabelAttribute(literalstr("@SYS40695"))
    ]
    public NoYesId parmRecalculateQuantities( NoYesId _recalculateQuantities = recalculateQuantities )
    {
        recalculateQuantities  =   _recalculateQuantities;

        return recalculateQuantities;
    }

}

pamMethod visbility contract class : SysOperationControlVisibilityAttribute

/// <summary>
/// Data contract class for <c>AssetAcquisition</c> SSRS report.
/// </summary>
/// <remarks>
/// This is the Data Contract class for the <c>AssetAcquisition</c> SSRS Report.
/// </remarks>
[DataContractAttribute]
public class AssetAcquisitionContract
{
    boolean showReportingCurrency = false;

    /// <summary>
    /// Hidden parameter for reporting currency column visibility.
    /// </summary>
    /// <param name = "_showReportingCurrency">
    /// boolean value.
    /// </param>
    /// <returns>
    /// Returns True if the current ledger's reporting currency is not blank and it is not same as accounting currency.
    /// </returns>
    [
        DataMemberAttribute, SysOperationControlVisibilityAttribute(false)
    ]
    public boolean parmShowReportingCurrency(boolean _showReportingCurrency = showReportingCurrency)
    {
        if (Ledger::reportingCurrency() && Ledger::accountingCurrency() != Ledger::reportingCurrency())
        {
            showReportingCurrency = true;
        }
        else
        {
            showReportingCurrency = _showReportingCurrency;
        }

        return showReportingCurrency;
    }

}

Sunday, April 11, 2021

Add new fields in sysoperation dialog and get the values in controller class in AX

UIBuilder class

[ExtensionOf(classstr(ExchangeRateImportUIBuilder))]
final class PDPExchangeRateImportUIBuilder_Extension
{
    public FormBuildIntControl      intCtrl;
    public FormBuildCheckBoxControl booleanCtrl;

    public void build()
    {
        next build();

        DialogField                 dialogField;
        DialogField                 dialogFieldNoYesId;
        ExchangeRateImportRequest   dataContract1;
        FormBuildCheckBoxControl    exchangeRateFromPreviousDayControl;

        dataContract1    = this.dataContractObject();

       //  creating this field if standared code skips it to avoid type cast error in contoller at runtime
        if(exchangeRateFromPreviousDayControl == null)
        {
            dialogField = dialog.addField(extendedtypestr(ExchangeRateFromPreviousDay));
            exchangeRateFromPreviousDayControl = dialogField.control();
            exchangeRateFromPreviousDayControl.value(dataContract1.parmExchangeRateFromPreviousDay());
            exchangeRateFromPreviousDayControl.allowEdit(false);
            exchangeRateFromPreviousDayControl.visible(false);
        }

        dialogField     = dialog.addField(extendedtypestr(NumberOf),"integer","integer");
        intCtrl         = dialogField.control();
        intCtrl.value(dataContract1.parmInteg());

        dialogField     = dialog.addField(extendedtypestr(NoYesId),"boolean","boolean");
        booleanCtrl     = dialogField.control();
        booleanCtrl.value(dataContract1.parmNoYesId());
    }

}

Contract class

[ExtensionOf(ClassStr(ExchangeRateImportRequest))]
[DataContractAttribute]
final class PDPExchangeRateImportRequest_Extension
{
    public int     integ;
    public NoYesId noYesValue;

    /// <summary>
    /// data memeber attribute of Integ field
    /// </summary>
    /// <param name = "_integ">integ</param>
    /// <returns>int</returns>
    [DataMemberAttribute]
    public int parmInteg(int _integ = 0)
    {
        if (!_integ)
        {
            integ = _integ;
        }

        return integ;
    }

    /// <summary>
    /// NoyesId field
    /// </summary>
    /// <param name = "_noYesValue">NoYesValue</param>
    /// <returns>boolean</returns>
    [DataMemberAttribute]
    public NoYesId parmNoYesId(NoYesId _noYesValue = NoYes::No)
    {
        if (!_noYesValue)
        {
            noYesValue = _noYesValue;
        }

        return _noYesValue;
    }

}

Controller class

[ExtensionOf(ClassStr(ExchangeRateImportController))]
final class PDPExchangeRateImportController_Extension
{
    public const str numberof = 'Fld11_1';
    public const str noYesValue = 'Fld12_1';


    public void getFromDialog()
    {
        next getFromDialog();

        FormRun                             theDialogForm;
        ExchangeRateImportRequest           exchangeRateImportRequest;
        ExchangeRateProviderFactory         factory;

        FormIntControl intCtrl;
        FormCheckBoxControl booleanCtrl;
        FormCheckBoxControl exchangeRateFromPreviousDayControl;
       
        exchangeRateImportRequest = this.getDataContractObject(classStr(ExchangeRateImportRequest));
        theDialogForm = this.dialog().formRun();

        intCtrl = theDialogForm.control(theDialogForm.controlId(numberof));

        booleanCtrl = theDialogForm.control(theDialogForm.controlId(noYesValue));

        exchangeRateImportRequest.parmInteg(intCtrl.value());

        exchangeRateImportRequest.parmExchangeRateTypeRecId(booleanCtrl.value());

        Info(strFmt("%1--%2",intCtrl.value(), booleanCtrl.value()));

    }

}

Table browser URL in D365FO

Critical Thinking icon icon by Icons8