21 December 2025

Customer inbound custom service in D365 F&O with X++

 




[DataContractAttribute]
class GOD_CustomerCreateContract
{
    str                 accountNum;     // Optional; if blank, a number sequence will be used
    str                 name;           // Required
    str                 custGroup;      // Required
    str                 currency;       // Optional; will fall back to company defaults if you handle that logic
    str                 email;          // Optional (see note below)
    str                 phone;          // Optional (see note below)
    str                 street;         // Optional
    str                 city;           // Optional
    str                 state;          // Optional
    str                 zipCode;        // Optional
    str                 country;        // Optional but recommended if address is sent
    str                 company;        // Optional; if set, code runs under this company

    // When true, creates a Person party; otherwise Organization
    boolean             isPerson;

    [DataMemberAttribute("AccountNum"), AifCollectionTypeAttribute('NONE', Types::String)]
    public str parmAccountNum(str _accountNum = accountNum)
    {
        accountNum = _accountNum;
        return accountNum;
    }

    [DataMemberAttribute("Name")]
    public str parmName(str _name = name)
    {
        name = _name;
        return name;
    }

    [DataMemberAttribute("CustGroup")]
    public str parmCustGroup(str _custGroup = custGroup)
    {
        custGroup = _custGroup;
        return custGroup;
    }

    [DataMemberAttribute("Currency")]
    public str parmCurrency(str _v = currency)
    {
        currency = _v;
        return currency;
    }

    [DataMemberAttribute("Email")]
    public str parmEmail(str _v = email)
    {
        email = _v;
        return email;
    }

    [DataMemberAttribute("Phone")]
    public str parmPhone(str _v = phone)
    {
        phone = _v;
        return phone;
    }

    [DataMemberAttribute("Street")]
    public str parmStreet(str _v = street)
    {
        street = _v;
        return street;
    }

    [DataMemberAttribute("City")]
    public str parmCity(str _v = city)
    {
        city = _v;
        return city;
    }

    [DataMemberAttribute("State")]
    public str parmState(str _v = state)
    {
        state = _v;
        return state;
    }

    [DataMemberAttribute("ZipCode")]
    public str parmZipCode(str _v = zipCode)
    {
        zipCode = _v;
        return zipCode;
    }

    [DataMemberAttribute("Country")]
    public str parmCountry(str _v = country)
    {
        country = _v;
        return country;
    }

    [DataMemberAttribute("Company")]
    public str parmCompany(str _v = company)
    {
        company = _v;
        return company;
    }

    [DataMemberAttribute("IsPerson")]
    public boolean parmIsPerson(boolean _v = isPerson)
    {
        isPerson = _v;
        return isPerson;
    }

}



[DataContractAttribute]
class GOD_CustomerCreateResponse
{
    boolean success;
    str     message;
    str     accountNum;
    str     partyNumber;
    RecId   custRecId;
    RecId   partyRecId;

    [DataMemberAttribute("Success")]
    public boolean parmSuccess(boolean _v = success)
    {
        success = _v;
        return success;
    }

    [DataMemberAttribute("Message")]
    public str parmMessage(str _v = message)
    {
        message = _v;
        return message;
    }

    [DataMemberAttribute("AccountNum")]
    public str parmAccountNum(str _v = accountNum)
    {
        accountNum = _v;
        return accountNum;
    }

    [DataMemberAttribute("PartyNumber")]
    public str parmPartyNumber(str _v = partyNumber)
    {
        partyNumber = _v;
        return partyNumber;
    }

    [DataMemberAttribute("CustRecId")]
    public RecId parmCustRecId(RecId _v = custRecId)
    {
        custRecId = _v;
        return custRecId;
    }

    [DataMemberAttribute("PartyRecId")]
    public RecId parmPartyRecId(RecId _v = partyRecId)
    {
        partyRecId = _v;
        return partyRecId;
    }

}



class GOD_CustomerCreateService
{
    private GOD_CustomerCreateResponse buildResponse(boolean _success, str _message, str _accountNum = '', RecId _custRecId = 0, RecId _partyRecId = 0)
    {
        GOD_CustomerCreateResponse resp = new GOD_CustomerCreateResponse();
        DirPartyTable             party;

        resp.parmSuccess(_success);
        resp.parmMessage(_message);
        resp.parmAccountNum(_accountNum);
        resp.parmCustRecId(_custRecId);
        resp.parmPartyRecId(_partyRecId);

        if (_partyRecId)
        {
            party = DirPartyTable::findRec(_partyRecId);
            resp.parmPartyNumber(party.PartyNumber);
        }

        return resp;
    }

