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:

























No comments:

Post a Comment

Give me the commetns and solutions

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

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