Showing posts with label AX 7. Show all posts
Showing posts with label AX 7. Show all posts

Wednesday, March 4, 2026

Error readable from string in D365FO

 Reference class: RetailSyncOrdersSchedulerTask

Error readable:

private str generateHumanReadableErrorDetail(System.Exception _exception, str _errorDetail = '')

{

    str errorDetail, exceptionType, stackTrace, exceptionMessage;


    errorDetail = _errorDetail;

    // When the CLR error encounters it gets the inner exception message, call stack and stores in Database.

    if (_exception && _exception.InnerException)

    {

        if (_exception.InnerException.InnerException && _exception.InnerException.StackTrace)

        {

            // Use inner exception if it is available as it is often more descriptive.

            errorDetail = errorDetail ? errorDetail +

                        _exception.InnerException.InnerException.Message : _exception.InnerException.InnerException.Message +

                         _exception.InnerException.StackTrace.ToString();

        }

        else if (_exception.InnerException.StackTrace)

        {

            errorDetail = _exception.InnerException.Message +  _exception.InnerException.StackTrace.ToString();

        }

        else

        {

            // When the exeption not returns a call stack, we get the call stack from the <c>Xsession<c> object.

            errorDetail = _exception.InnerException.Message + con2str(xSession::xppCallStack());

        }

    }


    return errorDetail;

}

Tuesday, December 2, 2025

Override custom dimension value on posting sales invoice in X++

 Requirement:

1. Override custom dimension value on posting sales invoice

2.  Ledger posting type as "Cost of goods, invoiced" & "Sales revenue"

3. Reason: Requirement is to default for certain orders type, these account has to be changed. 

4. Why can't we acheive in Customer or item level dimension. [Reason: Currently it was managed in item level, but requirement is to for same item if order type met certain criteria on posting, this need to defaulted with this dimension value for ledger reporting]


Class 1:

 

/// <summary>

///    The <c>LedgerVoucherTransObject</c> class represents a single transaction in an individual voucher. COC

/// </summary>

/// <remarks>

///    The transaction is stored in a temporary instance of a <see cref="T:LedgerTrans" /> record buffer.

///    The temporary transaction is inserted into the database during posting and made a permanent record.

/// </remarks>

[ExtensionOf(ClassStr(LedgerVoucherTransObject))]

final class LedgerVoucherTransObjectCls_SAN_Extension

{

    /// <summary>

    ///    Initializes a new instance of the <c>LedgerVoucherTransObject</c> class by using a transaction COC POL

    ///    currency amount and a ledger posting reference for defaulting.

    /// </summary>

    /// <param name="_defaultLedgerPostingReference">

    ///    The ledger posting reference to use for defaulting.

    /// </param>

    /// <param name="_postingType">

    ///    The posting type of the general journal entry.

    /// </param>

    /// <param name="_ledgerDimensionId">

    ///    The dimension attribute value combination of the general journal entry.

    /// </param>

    /// <param name="_transactionCurrencyCode">

    ///    The currency code of the general journal entry.

    /// </param>

    /// <param name="_transactionCurrencyAmount">

    ///    The amount in the transaction currency.

    /// </param>

    /// <param name="_exchangeRateHelper">

    ///    The accounting currency amount and secondary currency amount exchange rates.

    /// </param>

    /// <returns>

    ///    A new instance of the <c>LedgerVoucherTransObject</c> class.

    /// </returns>

    /// <remarks>

    ///    The default ledger posting reference is used to set the transaction type and exchange rate date.

    /// </remarks>

    public static LedgerVoucherTransObject newTransactionAmountDefault(

        LedgerVoucherObject _defaultLedgerPostingReference,

        LedgerPostingType _postingType,

        LedgerDimensionAccount _ledgerDimensionId,

        CurrencyCode _transactionCurrencyCode,

        Money _transactionCurrencyAmount,

        CurrencyExchangeHelper _exchangeRateHelper)

    {

        LedgerVoucherTransObject polpostingTrans;

        LedgerDimensionAccount  ledgerDimensionAccount, tempLedgerDimenionAcc;

        ledgerDimensionAccount = _ledgerDimensionId;

 

        if(ledgerDimensionAccount && (_postingType == LedgerPostingType::SalesConsump

            || _postingType == LedgerPostingType::SalesRevenue))

        {

            SAN_SalesInvoiceDimensionMockContext contextCur = SAN_SalesInvoiceDimensionMockContext::current();

            if(contextCur != null)

            {

                if(contextCur.isParameterActive && contextCur.isValidToOverride && contextCur.polproductLineToOverride)

                {

                    tempLedgerDimenionAcc = _ledgerDimensionId;

 

                    ledgerDimensionAccount = SalesInvoiceJournalPost::SAN_buildDefaultAndLedgerDimension(tempLedgerDimenionAcc,contextCur.polproductLineToOverride);

                }

            }

        }

 

        polpostingTrans = next newTransactionAmountDefault(_defaultLedgerPostingReference, _postingType, ledgerDimensionAccount, _transactionCurrencyCode, _transactionCurrencyAmount, _exchangeRateHelper);

 

        return polpostingTrans;

    }

 

}

 

 

Class 2:

/// <summary>

/// To hold the value of SAN_SalesInvoiceDimensionMockContext through out the runtime.

/// </summary>

class SAN_SalesInvoiceDimensionMockContext implements System.IDisposable

{

    public  boolean  isParameterActive;

    public  boolean  isValidToOverride;

    public  SalesTable      polsalesTable;

    public  str             polsmmSegmentId;

    public  str             polproductLineToOverride;

    static  SAN_SalesInvoiceDimensionMockContext   instance;

 

    protected void new ()

    {

        if (instance)

        {

            throw Error('Nesting of SAN_SalesInvoiceDimensionMockContext is not supported');

        }

 

        instance = this;

    }

 

    /// <summary>

    ///  To Create the instance wherever the method is called during the run time.

    /// </summary>

    /// <returns>current new instance</returns>

    Public static SAN_SalesInvoiceDimensionMockContext createInstance()

    {

        SAN_SalesInvoiceDimensionMockContext newInstance = new SAN_SalesInvoiceDimensionMockContext();

        instance = newInstance;

 

        return instance;

    }

 

    /// <summary>

    /// Dispose the instance once the Aging snapshot batch is run.

    /// </summary>

    public void dispose()

    {

        instance = null;

    }

 

    /// <summary>

    ///  To get the instance wherever the method is called during the run time.

    /// </summary>

    /// <returns>Current instance</returns>

    public static SAN_SalesInvoiceDimensionMockContext current()

    {

        return instance;

    }

 

}

 

 

Class 3:

/// <summary>

/// Extension of class SalesInvoiceJournalPost

/// </summary>

[Extensionof (classstr(SalesInvoiceJournalPost))]

Public Final class SalesInvoiceJournalPostCls_SAN_Extension