    /// <summary>
    /// Creates a customer with party, and optionally a postal address.
    /// </summary>
    /// <param name="_cust">Data contract with customer details.</param>
    /// <returns>Response contract with result info.</returns>
    [SysEntryPointAttribute(true)]
    public GOD_CustomerCreateResponse createCustomer(GOD_CustomerCreateContract _cust)
    {
        GOD_CustomerCreateResponse  response;
        str                        targetCompany = _cust.parmCompany();

        // Simple validation
        if (!_cust.parmName())
        {
            return this.buildResponse(false, "Name is required.");
        }

        if (!_cust.parmCustGroup())
        {
            return this.buildResponse(false, "CustGroup is required.");
        }

        // Validate customer group
        if (!CustGroup::exist(_cust.parmCustGroup()))
        {
            return this.buildResponse(false, strFmt("CustGroup %1 does not exist.", _cust.parmCustGroup()));
        }

        // Run in the specified company, if any
        if (targetCompany)
        {
            changecompany(targetCompany)
            {
                response = this.createCustomerInCurrentCompany(_cust);
            }
        }
        else
        {
            response = this.createCustomerInCurrentCompany(_cust);
        }

        return response;
    }

    private GOD_CustomerCreateResponse createCustomerInCurrentCompany(GOD_CustomerCreateContract _cust)
    {
        CustTable                   custTable;
        NumberSeq                   numSeq;
        boolean                     generatedAccount = false;
        str                         accountNum = _cust.parmAccountNum();
        RecId                       partyRecId;
        GOD_CustomerCreateResponse   resp;
        DirParty                    dirParty;
        DirPartyPostalAddressView   dirPartyPostalAddressView;
        DirPartyContactInfoView     dirPartyContactInfo;
        try
        {
            ttsbegin;

            // If caller supplied AccountNum and it already exists, return success (idempotent)
            if (accountNum && CustTable::exist(accountNum))
            {
                custTable = CustTable::find(accountNum, true);
                ttscommit;
                return this.buildResponse(true, "Customer already exists; no action taken.", accountNum, custTable.RecId, custTable.Party);
            }

            // Generate account from number sequence if not provided
            if (!accountNum)
            {
                NumberSequenceReference nsRef = CustParameters::numRefCustAccount();
                if (!nsRef)
                {
                    throw error("Customer account number sequence reference is not configured.");
                }
                numSeq = NumberSeq::newGetNum(nsRef);
                accountNum = numSeq.num();
                generatedAccount = true;
            }

            // Create Party (Organization or Person)
            DirPartyType partyType = _cust.parmIsPerson() ? DirPartyType::Person : DirPartyType::Organization;
            //partyRecId = DirParty::createParty(partyType, _cust.parmName());

            // Create the customer
            custTable.clear();
            custTable.initValue();                 // defaults
            custTable.AccountNum   = accountNum;
            custTable.CustGroup    = _cust.parmCustGroup();
            custTable.PaymTermId   = CustGroup::find(custTable.CustGroup).PaymTermId;
            //custTable.Party        = partyRecId;

            if (_cust.parmCurrency())
            {
                custTable.Currency = _cust.parmCurrency();
            }

            custTable.insert(partyType,_cust.parmName());

            // Mark number sequence as used if we generated it
            if (generatedAccount && numSeq)
            {
                numSeq.used();
            }

            // Optional: create postal address if provided
            dirParty = DirParty::constructFromCommon(custTable);
            dirPartyPostalAddressView.city          = _cust.parmCity();
            dirPartyPostalAddressView.Street        = _cust.parmStreet();
            dirPartyPostalAddressView.State         = _cust.parmState();
            dirPartyPostalAddressView.County        = _cust.parmCountry();
            dirPartyPostalAddressView.IsPrimary     = NoYes::Yes;
            dirParty.createOrUpdatePostalAddress(dirPartyPostalAddressView);
            //
            //if (_cust.parmStreet() || _cust.parmCity() || _cust.parmZipCode() || _cust.parmCountry())
            //{
            //    this.createPrimaryPostalAddress(partyRecId,
            //                                _cust.parmStreet(),
            //                                _cust.parmCity(),
            //                                _cust.parmState(),
            //                                _cust.parmZipCode(),
            //                                _cust.parmCountry());
            //}
            dirPartyContactInfo.LocationName = "Email address";
            dirPartyContactInfo.Locator = "abc@gmail.com";
            dirPartyContactInfo.Type = LogisticsElectronicAddressMethodType::Email;
            dirPartyContactInfo.IsPrimary       = NoYes::Yes;
            dirParty.createOrUpdateContactInfo(dirPartyContactInfo);

            // Optional: add electronic addresses (email/phone)
            // NOTE: There are multiple helper APIs; uncomment and adapt as per your standard.
            /*
        if (_cust.parmEmail())
        {
            DirPartyContactInfo::addElectronicAddress(partyRecId,
                                                        LogisticsElectronicAddressMethodType::Email,
                                                        _cust.parmEmail(),
                                                        true,     // isPrimary
                                                        "Primary email");
        }

        if (_cust.parmPhone())
        {
            DirPartyContactInfo::addElectronicAddress(partyRecId,
                                                        LogisticsElectronicAddressMethodType::Phone,
                                                        _cust.parmPhone(),
                                                        false,    // isPrimary
                                                        "Main phone");
        }
        */

            ttscommit;

            resp = this.buildResponse(true, "Customer created successfully.", accountNum, custTable.RecId, partyRecId);
        }
        catch (Exception::Error)
        {
            // Rollback handled automatically by the runtime on exception
            resp = this.buildResponse(false, infolog.text(), accountNum);
        }
        catch
        {
            resp = this.buildResponse(false, "Unknown error creating customer.", accountNum);
        }

        return resp;
    }

