https://anithasantosh.wordpress.com/2024/11/07/clean-up-jobs-in-d365fo/
Explore Dynamics World With Aslam
Tuesday, 24 December 2024
Monday, 23 December 2024
Open URL in browser using X++
In this blog article, we will see how we can open a URL in a web browser using X++ code. It is achieved using Browser the class which extends System Class Browser having the only method navigate(). It has three parameters from which only first is mandatory:
1. downloadURL (string) – URL you want to browse.
2. openInNewTab (Boolean) – It is used to specify URL should be open in the same tab or new tab
3. showExitWarning (Boolean) – Prompt a dialog to exit the current page.
class OpenURL
{
public static void main(Args _args)
{
Browser browser = new Browser();
browser.navigate(SystemParameters::find().DALPowerBILink, true, false);
//OR
browser.navigate("https://jonesd365.blogspot.com/", true, false);
}
}
So, this will open URL within a new tab in the browser using X++.
Sunday, 15 December 2024
Derived dimensions in d365 FO X++
public static void main(Args _args)
{
inventParameters inventParameters;
select inventParameters;
RefRecId defaultDimension;
defaultDimension = InventSite::find('21').DefaultDimension;
// Deriving default dimensions linked to inventory site
DimensionDefault derivedDimension = LedgerDimensionFacade::serviceApplyDerivedDimensionsForAttribute(defaultDimension,
inventParameters.SiteDimensionAttribute, Ledger::current());
// Merging derived default dimension linked to inventory site with original default dimension
boolean precedenceEnabled = DimensionAttributeDerivedDimensions::derivedDimensionPrecedenceEnabled();
if (precedenceEnabled && DimensionAttributeDerivedDimensions::dimensionAttributeHasDerivedDimensionPrecedence(inventParameters.SiteDimensionAttribute))
{
defaultDimension = LedgerDimensionDefaultFacade::serviceMergeDefaultDimensions(derivedDimension, defaultDimension);
}
else
{
defaultDimension = LedgerDimensionDefaultFacade::serviceMergeDefaultDimensions(defaultDimension, derivedDimension);
}
DimensionSetEntity dimensionSetEntity;
select * from dimensionSetEntity
where dimensionSetEntity.RecordId == defaultDimension;
Tuesday, 3 December 2024
Validate the Ledger dimension values in d365 FO
MainAccount mainAccountId = LedgerDimensionFacade::getMainAccountFromLedgerDimension(_ledgerPostingTransaction.LedgerDimension);
DimensionHierarchyId accountStructureId = DimensionHierarchy::getAccountStructure(mainAccountId.RecId);if (accountStructureId)
{
LedgerDimensionDefaultingEngine dimDefaultingEngine;
List dimensionSources;
// Validate input parameter
if (!mainAccountId.RecId|| !accountStructureId)
{
throw error(Error::wrongUseOfFunction(funcName()));
}
dimensionSources = new List(Types::Class);
dimensionSources.addEnd(LedgerDimensionDefaultingEngine::getDefaultDimensionSpecifiers(inventJournalTrans.BTCOffsetDefaultDimension));
dimDefaultingEngine = LedgerDimensionDefaultingEngine::constructForMainAccountId(mainAccountId.RecId, accountStructureId);
dimDefaultingEngine.applyDimensionSources(dimensionSources);
LedgerDimensionAccount ledgerDimensionAccountCustom = dimDefaultingEngine.getLedgerDimension();
generalJournalAccountEntry.LedgerDimension = ledgerDimensionAccountCustom;
}
else
{
throw error(strFmt("@SYS4009834", MainAccount::find(mainAccountId.RecId).MainAccountId));
}
Add New end point in Business event in D365 FO to set the header oauth authentication
BusinessEventsEndpointBase - send
This is the method where it trigger the logic.
--------------------------------------------------------------------------------------------------------
For sending the authentication we need to add new class for logic
using System.IO;
using System.Net;
using Microsoft.Dynamics.ApplicationPlatform.XppServices.Instrumentation;
/// <summary>
/// The <c>BusinessEventsHttpAdapter</c> sends the payload for business events to Http endpoint.
/// </summary>
/// <remarks>
/// There may be size restrictions in place for the target http end point. These size restrictions are not checked.
/// </remarks>
// This is a framework class. Customizing this class may cause problems with future upgrades to the software.
[BusinessEventsEndpoint(BusinessEventsEndpointType::HttpsWithHeaders)]
internal final class BusinessEventsHttpsWithHeadersAdapter extends BusinessEventsEndpointBase implements IBusinessEventsEndpoint
{
private str url;
private BusinessEventsId bussinessEventsId;
public void initialize(BusinessEventsEndpoint _endpoint, boolean _forceCreate)
{
this.endpoint = _endpoint;
if (!(_endpoint is BusinessEventsAzureEndpoint))
{
BusinessEventsEndpointManager::logUnknownEndpointRecord(tableStr(BusinessEventsAzureEndpoint), _endpoint.RecId);
}
bussinessEventsId = endpoint.KTBusinessEventId;
if(!bussinessEventsId)
{
throw warning(strFmt("@BusinessEvents:MissingAdapterConstructorParameter", classStr(BusinessEventsHttpAdapter), varStr(bussinessEventsId)));
}
this.parmBusinessEventId(endpoint.KTBusinessEventId);
BusinessEventsAzureEndpoint businessEventsHttpEndpoint = _endpoint as BusinessEventsAzureEndpoint;
str httpUrl = BusinessEventsEndpointManager::getAzureSecret(businessEventsHttpEndpoint, _forceCreate);
if (!httpUrl)
{
throw warning(strFmt("@BusinessEvents:MissingAdapterConstructorParameter", classStr(BusinessEventsHttpAdapter), varStr(httpUrl)));
}
this.parmUrl(httpUrl);
}
/// <summary>
/// Set and get the url
/// </summary>
/// <param name = "_url">Url</param>
/// <returns>Url</returns>
internal str parmUrl(str _url = url)
{
url = _url;
return url;
}
/// <summary>
/// Set and get the Business EventId
/// </summary>
/// <param name = "_businessEventId">businessEventId</param>
/// <returns>businessEventId</returns>
internal BusinessEventsId parmBusinessEventId(BusinessEventsId _businessEventsId = bussinessEventsId)
{
bussinessEventsId = _businessEventsId;
return bussinessEventsId;
}
/// <summary>
/// Send the payload to Http endpoint
/// </summary>
/// <param name = "_payload">Content of business event to be sent</param>
/// <param name = "_context">Context of the payload to be sent</param>
/// <exception>
/// NotSupportedException
/// The request scheme specified in requestUriString has not been registered.
/// ArgumentNullException
/// requestUriString is null.
/// SecurityException
/// The caller does not have WebPermissionAttribute permission to connect to the requested URI or a URI that the request is redirected to.
/// UriFormatException
/// In the .NET for Windows Store apps or the Portable Class Library, catch the base class exception, FormatException, instead.
/// The URI specified in requestUriString is not a valid URI.
/// NotImplementedException
/// Any attempt is made to access the method, when the method is not overridden in a descendant class.
/// ArgumentException
/// The sum of offset and count is greater than the buffer length.
/// ArgumentNullException
/// buffer is null.
/// ArgumentOutOfRangeException
/// offset or count is negative.
/// IOException
/// An I/O error occured, such as the specified file cannot be found.
/// NotSupportedException
/// The stream does not support writing.
/// ObjectDisposedException
/// Write(Byte[], Int32, Int32) was called after the stream was closed.
/// </exception>
[Hookable(false)]
protected void sendPayload(str _payload, BusinessEventsEndpointPayloadContext _context)
{
// convert payload string to byte array
var utf8 = System.Text.Encoding::get_UTF8();
var byteArrayPayload = utf8.GetBytes(_payload);
HttpWebRequest request;
if (_context is BusinessEventsCommitLogPayloadContext)
{
// Set Business Events properties
BusinessEventsCommitLogPayloadContext commitLogPayloadContext = _context as BusinessEventsCommitLogPayloadContext;
request = this.createHttpWebRequest(byteArrayPayload.get_Length(),commitLogPayloadContext.parmBusinessEventId());
}
else
{
BusinessEventsContract businessContract = FormJsonSerializer::deserializeObject(className2Id(classStr(BusinessEventsContractWithOnlyBaseFields)),_payload);
request = this.createHttpWebRequest(byteArrayPayload.get_Length(),businessContract.parmBusinessEventId());
}
int responseStatusCode = -1; //default to something obviously not a real status code
// send out the payload
using (System.IO.Stream dataStream = request.GetRequestStream())
{
dataStream.Write(byteArrayPayload, 0, byteArrayPayload.Length);
}
// request.GetResponse() may already result in an error if the request was e.g. a Bad Request(Status Code 400). This should be handled upstream via our global error handling.
using (HttpWebResponse response = request.GetResponse())
{
responseStatusCode = response.StatusCode;
}
// Narrowing it down. We support only response codes in the 200 range for the standard adapter. Redirects are not supported.
// Whereas some failures are handled via GetResponse() some other issues may not be. E.g. certain redirect codes where the target doesn't exist anymore.
if (responseStatusCode < 200 || responseStatusCode >= 300)
{
throw warning(strFmt("@BusinessEvents:BusinessEventsHttpResponseNotOK", classStr(BusinessEventsHttpAdapter), int2Str(responseStatusCode)));
}
}
/// <summary>
/// creates http web request
/// </summary>
/// <param name = "_contentLength">content length</param>
/// <param name = "businessEventId">Business EventId</param>
/// <returns>Http request</returns>
internal HttpWebRequest createHttpWebRequest(int _contentLength,BusinessEventsId businessEventId)
{
KTBusinessEventsHeaderDefinitionTable businessEventHeaders;
// create the request and set method, content type, and length
HttpWebRequest request = WebRequest::Create(url);
request.Method = 'POST';
request.ContentType = 'application/json';
request.ContentLength = _contentLength;
request.timeout = BusinessEventsParameters::getHttpTimeoutInMilliseconds();
// set RunId header on request
WebHeaderCollection headerCollection = request.Headers;
str runId = guid2Str(getCurrentThreadActivityId());
headerCollection.Set('x-ms-workflow-run-id', runId);
if(businessEventId == classStr(BusinessEventsTestEndpointContract))
{
businessEventId = bussinessEventsId;
}
// LogicApps logs this header which can then be used for correlation.
while select businessEventHeaders where businessEventHeaders.BusinessEventId == businessEventId
{
headerCollection.Set(businessEventHeaders.BusinessEventHeaderKey,businessEventHeaders.BusinessEventHeaderValue);
}
return request;
}
public boolean isTransient(System.Exception _exception)
{
if (_exception is System.ObjectDisposedException)
{
return true;
}
else if (_exception is System.IO.IOException)
{
return true;
}
else if (_exception is WebException)
{
WebException webException = _exception as WebException;
HttpWebResponse response = webException.Response;
if (webException.Message.Contains('The operation has timed out') ||
(response != null &&
(response.StatusCode == HttpStatusCode::ServiceUnavailable || response.StatusCode == 429)))
{
return true;
}
}
return false;
}
}
---------------------------------------------------------------
/// <summary>
/// Extension class for Business Event Endpoint Configuration for Custom Endpoint Type
/// </summary>
[ExtensionOf(formStr(BusinessEventsEndpointConfiguration))]
final class BTBusinessEventsEndpointConfiguration_Extension
{
/// <summary>
/// Method to get the concentrate Type Details
/// </summary>
/// <param name = "_endpointType">The Endpoint Object</param>
/// <returns>returns the Table that need to be displayed</returns>
public TableName getConcreteTableType(BusinessEventsEndpointType _endpointType)
{
TableName tableName = next getConcreteTableType(_endpointType);
if (_endpointType == BusinessEventsEndpointType::HttpsWithHeaders)
{
tableName = tableStr(BusinessEventsAzureEndpoint);
}
return tableName;
}
/// <summary>
/// Method to display Other Fields durin Custom Endpoint Type
/// </summary>
public void showOtherFields()
{
next showOtherFields();
this.control(this.controlId(formControlStr(BusinessEventsEndpointConfiguration,
HTTPWithHeadersEndpointFields))).visible(false);
BusinessEventsEndpointType selection =
any2Enum(EndpointTypeSelection.selection());
if (selection == BusinessEventsEndpointType::HttpsWithHeaders)
{
this.control(this.controlId(formControlStr(BusinessEventsEndpointConfiguration,
AzureEndpointFields))).visible(true);
this.control(this.controlId(formControlStr(BusinessEventsEndpointConfiguration,
HTTPWithHeadersEndpointFields))).visible(true);
}
}
}
---------------------------------------------------------------------------------
Extend the enum to add new additional element
BusinessEventsEndpointType.BTextension
--Add new Table(BTBusinessEventsHeaderDefinitionTable) form, menu item and menu, privilages
add fields on table
1.Header key
2.Header value,
3.BusinessEventId field and lookup
4.add relation BusinessEventTable and link the field BusinessEventId
on the form design header value control - set password style- YES
--------------------------------------------------------------------------------
Extend the table BusinessEventsEndpoint.BTextension
and add BTBusinessEventId field
and add Relation with BusinessEventsTable to link with business event id
Sunday, 24 November 2024
Add field in GeneralJournalAccountEntry in D365 FO
final class LedgerVoucherTransObject_Extension
{
public str customField;
/// <summary>
/// Gets or sets the <c>customField</c> parameter.
/// </summary>
/// <param name = "_customField">The value to set</param>
/// <returns>The value of the <c>customField</c> parameter</returns>
public str parmCustomField(str _customField= customField)
{
generalJournalAccountEntry.customField = _customField;
return generalJournalAccountEntry.customField;
}
public LedgerPostingTransactionTmp getLedgerPostingTransaction()
{
LedgerPostingTransactionTmp ledgerPostingTransaction;
ledgerPostingTransaction = next getLedgerPostingTransaction();
ledgerPostingTransaction.CustomField = generalJournalAccountEntry.CustomField;
return ledgerPostingTransaction;
}
public void initFromLedgerPostingTransaction(LedgerPostingTransactionTmp _ledgerPostingTransaction, LedgerPostingTransactionProjectTmp _projectPostingTransaction)
{
next initFromLedgerPostingTransaction(_ledgerPostingTransaction, _projectPostingTransaction);
generalJournalAccountEntry.CustomField = _ledgerPostingTransaction.CustomField;
}
public static LedgerVoucherTransObject newTransLedgerJournal(
LedgerJournalTrans _ledgerJournalTrans,
TaxAmount _taxAmount,
boolean _bridging,
container _intercompanyRecIds,
boolean _reversalsMayExist,
boolean _forcedExchangeRate)
{
LedgerVoucherTransObject ledgerVoucherTransObject;
ledgerVoucherTransObject = next newTransLedgerJournal(_ledgerJournalTrans, _taxAmount, _bridging, _intercompanyRecIds, _reversalsMayExist, _forcedExchangeRate);
ledgerVoucherTransObject.parmCustomField(_ledgerJournalTrans.CustomField);
return ledgerVoucherTransObject;
}
}
--------------------------------------------------------------------------------------------------------------
if we want to pass from different Journals or vouchers
/// <summary>
/// extension of LedgerVoucherObject
/// </summary>
[ExtensionOf(classStr(LedgerVoucherObject))]
final class BTC_LedgerVoucherObject_Extension
{
private InventTransId inventTransId;
/// <summary>
/// This method is sued to get invent journal trans
/// </summary>
/// <param name = "_inventTransId">InventTransId</param>
/// <returns>InventTransId</returns>
public InventTransId parmInventJournalTrans(InventTransId _inventTransId = inventTransId)
{
inventTransId = _inventTransId;
return inventTransId;
}
}
final class BTC_InventJournalCheckPost_Movement_C_Extension
{
/// This method is used to postTransLedger
/// </summary>
/// <param name = "_journalTransData">JournalTransData</param>
/// <param name = "_ledgerVoucher">LedgerVoucher</param>
protected void postTransLedger(
JournalTransData _journalTransData,
LedgerVoucher _ledgerVoucher)
{
InventJournalTrans inventJournalTrans = _journalTransData.journalTrans();
if (inventJournalTrans.inventJournalTable().JournalType == InventJournalType::Movement)
{
_ledgerVoucher.findLedgerVoucherObject().parmInventJournalTrans(inventJournalTrans.InventTransId);
}
next postTransLedger(_journalTransData, _ledgerVoucher);
}
/// This method is used to call end of posting the journal
/// </summary>
/// <param name = "_deleteErrors">boolean</param>
/// <returns>Integer</returns>
protected Integer runEnd(boolean _deleteErrors)
{
int ret = next runEnd(_deleteErrors);
if (journalCheckPostType != JournalCheckPostType::Check
&& journalTableData.journalTable().Posted)
{
InventJournalTable inventJournalTable = journalTableData.journalTable();
if (inventJournalTable.JournalType == InventJournalType::Movement)
{
//Add logic for addtional changes
}
}
return ret;
}
Clean up jobs in D365FO
https://anithasantosh.wordpress.com/2024/11/07/clean-up-jobs-in-d365fo/
-
The general journal can be created using X++ code. This code which I am sharing in this article helps you to create general journal ...
-
Upgrade to Finance and Operations: https://docs.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/migration-up...
-
Added new fields to CustAgingReportTmp public class DDDCustAgingReportController extends SrsReportRunController implements BatchRetryable ...