{

    /// <summary>

    ///     Posts to inventory.

    /// </summary>

    protected void postInventory()

    {

        using(SAN_SalesInvoiceDimensionMockContext contextSet = SAN_SalesInvoiceDimensionMockContext::createInstance())

        {

            if(SalesParameters::find().SAN_OverrideDimensionValue)

            {

                contextSet.isParameterActive = true; 

                SalesTable      salesTable;

                salesTable =  salesLine.SalesTable();

                if(salesTable && salesLine)

                {

                     contextSet.polsalesTable = salesTable;

                        contextSet.isValidToOverride = true;

                        contextSet.polproductLineToOverride = '09';

                }

            }

            next postInventory();

        }

    }

 

    /// <summary>

    ///     Posts one journal line.

    /// </summary>

    protected void postLine()

    {

        using(SAN_SalesInvoiceDimensionMockContext contextSet = SAN_SalesInvoiceDimensionMockContext::createInstance())

        {

           if(SalesParameters::find().SAN_OverrideDimensionValue)

            {

                contextSet.isParameterActive = true;

 

                SalesTable      salesTable;

                salesTable = salesParmLine.SalesLine().SalesTable();

                if(salesTable && salesParmLine  )

                {

                   contextSet.polsalesTable = salesTable;

                        contextSet.isValidToOverride = true;

                        contextSet.polproductLineToOverride = '09';

                }

            }            

            next postLine();

        }

    }

 

    /// <summary>

    /// buildDefaultAndLedgerDimension for govt and non-govt segment

    /// </summary>

    /// <param name = "_ledger">LedgerDimensionAccount</param>

    /// <param name = "_overridePL">overridePL</param>

    /// <returns>ledgerDimensionAccount</returns>

    Static ledgerDimensionAccount  SAN_buildDefaultAndLedgerDimension(LedgerDimensionAccount   _ledger, str _overridePL)

    {

        DimensionAttributeValueSetStorage   dimensionAttributeValueSetStorage;

        DimensionAttributeValue             dimensionAttributeValue;

        DimensionDefault                    dimensionDefault;

        LedgerDimensionAccount              ledgerDimensionAccount;

        DimensionAttributeLevelValueAllView dimAttrValueallview;

        dimensionAttributeValueSetStorage = new DimensionAttributeValueSetStorage();

        RefRecId            mainAccountRecId, LedgerDimensionAcc;

        mainAccountRecId = LedgerDimensionFacade::getMainAccountRecIdFromLedgerDimension(_ledger);

        LedgerDimensionAcc = LedgerDefaultAccountHelper::getDefaultAccountFromMainAccountRecId(mainAccountRecId);

 

        while select dimAttrValueallview 

            where dimAttrValueallview.ValueCombinationRecId == _ledger

        {

            if(DimensionAttribute::find(dimAttrValueallview.DimensionAttribute).Name != "MainAccount")

            {

                if(DimensionAttribute::find(dimAttrValueallview.DimensionAttribute).Name == "ProductLine")

                {

                    dimensionAttributeValue = DimensionAttributeValue::findByDimensionAttributeAndValue(

                                    DimensionAttribute::find(dimAttrValueallview.DimensionAttribute),

                                    _overridePL, false, true);

                }

                else

                {

                    dimensionAttributeValue = DimensionAttributeValue::findByDimensionAttributeAndValue(

                                    DimensionAttribute::find(dimAttrValueallview.DimensionAttribute),

                                    dimAttrValueallview.DisplayValue, false, true);

                }

                dimensionAttributeValueSetStorage.addItem(dimensionAttributeValue);

                dimensionDefault = dimensionAttributeValueSetStorage.save();

            }

        }

        ledgerDimensionAccount = LedgerDimensionFacade::serviceCreateLedgerDimension(LedgerDimensionAcc,dimensionDefault);

        return ledgerDimensionAccount;

    }

 

}

Tuesday, July 1, 2025

Set visibility of menu item based on Feature or parameter options in D365FO

 [SubscribesTo(classstr(SysMenuNavigationObjectFactory), staticdelegatestr(SysMenuNavigationObjectFactory, checkAddSubMenuDelegate))]

public static void menuItemVisibilityHandler(SysDictMenu _rootMenu, SysDictMenu _subMenu, SysBoxedBoolean _subMenuVisibility)

{

    if (_subMenu.isMenuItem())

    {

        var metadataElement = _subMenu.GetMenuItemMetaElement();

        if (metadataElement != null)

        {

            SAN_HelperCls::santst_setVisibilityMenuItems(metadataElement.Name, _subMenuVisibility);

        }

    }

}


public class  SAN_HelperCls
{

private static void santst_setVisibilityMenuItems(str _menuItemName, SysBoxedBoolean _subMenuVisibility)

{

    const str menuItemsPrefix = "SAN_TEST";


    if (strScan(_menuItemName, menuItemsPrefix , 0, strLen(_menuItemName)))

    {

        _subMenuVisibility.value = SAN_HelperCls::isEnabled();

    }

}


 public static boolean isEnabled()

 {

     return isConfigurationkeyEnabled(configurationkeynum(Trade)) && 

             FeatureStateProvider::isFeatureEnabled(SAN_DevFeature::instance());

 }

}


Parameter control (Yes/No)

or

Feature class:

using System.ComponentModel.Composition;

using Microsoft.Dynamics.ApplicationPlatform.FeatureExposure;


/// <summary>

/// The <c>SAN_DevFeature</c> class defines the enable advanced order hold feature.

/// </summary>

[ExportAttribute(identifierstr(Microsoft.Dynamics.ApplicationPlatform.FeatureExposure.IFeatureMetadata))]

[Microsoft.Dynamics.BusinessPlatform.SharedTypes.InternalUseOnlyAttribute]

public final class SAN_DevFeature implements IFeatureMetadata, IFeatureLifecycle

{

    private static SAN_DevFeature instance;


    private void new()

    {

    }


    [Hookable(false)]

    public WebSiteURL learnMoreUrl()

    {

        return 'https://learn.microsoft.com/en-us/';

    }


    private static void TypeNew()

    {

        instance = new SAN_DevFeature();

    }


    /// <summary>

    /// Obtains the singleton object instance.

    /// </summary>

    /// <returns>The <c>SAN_DevFeature</c> instance.</returns>

    [Hookable(false)]

    public static SAN_DevFeature instance()

    {

        return SAN_DevFeature::instance;

    }


    [Hookable(false)]

    public FeatureLabelId label()

    {

        return literalStr("SAN DEV tool features");

    }


    [Hookable(false)]

    public int module()

    {

        return FeatureModuleV0::SalesAndMarketing;

    }


    [Hookable(false)]

    public FeatureLabelId summary()

    {

        return literalStr("SAN DEV tool features");

    }


    [Hookable(false)]

    public boolean isEnabledByDefault()

    {

        return false;

    }


    [Hookable(false)]

    public boolean canDisable()

    {

        return true;

    }


    [Hookable(false)]

    public FeatureLifecycleStage FeatureStage()

    {

        return FeatureLifecycleStage::Released;

    }


}

Tuesday, June 24, 2025

Getting multiple row data into single SQL column

Table data:

Id | Text | Description

1 | Test1 | Test description

1 | Test2 | Test description

1 | Test3 | Test description

2 | Test2 | Test description

3 | Test3 | Test description

Expected result: System should return "Test1;Test2;Test3" for data related to Id = '1'

Query (T-SQL)


Sample 1:

SELECT  t.LegalEntity, t.ITEMNUMBER,

       replace(STUFF((SELECT '; ' + CAST(t1.Text AS VARCHAR(255)) [text()]

         FROM TESTTABLE t1

         WHERE t1.ID = t.ITEMNUMBER and t1.LEGALENTITY = t.LEGALENTITY

         FOR XML PATH(''), TYPE)

        .value('.','NVARCHAR(255)'),1,2,''),' ','') AS ComputeDescription

FROM InventTable  t

where  t.ITEMNUMBER in ('1') and t.LEGALENTITY = 'usmf'

group by t.LegalEntity, t.ITEMNUMBER


Sample 2:

SELECT it.ITEMLE, it.ITEMNUMBER,

CAST (ISNULL((SELECT STRING_AGG(t1.Text,';') AS ProductLine 

 FROM TESTTABLE t1

where t1.ID = it.ITEMNUMBER and t1.LEGALENTITY = it.DataAreaId),'') as NVARCHAR(255)) AS ComputeDescription

from InventTable it

where it.DataAreaId = 'chhq'

and it.ITEMNUMBER in ('0000505','0000511','0000497','0000499')