    /// <summary>
    /// Creates a primary postal address for the specified party.
    /// </summary>
    /*
    private void createPrimaryPostalAddress(RecId _partyRecId, str _street, str _city, str _state, str _zip, str _country)
    {
        // Use logistics location framework
        LogisticsLocation         loc = LogisticsLocation::construct();
        RecId                     locationRecId;
        LogisticsPostalAddress    postal = LogisticsPostalAddress::construct();
        DirPartyLocation          partyLocation = DirPartyLocation::construct();

        // Create a location
        locationRecId = loc.createLocation();

        // Create postal address record
        postal.parmLocation(locationRecId);
        postal.parmIsPrimary(true);
        postal.parmStreet(_street);
        postal.parmCity(_city);
        postal.parmState(_state);
        postal.parmZipCode(_zip);
        postal.parmCountryRegionId(_country);
        postal.create();

        // Link the location to the party
        partyLocation.parmParty(_partyRecId);
        partyLocation.parmLocation(locationRecId);
        partyLocation.parmIsPostalAddress(true);
        partyLocation.parmIsPrimary(true);
        partyLocation.create();
    }
    */

}


internal final class GOD_CustomerCreateTestingJob
{
    /// <summary>
    /// Class entry point. The system will call this method when a designated menu 
    /// is selected or when execution starts and this class is set as the startup class.
    /// </summary>
    /// <param name = "_args">The specified arguments.</param>
    public static void main(Args _args)
    {
        GOD_CustomerCreateContract  contract = new GOD_CustomerCreateContract();      
        GOD_CustomerCreateService customerCreateService = new GOD_CustomerCreateService();

        contract.parmAccountNum('US-104');
        contract.parmCompany('USMF');
        contract.parmName('TestCust');
        contract.parmCustGroup('90');

        contract.parmStreet('KPHB');
        contract.parmCity('Chennai');
        contract.parmStreet('Adayar');
        contract.parmCountry('India');
        contract.parmState('TamilNadu');
        customerCreateService.createCustomer(contract);

    }

}



{
  "_cust": {
    "Company": "USMF",
    "Name": "Contoso Retail Pvt Ltd",
    "CustGroup": "10",
    "Currency": "USD",
    "Street": "1 Microsoft Way",
    "City": "Redmond",
    "State": "WA",
    "ZipCode": "98052",
    "Country": "US",
    "IsPerson": false
  }
}
Post URL:

























How to download the multiple document attchments in D365 F&O Using X++

 The below code is to download the attachments which are attached in Vendor Invoice journals

AP->Inquiries and reports->Invoice-> Invoice Journal


The above screen added new button to download the multiple selected records attachments.