Wednesday, May 14, 2025

Upload file CSV/TXT using SysOperation in D365 X++

Upload CSV/TXT using SysOperation X++


Contract:
/// <summary>
/// Contract class upload through excel for PO inbound journals
/// </summary>
[DataContractAttribute,
    SysOperationContractProcessingAttribute(classStr(SAN_POImportUIBuilder))]
public class SAN_POImportContract extends SysOperationDataContractBase 
                    Implements SysOperationInitializable, SysOperationValidatable
{
    FileName                    fileName;
    private System.IO.Stream    inputDataStream;
    NoYes                       hasColumnNames;
    Str1260                     layout;

    /// <summary>
    /// Validates the parameters.
    /// </summary>
    /// <returns>
    /// true if successful; otherwise, false.
    /// </returns>
    public boolean validate()
    {
        return true;
    }

    public void initialize()
    {
        hasColumnNames = NoYes::Yes;
    }

    /// <summary>
    /// Gets or sets the value of the data contract parameter fileName.
    /// </summary>
    /// <param name="_fileName">
    /// The new value of the data contract parameter fileName.
    /// </param>
    /// <returns>
    /// Returns the current value of data contract parameter fileName.
    /// </returns>
    public Filename parmFileName(FileName _fileName = fileName)
    {
        fileName = _fileName;
        return fileName;
    }

    /// <summary
    /// Gets or sets the value of the data contract parameter hasColumnNames.
    /// </summary>
    /// <param name = "_hasColumnNames">
    /// The new value of the data contract parameter hasColumnNames.
    /// </param>
    /// <returns>
    /// Returns the current value of data contract parameter hasColumnNames.
    /// </returns>
    [DataMemberAttribute,
        SysOperationLabelAttribute("File has header column") ]
    public NoYes parmHasColumnNames(NoYes _hasColumnNames = hasColumnNames)
    {
        hasColumnNames = _hasColumnNames;
        return hasColumnNames;
    }

    /// <summary>
    /// Set or get Input file data stream
    /// </summary>
    /// <param name = "_value">current value of stream</param>
    /// <returns>file memory stream to process</returns>
    [Hookable(false)]
    public System.IO.Stream parmInputDataStream(System.IO.Stream _value = inputDataStream)
    {
        inputDataStream = _value;
        return inputDataStream;
    }

    /// <summary>
    /// Gets or sets the value of the data contract parameter fileLayout.
    /// </summary>
    /// <param name="_layout">
    /// The new value of the data contract parameter fileLayout.
    /// </param>
    /// <returns>
    /// Returns the current value of data contract parameter fileLayout.
    /// </returns>
    [DataMemberAttribute,
        SysOperationLabelAttribute("File layout") ]
    public Str1260 parmLayout(Str1260 _layout = layout)
    {
        layout =  '';
        layout =  'TXT/CSV File delimited as |' + '\n';

        layout += strfmt("%1 | %2 : %3\n", '1', 'A', 'Column1');
        layout += strfmt("%1 | %2 : %3\n", '2', 'B', 'Column2');
        layout += strfmt("%1 | %2 : %3\n", '3', 'C', 'Column3');
        layout += strfmt("%1 | %2 : %3\n", '4', 'D', 'Column4');
        layout += strfmt("%1 | %2 : %3\n", '5', 'E', 'Column5');


        return layout;
    }

}

UI Builder:
/// <summary>
/// Class is to upload through excel for PO inbound substitution journals to handle run time contract operations
/// </summary>
class SAN_POImportUIBuilder extends SysOperationAutomaticUIBuilder
{

    #define.commandButton('CommandButton')
    #define.fileUpload('FileUpload')
    #define.fileTypesAccepted('.txt,.csv')
    
    SAN_POImportContract   contract;