[ExtensionOf(formControlStr(VendInvoiceJournal,ABCDownloadFile))]
final class ABC_VendInvoiceJournalButton_Extension
{
    public void clicked()
    {
        FormButtonControl fbc = this as FormButtonControl;

        VendInvoiceJour vendInvoiceJour;
        DocuRef docuRef;
        DocuValue docuValue;
        str docURL;

        next clicked();
        FormRun             formRun     = fbc.formRun();
        FormDataSource      fds         = formRun.dataSource();        
        vendInvoiceJour = fds.getFirst(true);

        while(vendInvoiceJour.RecId!=0)
        {
            select firstOnly docuRef
                where docuRef.RefTableId    == vendInvoiceJour.TableId
                && docuRef.RefRecId         == vendInvoiceJour.RecId
                && docuRef.RefCompanyId     == vendInvoiceJour.DataAreaId;

            if (docuRef.RecId)
            {
                docuValue = DocuValue::find(docuRef.ValueRecId);
                
                
                if (docuValue.RecId)
                {
                    docURL = File::SendFileToTempStore(DocumentManagement::getAttachmentStream(docuRef),docuValue.fileName+'.'+docuValue.FileType,classStr(FileUploadTemporaryStorageStrategy),true);
                    Browser browser = new Browser();
                    browser.navigate(docURL,true,false);
                }
                else
                {
                    warning("No file attachment found for the selected record.");
                }
            }
            else
            {
                warning("No document reference found for the selected record.");
            }
            vendInvoiceJour = fds.getNext();
        }
            
    }







21 November 2025

DB restore production DB to DEV environment in D365 FO using Bacpac file

 

https://www.linkedin.com/pulse/restore-production-database-dev-environment-d365fo-using-abbas-tfwbe

SQLPackage link : https://www.linkedin.com/redir/redirect?url=https%3A%2F%2Fgo%2Emicrosoft%2Ecom%2Ffwlink%2F%3Flinkid%3D2261576&urlhash=8Q0l&trk=article-ssr-frontend-pulse_little-text-block

Step1. Need to download SQL package

Stop all the required services


Rename the AXDB to AXDB Old


Run the command to connect to package extracted path

Then Run the below command. To import

==

SqlPackage.exe /a:import /sf:C:\SQLPackage\carlsbergbalkanchinabackup.bacpac /tsn:vm-WE-MD09-d-1 /tdn:AxDB /p:CommandTimeout=200000 /TargetEncryptConnection:False



tsn (target server name) – The name of the SQL Server to import into.

tdn (target database name) – The name of the database to import into. The database should not already exist.

sf (source file) – The path and name of the file to import from.

/p:CommandTimeout=200000 seconds approximately 55 hours timeout for command

/TargetEncryptConnectionLFalse this is the updated script which will keep us away from certificate errors

After DB import run the below script

CREATE USER axdeployuser FROM LOGIN axdeployuser

EXEC sp_addrolemember 'db_owner', 'axdeployuser'


CREATE USER axdbadmin FROM LOGIN axdbadmin

EXEC sp_addrolemember 'db_owner', 'axdbadmin'


CREATE USER axmrruntimeuser FROM LOGIN axmrruntimeuser

EXEC sp_addrolemember 'db_datareader', 'axmrruntimeuser'

EXEC sp_addrolemember 'db_datawriter', 'axmrruntimeuser'


CREATE USER axretaildatasyncuser FROM LOGIN axretaildatasyncuser


CREATE USER axretailruntimeuser FROM LOGIN axretailruntimeuser


CREATE USER axdeployextuser FROM LOGIN axdeployextuser


CREATE USER [NT AUTHORITY\NETWORK SERVICE] FROM LOGIN [NT AUTHORITY\NETWORK SERVICE]

EXEC sp_addrolemember 'db_owner', 'NT AUTHORITY\NETWORK SERVICE'


UPDATE T1

SET T1.storageproviderid = 0

    , T1.accessinformation = ''

    , T1.modifiedby = 'Admin'

    , T1.modifieddatetime = getdate()

FROM docuvalue T1

WHERE T1.storageproviderid = 1 --Azure storage


DROP PROCEDURE IF EXISTS SP_ConfigureTablesForChangeTracking

DROP PROCEDURE IF EXISTS SP_ConfigureTablesForChangeTracking_V2

GO

-- Begin Refresh Retail FullText Catalogs

DECLARE @RFTXNAME NVARCHAR(MAX);

DECLARE @RFTXSQL NVARCHAR(MAX);

DECLARE retail_ftx CURSOR FOR

SELECT OBJECT_SCHEMA_NAME(object_id) + '.' + OBJECT_NAME(object_id) fullname FROM SYS.FULLTEXT_INDEXES

    WHERE FULLTEXT_CATALOG_ID = (SELECT TOP 1 FULLTEXT_CATALOG_ID FROM SYS.FULLTEXT_CATALOGS WHERE NAME = 'COMMERCEFULLTEXTCATALOG');

OPEN retail_ftx;

FETCH NEXT FROM retail_ftx INTO @RFTXNAME;


BEGIN TRY

    WHILE @@FETCH_STATUS = 0 

    BEGIN 

        PRINT 'Refreshing Full Text Index ' + @RFTXNAME;

        EXEC SP_FULLTEXT_TABLE @RFTXNAME, 'activate';