    /// <summary>
    /// Adds a button to the dialog to import a file.
    /// </summary>
    [Hookable(false)]
    public void build()
    {
        DialogGroup             dialogGroup;
        DialogTabPage           dialogTabPage;
        Dialog                  dlg;
        FormBuildControl        formBuildControl;
        FileUploadBuild         dialogFileUpload;
        DialogField             dialogLayout;
        FormBuildTabPageControl parametersTabPage;
        FormBuildTabPageControl layoutTabPage;

        dlg = this.dialog();

        contract = this.dataContractObject() as SAN_POImportContract;

        dialogTabPage = dlg.addTabPage("@SYS7764");
        dialogTabPage.columns(2);
        parametersTabPage = dlg.formBuildDesign().control(dialogTabPage.name());
        parametersTabPage.fastTabExpanded(FastTabExpanded::Yes);
        dialogGroup = dlg.addGroup('Upload file');
        formBuildControl = dlg.formBuildDesign().control(dialogGroup.name());
        dialogFileUpload = formBuildControl.addControlEx(classstr(FileUpload), #fileUpload);
        dialogFileUpload.style(FileUploadStyle::MinimalWithFilename);
        dialogFileUpload.baseFileUploadStrategyClassName(classstr(FileUploadTemporaryStorageStrategy));
        dialogFileUpload.fileTypesAccepted(#fileTypesAccepted);
        dialogFileUpload.fileNameLabel("@SYS308842");

        dlg.addGroup("@SYS339526");
        this.addDialogField(methodStr(SAN_POImportContract, parmHasColumnNames), contract);

        dialogTabPage = dlg.addTabPage("@Polaris:FileLayout");
        layoutTabPage = dlg.formBuildDesign().control(dialogTabPage.name());
        layoutTabPage.fastTabExpanded(FastTabExpanded::Yes);
        dialogLayout = this.addDialogField(methodStr(SAN_POImportContract, parmLayout), contract);
        dialogLayout.showLabel(false);
        dialogLayout.enabled(false);
        dialogLayout.widthMode(1);
        dialogLayout.displayHeight(15);
    }

    /// <summary>
    /// Handles dialog closing events.
    /// </summary>
    /// <param name = "sender">
    /// xFormRun class.
    /// </param>
    /// <param name = "e">
    /// FormEventArgs class.
    /// </param>
    [SuppressBPWarningAttribute('BPParametersNotUsed', 'This event parameter is not used')]
    private void dialogClosing(xFormRun sender, FormEventArgs e)
    {
        FormEventArgs formEventArgs;

        formEventArgs = e;

        this.dialogEventsUnsubscribe(sender as FormRun);
    }

    /// <summary>
    /// Handles dialog events.
    /// </summary>
    /// <param name = "_formRun">
    /// FormRun class type.
    /// </param>
    private void dialogEventsSubscribe(FormRun _formRun)
    {
        FileUpload fileUpload = _formRun.control(_formRun.controlId(#fileUpload));
        fileUpload.notifyUploadCompleted += eventhandler(this.uploadCompleted);
        fileUpload.notifyUploadAttemptStarted += eventhandler(this.uploadStarted);
        _formRun.onClosing += eventhandler(this.dialogClosing);
    }

    /// <summary>
    /// Handles dialog event.
    /// </summary>
    /// <param name = "_formRun">
    /// FormRun class type.
    /// </param>
    private void dialogEventsUnsubscribe(FormRun _formRun)
    {
        FileUpload fileUpload = _formRun.control(_formRun.controlId(#fileUpload));
        fileUpload.notifyUploadCompleted -= eventhandler(this.uploadCompleted);
        fileUpload.notifyUploadAttemptStarted -= eventhandler(this.uploadStarted);
        _formRun.onClosing -= eventhandler(this.dialogClosing);
    }

    /// <summary>
    /// Handles dialog event post run.
    /// </summary>
    [Hookable(false)]
    public void postRun()
    {
        super();
        FormRun formRun = this.dialog().dialogForm().formRun();
        this.dialogEventsSubscribe(formRun);
        this.setDialogOkButtonEnabled(formRun, false);
    }

    /// <summary>
    /// Enables or disables the dialog Ok button.
    /// </summary>
    /// <param name = "_formRun">
    /// The <c>FormRun</c> object.
    /// </param>
    /// <param name = "_isEnabled">
    /// Indicates to enable or disable the Ok button.
    /// </param>
    protected void setDialogOkButtonEnabled(FormRun _formRun, boolean _isEnabled)
    {
        FormControl okButtonControl = _formRun.control(_formRun.controlId(#commandButton));
        if (okButtonControl)
        {
            okButtonControl.enabled(_isEnabled);
        }
    }

    /// <summary>
    /// After the file has been uploaded, the Ok button is enabled.
    /// </summary>
    protected void uploadCompleted()
    {
        contract = this.dataContractObject() as SAN_POImportContract;
        var formRun = this.dialog().dialogForm().formRun();
        FileUpload fileUpload = formRun.control(formRun.controlId(#fileUpload));

        contract.parmFileName(fileUpload.fileName());
        using (System.IO.Stream stream = fileUpload.getUploadedFile(true))
        {
            if (stream)
            {
                System.IO.MemoryStream copiedStream = new System.IO.MemoryStream();
                stream.CopyTo(copiedStream);
                
                contract.parmInputDataStream(copiedStream);
            }
        }

        this.setDialogOkButtonEnabled(formRun, contract.parmInputDataStream() != null);
    }

    /// <summary>
    /// During file upload, the Ok button is disabled.
    /// </summary>
    private void uploadStarted()
    {
        var formRun = this.dialog().dialogForm().formRun();
        this.setDialogOkButtonEnabled(formRun, false);
    }

}


Controller:
/// <summary>
/// Class is to handle controller logic to upload  PO journals
/// </summary>
class SAN_POImportController extends SysOperationServiceController
{
    public ClassDescription caption()
    {
        return "Upload  Purchase Journals";
    }

    /// <summary>
    /// Provides entry point for the instance of <c>SAN_POImportController</c>.
    /// </summary>
    /// <param name = "_args">
    /// The arguments passed to the class <c>SAN_POImportController</c>.
    /// </param>
    public static void main(Args _args)
    {
        SAN_POImportController controller;

        controller = SAN_POImportController::construct();
        controller.showBatchTab(false);
        controller.parmArgs(_args);
        controller.startOperation();
    }

    /// <summary>
    /// Initializes new instance of <c>SAN_POImportController</c>.
    /// </summary>
    /// <returns>
    /// Return object of <c>SAN_POImportController</c>.
    /// </returns>
    public static SAN_POImportController construct()
    {
        SAN_POImportController   controller;
        
        controller = new SAN_POImportController(classStr(SAN_POImportService),
                                                        methodStr(SAN_POImportService, Run),
                                                        SysOperationExecutionMode::Synchronous);

        return controller;
    }

}

Service:
/// <summary>
/// Class is to upload through excel for PO upload journals to handle service operations
/// </summary>
class SAN_POImportService extends SysOperationServiceBase
{
    System.IO.Stream                        stream;
    int                                     totalLinesProcessed = 0;
    SAN_POImportContract        contract;
    str                             filename, journalProcessId;

    /// <summary>
    /// process file to upload WG purchase line item substitution journals
    /// </summary>
    /// <param name = "_contract">
    /// Contract class.
    /// </param>
    public void run(SAN_POImportContract _contract)
    {
        contract            = _contract;
        stream              = _contract.parmInputDataStream();
        filename            = _contract.parmFileName();
        this.processFile(_contract);
    }

    /// <summary>
    /// Processing excel file input
    /// </summary>
    /// <param name = "_contract">
    /// Contract class.
    /// </param>
    protected void processFile(SAN_POImportContract _contract)
    {
        #File
        TextStreamIo inFile = TextStreamIo::constructForRead(stream);
        inFile.inFieldDelimiter('|'); //TODO delimiter used
        inFile.inRecordDelimiter('\r\n'); 
        container recordContainer;
        str item;
        boolean     isFirstLine = true;
        recordContainer = inFile.read();
        if(_contract.parmHasColumnNames())
        {
            recordContainer = inFile.read();
        }
        
        ttsbegin;
        while (inFile.status() == IO_Status::OK)
        {
            if(isFirstLine)
            {
journalProcessId = '';
                //Header record creation logic
                isFirstLine= false;
            }

           //line record creation logic
            //Read the next line
            recordContainer = inFile.read();
        }
        ttscommit;

        info(strFmt("Journal - %2 Total record lines created %1",totalLinesProcessed, journalProcessId));
    }

}

Wednesday, February 19, 2025

Validate product from Retail Assortment in D365FO X++

Public boolean SAN_validateProductAssortment(Str    _itemId)

{

    RetailAssortmentTable           retailAssortmentTable;

    RetailChannelAssortedProductView  retailChannelAssortedProductView;

    InventTable                       inventTable;

    while select  retailAssortmentTable

    {

            select firstonly retailChannelAssortedProductView

                 exists join inventTable

                where inventTable.ItemId == _itemId

                  && retailChannelAssortedProductView.AssortmentRecId == retailAssortmentTable.RecId

                  && retailChannelAssortedProductView.ProductID == inventTable.Product

                  && retailChannelAssortedProductView.InventLocationDataAreaId == inventTable.DataAreaId;

            if(retailChannelAssortedProductView)

            {

                return true;

            }

        }


    }

    throw Error("Invalid item to sell based on assortments");

} 

Thursday, January 2, 2025

Create call center channel users X++ D365FO

 RetailChannelTable   channelTable;

MCRChannelUser      channelUser;


while select channelTable

    where channelTable.ChannelType ==  RetailChannelType::MCRCallCenter

{

    channelUser.clear();

    changecompany(channelTable.DefaultCustDataAreaId)

    {

        channelUser = MCRChannelUser::find();


        if(!channelUser)

        {

            channelUser.clear();

            channelUser.initValue();

            channelUser.Channel = channelTable.RecId;

            channelUser.User = curUserId();

            channelUser.insert();

        }

    }           

}

Monday, November 4, 2024

Copy Markup charges while posting purchase invoice using X++

 Copy Markup charges while posting purchase invoice using X++


Class:

Important: Code logic is just for Reference. 

New class => Duplicate of class MarkupCopy & Named as SAN_MarkupCopy

/// <summary>

/// Duplicate Copy class of MarkupCopy

/// Due to extension limited capabilities on internal and private implementation from STD OOB


Helper class: To call logic 

/// <summary>

/// Helper class to centralized logic to copy charges from and to table buffer for purchase

/// </summary>

class SAN_GeneratePurchaseMarkupChargesHelper

{

    public static void createPurchaseHeaderMarkupCharge(

                        Common          _fromTable, 

                        Common          _toTable,

                        PurchTable      _purchTable )

    {

        MarkupCopy                          markupCopy;

        MarkupCopyFromPurchOrderParameters  copyFromPurchOrderToSubTableParameters;


        markupCopy = MarkupCopy::construct();

        copyFromPurchOrderToSubTableParameters =

            MarkupCopyFromPurchOrderParameters::createCopyFromPurchOrderParameters(

                _fromTable,

                _toTable,

                '',

                _purchTable.CurrencyCode,

                SourceDocumentLineAccountingStatus::Draft,

                _purchTable,

                false);

        markupCopy.copyFromPurchOrder(copyFromPurchOrderToSubTableParameters);

    }


  public static void createPurchaseLinesMarkupCharge(

                        PurchLine          _purchLine,

                        VendInvoiceInfoLine          _vendInvoiceInfoLine,

                        PurchTable                  _purchTable,

                        VendInvoiceInfoTable    _vendInvoiceInfoTable)

    {

        ttsbegin;

        SAN_MarkupCopy markupCopy;


        markupCopy = SAN_MarkupCopy::construct();

        MarkupCopyFromPurchOrderParameters copyFromPurchOrderToLineParameters;

        

        copyFromPurchOrderToLineParameters =

                    MarkupCopyFromPurchOrderParameters::createCopyFromPurchOrderParameters(

                        _purchLine,

                        _vendInvoiceInfoLine,

                        '',

                        '',

                        SourceDocumentLineAccountingStatus::Draft,

                        null,

                        false);

        SysDaBinaryExpression       sysDaBinaryExpressionSource, sysDaBinaryExpressionDest;

        sysDaBinaryExpressionDest =  new SysDaEqualsExpression(

                new SysDaFieldExpression(_vendInvoiceInfoLine, fieldStr(VendInvoiceInfoLine, ParmId)), new SysDaValueExpression(_vendInvoiceInfoTable.ParmId))

            .and(new SysDaEqualsExpression(

                new SysDaFieldExpression(_vendInvoiceInfoLine, fieldStr(VendInvoiceInfoLine, TableRefId)), new SysDaValueExpression(_vendInvoiceInfoTable.TableRefId)));


        sysDaBinaryExpressionSource =  new SysDaEqualsExpression(

                new SysDaFieldExpression(_purchLine, fieldStr(PurchLine, RecId)), new SysDaFieldExpression(_vendInvoiceInfoLine, fieldStr(VendInvoiceInfoLine, PurchLineRecId)));


        copyFromPurchOrderToLineParameters.parmBufferToWhereClause(sysDaBinaryExpressionDest);

        copyFromPurchOrderToLineParameters.parmBufferFromWhereClause(sysDaBinaryExpressionSource);

        copyFromPurchOrderToLineParameters.parmDocumentStatusToExclude(DocumentStatus::Invoice);



        MarkupTransTmp markUpTransTmp = markupCopy.copyForAllDocumentLinesUsingTempMarkupTrans(copyFromPurchOrderToLineParameters);

        SAN_GeneratePurchaseMarkupChargesHelper::copyMarkupFromPurchOrderOptimizedCleanup(_vendInvoiceInfoTable, markUpTransTmp);


        ttscommit;

    }


    private static void copyMarkupFromPurchOrderOptimizedCleanup(VendInvoiceInfoTable _vendInvoiceInfoTable, MarkupTransTmp _markUpTransTmpCopy)

    {

        MarkupTrans markupTransOtherInvoices;

        VendInvoiceInfoLine vendInvoiceInfoLine;

        MarkupTransMapping markUpTransMapping;

        

        // Find if there is a MarkupTrans that is not marked Keep and it is already connected to another invoice

        // we will only call delete if there exists one

        select firstonly RecId from _markUpTransTmpCopy

        join vendInvoiceInfoLine

            where vendInvoiceInfoLine.ParmId == _vendInvoiceInfoTable.ParmId

                && vendInvoiceInfoLine.TableRefId == _vendInvoiceInfoTable.TableRefId

                && _markUpTransTmpCopy.TransTableId == vendInvoiceInfoLine.TableId

                && _markUpTransTmpCopy.TransRecId == vendInvoiceInfoLine.RecId

                && _markUpTransTmpCopy.SourceDocumentLine == 0

                && !_markUpTransTmpCopy.Keep

        join markupTransOtherInvoices

            where markupTransOtherInvoices.OrigTableId == _markUpTransTmpCopy.OrigTableId

                && markupTransOtherInvoices.OrigRecId == _markUpTransTmpCopy.OrigRecId

                && markupTransOtherInvoices.TransTableId == _markUpTransTmpCopy.TransTableId

                && markupTransOtherInvoices.TransRecId != _markUpTransTmpCopy.TransRecId;


        if (_markUpTransTmpCopy.RecId != 0)

        {

            vendInvoiceInfoLine.clear();

            markupTransOtherInvoices.clear();


            delete_from _markUpTransTmpCopy

            exists join vendInvoiceInfoLine

                where vendInvoiceInfoLine.ParmId == _vendInvoiceInfoTable.ParmId

                    && vendInvoiceInfoLine.TableRefId == _vendInvoiceInfoTable.TableRefId

                    && _markUpTransTmpCopy.TransTableId == vendInvoiceInfoLine.TableId

                    && _markUpTransTmpCopy.TransRecId == vendInvoiceInfoLine.RecId

                    && _markUpTransTmpCopy.SourceDocumentLine == 0

                    && !_markUpTransTmpCopy.Keep

            join markupTransOtherInvoices

                where markupTransOtherInvoices.OrigTableId == _markUpTransTmpCopy.OrigTableId

                    && markupTransOtherInvoices.OrigRecId == _markUpTransTmpCopy.OrigRecId

                    && markupTransOtherInvoices.TransTableId == _markUpTransTmpCopy.TransTableId

                    && markupTransOtherInvoices.TransRecId != _markUpTransTmpCopy.TransRecId;

        

            //Perform cascading delete action for <c>MarkupTransMapping</c> table

            markUpTransMapping.clear();


            markUpTransMapping.skipDataMethods(true);

            markUpTransMapping.skipDeleteActions(true);

            markUpTransMapping.skipEvents(true);


            delete_from markUpTransMapping

            notexists join _markUpTransTmpCopy

                where _markUpTransTmpCopy.TransRecId == markUpTransMapping.MarkupTransTransRecId &&

                    _markUpTransTmpCopy.TransTableId == markUpTransMapping.MarkupTransTransTableId &&

                    _markUpTransTmpCopy.LineNum == markUpTransMapping.MarkupTransLineNum;

        }


        // Create a SourceDocumentLine record for each new charge

        RecId sourceDocumentHeader = _vendInvoiceInfoTable.SourceDocumentHeader;

        int SourceRelationType = tableNum(MarkupTrans);

        EnumName TypeEnumName = enumStr(SourceDocumentLine_VendorInvoice);

        EnumValue TypeEnumValue = SourceDocumentLine_VendorInvoice::VendorInvoiceChargeLine;

        SourceDocumentLineAccountingStatus AccountingStatus = SourceDocumentLineAccountingStatus::Draft;

        AccountingDate ExchangeRateDate = _vendInvoiceInfoTable.updateDate();


        vendInvoiceInfoLine.clear();

        

        SourceDocumentLine sourceDocumentLine;

        

        sourceDocumentLine.skipDataMethods(true);

        sourceDocumentLine.skipEvents(true);

        sourceDocumentLine.skipTempTableForInsertRecordSet(true);


        insert_recordset sourceDocumentLine

        (

            ParentSourceDocumentLine,

            SourceDocumentHeader,

            AccountingStatus,

            ExchangeRateDate,

            SourceRelationType,

            TypeEnumName,

            TypeEnumValue,

            SourceImplementationRecId

        )

        select

            SourceDocumentLine,

            sourceDocumentHeader,

            AccountingStatus,

            ExchangeRateDate,

            SourceRelationType,

            TypeEnumName,

            TypeEnumValue

        from vendInvoiceInfoLine

            where vendInvoiceInfoLine.ParmId == _vendInvoiceInfoTable.ParmId

                && vendInvoiceInfoLine.TableRefId == _vendInvoiceInfoTable.TableRefId

        join RecId from _markUpTransTmpCopy

            where _markUpTransTmpCopy.TransTableId == vendInvoiceInfoLine.TableId

                && _markUpTransTmpCopy.TransRecId == vendInvoiceInfoLine.RecId

                && _markUpTransTmpCopy.SourceDocumentLine == 0;


        sourceDocumentLine.skipTempTableForInsertRecordSet(false);


        vendInvoiceInfoLine.clear();

        sourceDocumentLine.clear();


        update_recordset _markUpTransTmpCopy setting

            SourceDocumentLine = sourceDocumentLine.RecId

            where _markUpTransTmpCopy.SourceDocumentLine == 0

        join vendInvoiceInfoLine

            where vendInvoiceInfoLine.ParmId == _vendInvoiceInfoTable.ParmId

                && vendInvoiceInfoLine.TableRefId == _vendInvoiceInfoTable.TableRefId

                && vendInvoiceInfoLine.TableId == _markUpTransTmpCopy.TransTableId

                && vendInvoiceInfoLine.RecId == _markUpTransTmpCopy.TransRecId

        join sourceDocumentLine

            where sourceDocumentLine.SourceDocumentHeader == sourceDocumentHeader &&

                sourceDocumentLine.ParentSourceDocumentLine == vendInvoiceInfoLine.SourceDocumentLine &&

                sourceDocumentLine.SourceImplementationRecId == _markUpTransTmpCopy.RecId;


        SAN_GeneratePurchaseMarkupChargesHelper::insertMarkupTransRecordFromMarkupTransTmp(_markUpTransTmpCopy);


        //dispose buffers

        vendInvoiceInfoLine.dispose();

        _markUpTransTmpCopy.dispose();

        markupTransOtherInvoices.dispose();

        markUpTransMapping.dispose();

        sourceDocumentLine.dispose();

    }


    private static void insertMarkupTransRecordFromMarkupTransTmp(MarkupTransTmp _markUpTransTmpCopy)

    {

        SysDaInsertObject insertObj = SAN_GeneratePurchaseMarkupChargesHelper::buildMarkupTransInsertObject();

        SysDaQueryObject  queryObj  = SAN_GeneratePurchaseMarkupChargesHelper::buildMarkupTransTmpQueryObject(_markUpTransTmpCopy);

            

        SAN_GeneratePurchaseMarkupChargesHelper::insertRecords(queryObj, insertObj);

    }


    private static SysDaInsertObject buildMarkupTransInsertObject()

    {

        //insert_recordset markupTransDestination (

        //    BankLCImportChargeAllocation_SA, CalculatedAmount, CalculatedAmountMST_W, CurrencyCode, etc.)


        MarkupTrans markupTransDestination;


        markupTransDestination.skipEvents(true);

        markupTransDestination.skipDataMethods(true);

        markupTransDestination.skipTempTableForInsertRecordSet(true);


        SysDaInsertObject   markupTransInsertObject = new SysDaInsertObject(markupTransDestination);


        SAN_GeneratePurchaseMarkupChargesHelper::addMarkupTransFields(markupTransInsertObject.fields());


        return markupTransInsertObject;

    }


    private static SysDaQueryObject buildMarkupTransTmpQueryObject(MarkupTransTmp _markupTransTmpSource)

    {

        //select BankLCImportChargeAllocation_SA, CalculatedAmount, CalculatedAmountMST_W, CurrencyCode, etc. from markupTransTmpSource


        SysDaQueryObject markupTransTmpQueryObject = new SysDaQueryObject(_markupTransTmpSource);

        SAN_GeneratePurchaseMarkupChargesHelper::addMarkupTransFields(markupTransTmpQueryObject.projection());

        

        return markupTransTmpQueryObject;

    }


    private static void addMarkupTransFields(SysDaSelection _selectionObject)

    {

        _selectionObject

            .add(fieldStr(MarkupTrans, BankLCImportChargeAllocation_SA))

            .add(fieldStr(MarkupTrans, CalculatedAmount))

            .add(fieldStr(MarkupTrans, CalculatedAmountMST_W))

            .add(fieldStr(MarkupTrans, CurrencyCode))

            .add(fieldStr(MarkupTrans, CustomsAssessableValue_IN))

            .add(fieldStr(MarkupTrans, CustVendPosted_RU))

            .add(fieldStr(MarkupTrans, ExchRate_RU))

            .add(fieldStr(MarkupTrans, ExchRateSecond_RU))

            .add(fieldStr(MarkupTrans, FromAmount))

            .add(fieldStr(MarkupTrans, IsAutoCharge))

            .add(fieldStr(MarkupTrans, IsTieredCharge))

            .add(fieldStr(MarkupTrans, ItemPosted_RU))

            .add(fieldStr(MarkupTrans, Keep))

            .add(fieldStr(MarkupTrans, LineNum))

            .add(fieldStr(MarkupTrans, MarkupAllocateAfter_IN))

            .add(fieldStr(MarkupTrans, MarkupCategory))

            .add(fieldStr(MarkupTrans, MarkupClassification_BR))

            .add(fieldStr(MarkupTrans, MarkupCode))

            .add(fieldStr(MarkupTrans, MCRBrokerContractFee))

            .add(fieldStr(MarkupTrans, MCRCouponMarkup))

            .add(fieldStr(MarkupTrans, MCRInstallmentEligible))

            .add(fieldStr(MarkupTrans, MCRMarkupTransCreatedBy))

            .add(fieldStr(MarkupTrans, MCRMiscChargeOverride))

            .add(fieldStr(MarkupTrans, MCROriginalMiscChargeValue))

            .add(fieldStr(MarkupTrans, MCRReasonCode))

            .add(fieldStr(MarkupTrans, MCRRetailInfoCodeId))

            .add(fieldStr(MarkupTrans, MCRSavedRecId))

            .add(fieldStr(MarkupTrans, MCRSavedTableId))

            .add(fieldStr(MarkupTrans, ModuleCategory))

            .add(fieldStr(MarkupTrans, ModuleType))

            .add(fieldStr(MarkupTrans, NotionalCharges_IN))

            .add(fieldStr(MarkupTrans, NotionalPct_IN))

            .add(fieldStr(MarkupTrans, TaxAmount))

            .add(fieldStr(MarkupTrans, TaxAmountExcise_RU))

            .add(fieldStr(MarkupTrans, TaxAmountExciseMST_RU))

            .add(fieldStr(MarkupTrans, TaxAmountMst_W))

            .add(fieldStr(MarkupTrans, TaxAmountVAT_RU))

            .add(fieldStr(MarkupTrans, TaxAmountVATMST_RU))

            .add(fieldStr(MarkupTrans, TaxAutoGenerated))

            .add(fieldStr(MarkupTrans, TaxGroup))

            .add(fieldStr(MarkupTrans, TaxItemGroup))

            .add(fieldStr(MarkupTrans, TaxValueVAT_RU))

            .add(fieldStr(MarkupTrans, TaxVATType_RU))

            .add(fieldStr(MarkupTrans, TaxWriteCode))

            .add(fieldStr(MarkupTrans, ToAmount))

            .add(fieldStr(MarkupTrans, Txt))

            .add(fieldStr(MarkupTrans, Value))

            .add(fieldStr(MarkupTrans, VATDocumentType_RU))

            .add(fieldStr(MarkupTrans, SATProductCode_MX))

            .add(fieldStr(MarkupTrans, SATUnitCode_MX))

            .add(fieldStr(MarkupTrans, WithholdingTypeCode_MX))

            .add(fieldStr(MarkupTrans, MarkupAutoTableRecId))

            .add(fieldStr(MarkupTrans, RetailShippingPromotionDiscount))

            .add(fieldStr(MarkupTrans, IsAdvancedLineProrated))

            .add(fieldStr(MarkupTrans, IsOverriddenProratedLine))

            .add(fieldStr(MarkupTrans, MarkupAutoLineRecId))

            .add(fieldStr(MarkupTrans, IsOverriddenLine))

            .add(fieldStr(MarkupTrans, PreviousValue))

            .add(fieldStr(MarkupTrans, OverrideSalesTax))

            .add(fieldStr(MarkupTrans, TransTableId))

            .add(fieldStr(MarkupTrans, TransRecId))

            .add(fieldStr(MarkupTrans, OrigTableId))

            .add(fieldStr(MarkupTrans, OrigRecId))

            .add(fieldStr(MarkupTrans, SpecificUnitSymbol))

            .add(fieldStr(MarkupTrans, SourceDocumentLine));


        if (LedgerParameters::find().EnableWHTOnCharges == NoYes::Yes)

        {

            _selectionObject

                .add(fieldStr(MarkupTrans, TaxWithholdGroup))

                .add(fieldStr(MarkupTrans, TaxWithholdItemGroup));

        }

    }


    private static void insertRecords(SysDaQueryObject _queryObject, SysDaInsertObject _insertObject)

    {

        _insertObject.query(_queryObject);

        SysDaInsertStatement insertStatement = new SysDaInsertStatement();


        insertStatement.executeQuery(_insertObject);

    }


}

Friday, July 19, 2024

Adding custom field on customer payment generation in D365FO X++

Sample code only for reference.


[ExtensionOf(classStr(CustOverPaym))]

final class CustOverPaymCls_san_Extension

{

    private  container      sanCustCRCTypes;


    #define.CurrentVersion(3)

    #localmacro.CurrentList

        sanCustCRCTypes

    #endmacro


    /// <summary>

    /// Set and get Parm customer credit management types

    /// </summary>

    /// <param name = "_sanParams">current params</param>

    /// <returns>CustCRCTypes</returns>

    public container san_parmCustCRCTypes(container _sanParams = sanCustCRCTypes)

    {

        sanCustCRCTypes = _sanParams;

        return sanCustCRCTypes;

    }


    public container pack()

    {

        container packedClass = next pack();

        return SysPackExtensions::appendExtension(packedClass, classStr(CustOverPaymCls_san_Extension), this.myPack());

    }


    private container myPack()

    {

        return [#CurrentVersion, #CurrentList];

    }


    public boolean unpack(container _packedClass)

    {

        boolean result = next unpack(_packedClass);


        if (result)

        {

            container myState = SysPackExtensions::findExtension(_packedClass, classStr(CustOverPaymCls_san_Extension));

            if (!this.myUnpack(myState))

            {

                result = false;

            }

        }


        return result;

    }


    private boolean myUnpack(container packedClass)

    {

        Integer version = RunBase::getVersion(packedClass);

        switch (version)

        {

            case #CurrentVersion:

                [version, #currentList] = packedClass;

                break;

            default:

                return false;

        }

        return true;

    }


    /// <summary>

    /// Builds the cross company query used to calculate summarized totals. COC

    /// </summary>

    /// <param name = "_calcArgs">Contains arguments used to construct the query</param>

    /// <returns>The generated query.</returns>

    protected Query buildCrossCompanyQueryForCalcTotalSummarized(CustOverPaymCalcTotalArgs _calcArgs)

    {

        Query       sanQuery;

        sanQuery = next buildCrossCompanyQueryForCalcTotalSummarized(_calcArgs);

        if(sanCustCRCTypes != conNull())

        {

            this.san_setQueryRangeCustCRCTypes(sanQuery);

        }

        return sanQuery;

    }


    /// <summary>

    /// Builds the cross company query used to calculate not summarized totals. COC

    /// </summary>

    /// <param name = "_calcArgs">Contains arguments used to construct the query</param>

    /// <returns>The generated query.</returns>

    protected Query buildCrossCompanyQueryForCalcTotalNonSummarized(CustOverPaymCalcTotalArgs _calcArgs)

    {

        Query       sanQuery;

        sanQuery = next buildCrossCompanyQueryForCalcTotalNonSummarized(_calcArgs);

        if(sanCustCRCTypes != conNull())

        {

            this.san_setQueryRangeCustCRCTypes(sanQuery);

        }

        return sanQuery;

    }


    /// <summary>

    /// Builds the query used in methods <c>findTransactionsForReimbursementNonSummarized</c> and <c>findTransactionsForReimbursementSummarized</c>. COC

    /// </summary>

    /// <param name = "_findTransArgs">Contains arguments used to build the query.</param>

    /// <returns>The generated query.</returns>

    protected Query buildFindTransactionsForReimbursementQuery(CustOverPaymFindTransactionsForReimburseArgs _findTransArgs)

    {

        Query       sanQuery;

        sanQuery = next buildFindTransactionsForReimbursementQuery(_findTransArgs);

        if(sanCustCRCTypes != conNull())

        {

            this.san_setQueryRangeCustCRCTypes(sanQuery);

        }

        return sanQuery;

    }


    /// <summary>

    /// setting query range for customer credit management types to existing query

    /// </summary>

    /// <param name = "_query">Query</param>

    public void san_setQueryRangeCustCRCTypes(Query  _query)

    {

        QueryBuildDataSource    qbdsCustTable;

        str                     CustCRCTypesIds;

        san_CustCreditManagementTypes       custCreditManagementTypes;


        #define.sanSemicolon(';')

        #define.sanComma(',')


        qbdsCustTable = _query.dataSourceTable(tableNum(CustTable));

        if(qbdsCustTable == null)

        {

            qbdsCustTable = _query.addDataSource(tableNum(CustTable));

        }


        CustCRCTypesIds = con2StrUnlimited(sanCustCRCTypes,#sanComma);

        if (CustCRCTypesIds)

        {

            CustCRCTypesIds = Global::strReplace(CustCRCTypesIds, #sanSemicolon, #sanComma);

            qbdsCustTable.addRange(fieldNum(CustTrans, san_CustCreditMgmtType)).value(CustCRCTypesIds);

        }

    }


}

Wednesday, June 5, 2024

Read value from XML (Attribute based) in D365FO

     public void readXML_Test(str _invoiceId)

    {

        str xmlData = @'<?xml version="1.0" encoding="UTF-8"?>

<cfdi:Comprobante Moneda="MXN" xsi:schemaLocation="<URL>" Version="4.0" Serie="MI" Folio="22" Fecha="2024-05-07T15:55:22" NoCertificado="01101111100504511177" Certificado="Q1MDkwNzcwDQYJKo/mgAwIBAgIUMDAwMD5on" CondicionesDePago="Neto 30 Dias" Total="1160.00" TipoDeComprobante="I" MetodoPago="PPP" FormaPago="99" LugarExpedicion="66269" Exportacion="01" SubTotal="1000.00" Sello="Rpf" xmlns:cfdi="<URL>" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <cfdi:Emisor Rfc="TTTX6" Nombre="TEST SALES MEXICO" RegimenFiscal="601"/>

    <cfdi:Receptor Rfc="TT10427D90" Nombre="TEST INTERNATIONAL" UsoCFDI="G01" DomicilioFiscalReceptor="6310" RegimenFiscalReceptor="601"/>

    <cfdi:Conceptos>

        <cfdi:Concepto ClaveProdServ="10111300" NoIdentificacion="10111300" Cantidad="1" ClaveUnidad="ACT" Unidad="un" Descripcion="test" ValorUnitario="1000.000000" Importe="1000" ObjetoImp="02">

            <cfdi:Impuestos>

                <cfdi:Traslados>

                    <cfdi:Traslado Base="1000.00" Impuesto="002" TipoFactor="Tasa" TasaOCuota="0.160000" Importe="160.00"/>

                </cfdi:Traslados>

            </cfdi:Impuestos>

        </cfdi:Concepto>

 <cfdi:Concepto ClaveProdServ="10111300" NoIdentificacion="10111300" Cantidad="1" ClaveUnidad="ACT" Unidad="un" Descripcion="test" ValorUnitario="1000.000000" Importe="1000" ObjetoImp="02">

            <cfdi:Impuestos>

                <cfdi:Traslados>

                    <cfdi:Traslado Base="1000.00" Impuesto="002" TipoFactor="Tasa" TasaOCuota="0.160000" Importe="160.00"/>

                </cfdi:Traslados>

            </cfdi:Impuestos>

        </cfdi:Concepto>

    </cfdi:Conceptos>

    <cfdi:Impuestos TotalImpuestosTrasladados="160.00">

        <cfdi:Traslados>

            <cfdi:Traslado Base="1000.00" Impuesto="002" TipoFactor="Tasa" TasaOCuota="0.160000" Importe="160.00"/>

        </cfdi:Traslados>

    </cfdi:Impuestos>

    <cfdi:Complemento>

        <tfd:TimbreFiscalDigital Version="1.1" UUID="27230B06-966F-4F0B-8111-3F57EEBF4841" FechaTimbrado="2024-05-07T15:56:38" RfcProvCertif="INT020124V62" SelloCFD="Rpf2O==" xsi:schemaLocation="<>URL3" xmlns:tfd="<URL1>"/>

    </cfdi:Complemento>

</cfdi:Comprobante>';


        XmlDocument xmlDocument;

        XmlNodeList xmlRecords, xmlFields, xmlFields1, xmlFields2, xmlFields3;

        XmlNode xmlRecord, xmlField, xmlField1, xmlField2, xmlField3;

        XMLParseError xmlError;

        XmlElement xmlParentRecord;

        xmlDocument = new XmlDocument();

        xmlDocument.loadXml(xmlData);


        xmlError = xmlDocument.parseError();


        Map         mapHeader = new Map(Types::String, Types::Container);

        Map         mapParentNode = new Map(Types::String, Types::Container);

        Map         mapSummary = new Map(Types::String, Types::Container);

        Map         mapItemInfo = new Map(Types::String, Types::Container);


        str     einvoiceId = _invoiceId;

        xmlParentRecord = xmlDocument.root();

        var name = xmlParentRecord.name();

        xmlRecords  = xmlDocument.root().childNodes();

        xmlRecord   = xmlRecords.nextNode();

        XmlNamedNodeMap mapValue;

        XmlNode     curNode;


        mapValue = xmlParentRecord.attributes();

        

        if(!mapParentNode.exists(strFmt("ParentNode%1", einvoiceId)))

        {

            mapParentNode.insert(strFmt("ParentNode%1", einvoiceId), [mapValue.getNamedItem('CondicionesDePago').nodeValue(),mapValue.getNamedItem('Moneda').nodeValue(), mapValue.getNamedItem('TipoDeComprobante').nodeValue()]);

        }


        while (xmlRecord)

        {

            mapValue = null;

            switch(xmlRecord.name())

            {

                case "cfdi:Receptor" : //XML without any sub XML tags

                    mapValue = xmlRecord.attributes();

                    if(!mapHeader.exists(strFmt("Header%1%2", einvoiceId,xmlRecord.name())))

                    {

                        mapHeader.insert(strFmt("Header%1%2", einvoiceId,xmlRecord.name()), [mapValue.getNamedItem('UsoCFDI').nodeValue(),mapValue.getNamedItem('Nombre').nodeValue(), mapValue.getNamedItem('DomicilioFiscalReceptor').nodeValue()]);

                    }

                    break;


                case "cfdi:Emisor" : //XML without any sub XML tags

                    mapValue = xmlRecord.attributes();

                    if(!mapHeader.exists(strFmt("Header%1%2", einvoiceId,xmlRecord.name())))

                    {

                        mapHeader.insert(strFmt("Header%1%2", einvoiceId,xmlRecord.name()), [mapValue.getNamedItem('RegimenFiscal').nodeValue()]);

                    }

                    break;


                case "cfdi:Conceptos" : //XML with sub XML tags

                    xmlFields = xmlRecord.childNodes();

                    xmlField = xmlFields.nextNode();

                    int counter;

                    while (xmlField)

                    {

                        counter++;

                        anytype     claveProdServ,claveUnidad,netAmountImporte,description, base,impuesto,tipoFactor,TasaOcuota,actualSalesTaxImporte,descuento;

                        mapValue = xmlField.attributes();

                        claveProdServ = mapValue.getNamedItem('ClaveProdServ').nodeValue();

                        claveUnidad = mapValue.getNamedItem('ClaveUnidad').nodeValue();

                        netAmountImporte = mapValue.getNamedItem('Importe').nodeValue();

                        description = mapValue.getNamedItem('Descripcion').nodeValue();

                        if(mapValue.getNamedItem('Descuento'))

                        {

                            descuento = mapValue.getNamedItem('Descuento').nodeValue();

                        }

                        xmlFields1 = xmlField.childNodes();

                        xmlField1 =xmlFields1.nextNode();

                        while (xmlField1)

                        {

                            xmlFields2 = xmlField1.childNodes();

                            xmlField2 =xmlFields2.nextNode();


                            while (xmlField2)

                            {

                                xmlFields3 = xmlField2.childNodes();

                                xmlField3 =xmlFields3.nextNode();


                                while (xmlField3)

                                {

                                    mapValue = xmlField3.attributes();

                                    base = mapValue.getNamedItem('Base').nodeValue();

                                    impuesto = mapValue.getNamedItem('Impuesto').nodeValue();

                                    tipoFactor = mapValue.getNamedItem('TipoFactor').nodeValue();

                                    TasaOCuota = mapValue.getNamedItem('TasaOCuota').nodeValue();

                                    actualSalesTaxImporte = mapValue.getNamedItem('Importe').nodeValue();

                                    xmlField3 = xmlFields3.nextNode();

                                }

                                xmlField2 = xmlFields2.nextNode();

                            }

                            xmlField1 = xmlFields1.nextNode();

                        }


                        if(!mapItemInfo.exists(strFmt("Lines%1%2", einvoiceId,counter)))

                        {

                            mapItemInfo.insert(strFmt("Lines%1%2", einvoiceId,counter), [claveProdServ,claveUnidad,netAmountImporte,description, base,impuesto,tipoFactor,TasaOcuota,actualSalesTaxImporte,descuento]);

                        }

                        xmlField =xmlFields.nextNode();

                    }

                    break;

            }

            xmlRecord = xmlRecords.nextNode();

        }


        //To read values

        //if(mapItemInfo.exists(strFmt("Lines%1%2",InvoiceId,lineCounter)))

        //{

        //    container   itemInfoLines = mapItemInfo.lookup(strFmt("Lines%1%2",eInvoiceJourMapping.invoiceId(),lineCounter));

        //    conPeek(itemInfoLines,1);

        //    conPeek(itemInfoLines,2);

        //    conPeek(itemInfoLines,3);

        //    conPeek(itemInfoLines,4);

        //}

    }

Tuesday, March 5, 2024

Get Feature is enabled or disabled in D365FO X++

 Issue: Feature is enabled or disabled to get in customize ISV/VAR/USR model. Since it was defined as internal class by MS, and which can't be accessible in reference model.

Workaround solution (X++) in ISV/VAR/USR model: Without using MS OOB class.

IdentifierName featureEnableFeature = 'Dynamics.AX.Application.<>';

        FeatureManagementState featureManagementState;

boolean isFeatureEnabled = false;

        select firstonly featureManagementState

            where featureManagementState.Name == featureEnableFeature ;

        if (featureManagementState.IsEnabled)

        {

            isFeatureEnabled  = true;

        }

Skip Inventory reservation on Sales order lines in D365FO

[ExtensionOf(classStr(InventUpd_Reservation))] final class InventUpd_ReservationCls_SAN_Extension {     void updateNow()     {         if(mo...