        SET @RFTXSQL = 'ALTER FULLTEXT INDEX ON ' + @RFTXNAME + ' START FULL POPULATION';

        EXEC SP_EXECUTESQL @RFTXSQL;

        FETCH NEXT FROM retail_ftx INTO @RFTXNAME;

    END

END TRY

BEGIN CATCH

    PRINT error_message()

END CATCH


CLOSE retail_ftx; 

DEALLOCATE retail_ftx; 

-- End Refresh Retail FullText Catalogs


--Begin create retail channel database record--

declare @ExpectedDatabaseName nvarchar(64) = 'Default';

declare @DefaultDataGroupRecId BIGINT;

declare @ExpectedDatabaseRecId BIGINT; 

IF NOT EXISTS (select 1 from RETAILCONNDATABASEPROFILE where NAME = @ExpectedDatabaseName)

BEGIN 

select @DefaultDataGroupRecId = RECID from RETAILCDXDATAGROUP where NAME = 'Default'; 

insert into RETAILCONNDATABASEPROFILE (DATAGROUP, NAME, CONNECTIONSTRING, DATASTORETYPE)

values (@DefaultDataGroupRecId, @ExpectedDatabaseName, NULL, 0); 

select @ExpectedDatabaseRecId = RECID from RETAILCONNDATABASEPROFILE where NAME = @ExpectedDatabaseName; 

insert into RETAILCDXDATASTORECHANNEL (CHANNEL, DATABASEPROFILE)

select RCT.RECID, @ExpectedDatabaseRecId from RETAILCHANNELTABLE RCT

inner join RETAILCHANNELTABLEEXT RCTEX on RCTEX.CHANNEL = RCT.RECID

        update RETAILCHANNELTABLEEXT set LIVECHANNELDATABASE = @ExpectedDatabaseRecId where LIVECHANNELDATABASE = 0

END; 

---------------


Restart the required services.
Refresh models and build the Models

24 August 2025

How to add the Display method in the form level in D365 F&O

 The below examle is to add the form -Data source level adding display method and assigning to form design control.



25 April 2025

AXmodel file import in D365 F&O

 ModelUtil.exe -import -metadatastorepath=k:\aosservice\packageslocaldirectory -file=C:\Temp\DEST.axmodel

10 April 2025

X++ code to get the time and add and substract using the current date time in D365 F&O

 utcdatetime dateTime = DateTimeUtil::getSystemDateTime();//DateTimeUtil::utcNow();

        utcdatetime newDateTime = DateTimeUtil::addMinutes(dateTime, -20);

        Info(strFmt("Current time: %1 redueced Time: %2 ",dateTime,newDateTime));


09 April 2025

X++ code to create new user in D365 F&O

 public void createUser(UserId _userId,Name _userName)

{

    UserInfo userInfo;

    SecurityUserRole userRole,userRoleToSave;

    SecurityRole    securityRole;

   

    // Step 1: Create new User Info record

    ttsBegin;


    userInfo.id             = _userId;

    userInfo.Name           = _userName;

    userInfo.networkAlias   = Xine_UserAcessTable.Email;

    userInfo.networkDomain  = Xine_UserAcessTable.NetworkDomain;

    userInfo.ObjectId       = Xine_UserAcessTable.TelemetryId;        

    userInfo.enable         = Xine_UserAcessTable.Enabled;

    userInfo.insert(); // Save the user info record


    info(strFmt('User created: %1', userInfo.id));


    select securityRole

        where securityRole.Name == "System user";


    select forupdate userRoleToSave 

        where userRoleToSave.User == _userId &&

                userRoleToSave.SecurityRole == securityRole.recId;

    if ( userRoleToSave.recId)

    {

        userRoleToSave.AssignmentMode = RoleAssignmentMode::Manual;

        userRoleToSave.AssignmentStatus = RoleAssignmentStatus::Enabled;

        userRoleToSave.update();

    }

    else

    {

        userRole.User = _userId;

        userRole.SecurityRole = securityRole.recId; // Set the role of the user (System User role is used here as an example)

        userRole.AssignmentMode = RoleAssignmentMode::Manual;

        userRole.AssignmentStatus = RoleAssignmentStatus::Enabled;

        userRole.insert(); // Save user role


        info(strFmt('Role assigned to user: %1', userRole.SecurityRole));

    }


    // Commit the transaction

    ttsCommit;

}

Customer inbound custom service in D365 F&O with X++

  [DataContractAttribute] class GOD_CustomerCreateContract {     str                 accountNum;     // Optional; if blank, a number sequenc...