Showing posts with label Development. Show all posts
Showing posts with label Development. Show all posts

Microsoft announced the deprecation of Visual Studio 2022 for X++ development starting with Platform Update 74 (10.0.49)

Microsoft announced the deprecation of Visual Studio 2022 for X++ development.

Starting with Platform Update 74 (10.0.49), Visual Studio 2022 is no longer supported for X++ development. The only supported IDE is Visual Studio 2026.

The general availability of Platform Update 74 (10.0.49) will be in September 2026, so there is plenty of time to prepare a migration plan and get ready for the new update.

Table synchronization failure in Dynamics 365 Finance & Operations due to the "SaveContent" field property.

Once, I experienced a database synchronization issue: "Table Sync Failed for Table: XXXXX. Exception: System.InvalidOperationException: Database execution failed: Column name 'XXXXXX' does not exist in the target table or view." 

I had checked the table structure in my local DevBox and the mentioned field was present in the table. I checked the table structure on my local DevBox, and the mentioned field was present. When I checked the field's properties, I recognized that it has the "SaveContent" property set to "No" and is included in one of the table's indexes.

I set the "SaveContent" property set to "Yes" and performed a compilation and database synchronization. After that, the error "Table Sync Failed for Table: XXXXX. Exception: System.InvalidOperationException: Database execution failed: Column name 'XXXXXX' does not exist in the target table or view." disappeared.

Set-based operations in Dynamics 365 Dynamics 365 Finance and Operations. Cannot insert the value NULL into column 'Name', table 'TableName'; column does not allow nulls. UPDATE/INSERT fails.

When a set-based operation is used (e.g., update_recordset or insert_recordset), and an error occurs with the message "Cannot insert the value NULL into column 'Name', table 'TableName'; the column does not allow NULLs. UPDATE/INSERT fails", it means that no corresponding record was found in the joined table, so a null value was fetched and tried to be inserted into the table's column. 

It can happen if values are obtained from outer-joined data sources when there is no corresponding record in the outer-joined table, and thus a null value is retrieved.

Another case is when values are obtained from views, and those views contain calculated columns whose values can be "empty".

The solution can be using "inner join" with update_recordset and insert_recordset commands, or by using a standard "while select" clause with a view that has calculated columns.

For example, if the view is used and the code below throws the mentioned error: "Cannot insert the value NULL into column 'Name', table 'TableName'; the column does not allow NULLs. UPDATE fails"

TableForecast     tableForecast;

OperationSumView  operationSumView;

update_recordset tableForecast

    setting ItemId    = operationSumView.ItemId,
            ItemName  = operationSumView.ItemName
join ItemId, ItemName from operationSumView
    where operationSumView.RefRecId == tableForecast.RecId;

It is an example of how it can be changed to avoid the error.

while select forupdate tableForecast

join ItemId, ItemName from operationSumView
   where operationSumView.RefRecId == tableForecast.RecId
{
   tableForecast.ItemId    = operationSumView.ItemId;
   tableForecast.ItemName  = operationSumView.ItemName;
   tableForecast.update();
}

Dynamics 365 Finance and Operations: How to overwrite system fields.

In some cases, we need to modify the values of system fields. Below is a code example of how to do this. The key points are the "OverwriteSystemFieldsPermission" and "overwriteSystemFields" commands:

public void insert()

{
   // Assert and enable the system overwrite permission and metadata property.
   new OverwriteSystemfieldsPermission().assert();

   this.overwriteSystemfields(true);

   this.setAuditFieldsAnonymous();

   super();

   // Revert and disable the system overwrite permission and metadata property.
   this.overwriteSystemfields(false);
   CodeAccessPermission::revertAssert();
}

public void update()

{
   // Assert and enable the system overwrite permission and metadata property.
   new OverwriteSystemfieldsPermission().assert();

   this.overwriteSystemfields(true);
   this.setAuditFieldsAnonymous();
     
   super();
 
   // Revert and disable the system overwrite permission and metadata property.
   this.overwriteSystemfields(false);
   CodeAccessPermission::revertAssert();
}

public void setAuditFieldsAnonymous()

{
   utcdatetime  now = DateTimeUtil::utcNow();
   SysDictField createdByField;
   SysDictField createdDateTimeField;
   SysDictField modifiedByField;
   SysDictField modifiedDateTimeField;

   createdByField = new SysDictField(tableNum(Table), 

                                    fieldNum(Table, CreatedBy));
   createdDateTimeField = new SysDictField(tableNum(Table), 
                                           fieldNum(Table, CreatedDateTime));
   modifiedByField = new SysDictField(tableNum(Table), 
                                      fieldNum(Table, ModifiedBy));
   modifiedDateTimeField = new SysDictField(tableNum(Table), 
                                            fieldNum(Table, ModifiedDateTime));
           

   if (createdByField.isSQL())

   {
      this.(fieldNum(Table, CreatedBy)) = '';
   }

 

   if (modifiedByField.isSQL())
   {
      this.(fieldNum(Table, ModifiedBy)) = '';
   }

 

   if (modifiedDateTimeField.isSql())
   {
      this.(fieldNum(Table, ModifiedDateTime)) = now;
   }

 

   if (this.RecId == 0 && createdDateTimeField.isSql())
   {
      // CreatedDateTime is only set on insert.
      this.(fieldNum(Table, createdDateTime)) = now;
   }
}

D365 SysOperation framework. The "SysOperationJournaledParameters" attribute. Adding a batch job as a subtask to the main batch job.

We have a class that is implemented using the SysOperation Framework and needs to be added to the "Batch task" form (System Administration > Batch > View Task) so that we can define execution parameters. For example, we need to set dependencies, sub-tasks or the execution sequence between batch jobs. In this case, we need to use the "SysOperationJournaledParameters" attribute.

I found an example in the "Automatic release to warehouse" operation and adjusted it to my needs. I made the following modifications: 

1. Added the "SysOperationJournaledParameters" attribute to the controller class:

[SysOperationJournaledParameters(true)]
public class MyControllerClass extends SysOperationServiceController
{
}

2. Added the service class and its methods to the "construct" method:

public static SysOperationController construct(Args _args)
{
    SysOperationController controller = new MyControllerClass(
                                                                            classstr(MyServiceClass),
                                                                            methodstr(MyServiceClass, process),
                                                                            SysOperationExecutionMode::Synchronous);
    controller.parmArgs(_args);
    controller.parmDialogCaption(MyControllerClass::description());
 
    return controller;
}

Dynamics 365 Finance and Operations Unified Developer Experience (UDE) environment. How to perform an IIS reset.

The fastest way to restart AOS Service in Unified Developer Experience (UDE) environment is to Start/Stop FO Online Debugger in Visual Studio.

I figured it out based on my experience, but later, I found some useful tips here: https://learn.microsoft.com/en-us/power-platform/developer/unified-experience/finance-operations-faq#stopping-debugging-restarts-the-runtime

Dynamics 365 Finance and Operations Unified Developer Experience (UDE) environment is unavailable. Error 502.

In some cases, the Dynamics 365 Finance and Operations Unified Developer Experience (UDE) environment may not be available, and returns:

Error 502
Gateway Request Id: be5bdd0b-0e8c-4479-bd57-a91a61123c56
Request Affinity: AOS1

Based on my experience, there are several options to fix this error:

  • Restart the services by switching the environment to Administration mode and then switching it back.
  • Deploy a model when this happens.
  • Wait for some time and it will come back online automatically.

D365 Finance and Operations. SysOperation Framework and Data Contracts.

Overview

The data contract is a class that is the parameter for the entry point to the class that performs the process. The SysOperation framework automatically creates a dialog from the data contract, which will use the EDTs in the contract to provide the label, and even drop-down lists based on the table reference on the EDT.

User interface

You can associate the data contract with the user dialog with the SysOperationContractProcessingAttribute

[
DataContractAttribute,
SysOperationAlwaysInitializeAttribute,
SysOperationContractProcessingAttribute(classStr(CustRecurrenceInvoiceUIBuilder))
]
class CustRecurrenceInvoiceDataContract implements SysOperationInitializable,SysOperationValidatable

or with the SysOperationContractProcessing

[
DataContractAttribute,
SysOperationAlwaysInitializeAttribute,
SysOperationContractProcessing(classstr(CustRecurrenceInvoiceUIBuilder)
]

When you add the parm methods to the contract class the field will be added to the user dialog.

[
DataMemberAttribute,
SysOperationLabelAttribute(literalstr("@SYS318853")),
SysOperationHelpTextAttribute(literalstr("@SYS318854")),
SysOperationDisplayOrderAttribute('1')
]
public TransDate parmFromDate(TransDate _fromDate = fromDate)
{
    fromDate = _fromDate;
    return fromDate;
}

[
DataMemberAttribute,
SysOperationGroupMemberAttribute(identifierStr(Statements)),
SysOperationControlVisibilityAttribute(false)
]
public RefRecID parmWorkOrderLineRecID(RefRecID   _lineRecID = lineRecID)
{
    lineRecID = _lineRecID; 
    return lineRecID;
}

From a technical perspective, data-contracts can implement different interface classes.

Class interfaces

SysOperationInitializable 

The main goal of this interface is to implement the contract initialization. If you want to initialize the default parameters in run time before the UI interface is displayed to the user, you need to implement the SysOperationInitializable class.

class EAMWorkOrderAddNoteDescriptionContract implements SysOperationInitializable

In this case method "initialize" will be available and can be rewritten. This method is used to initialize variables within the data contract. However, this method is called if no user usage data is found.

public void initialize()
{
    this.parmFromDate(DateTimeUtil::getSystemDate());
}

In case the method "initialize" must be called at any time you have to add the SysOperationAlwaysInitializeAttribute to your contract.

[
DataContractAttribute,
SysOperationAlwaysInitializeAttribute,
SysOperationContractProcessingAttribute(classStr(CustRecurrenceInvoiceUIBuilder))
]
class CustRecurrenceInvoiceDataContract

In addition, if you want to clean up the field value on the dialog, it is required to add the same SysOperationAlwaysInitializeAttribute attribute to the desired method:

[
DataMemberAttribute,
SysOperationAlwaysInitializeAttribute,
SysOperationLabelAttribute(literalstr("@SYS318853")),
SysOperationHelpTextAttribute(literalstr("@SYS318854")),
SysOperationDisplayOrderAttribute('1')
]
public TransDate parmFromDate(TransDate _fromDate = fromDate)
{
    fromDate = _fromDate;
    return fromDate;
}


SysOperationValidatable

The main goal of the SysOperationValidatable interface is to implement the contract validation. It allows you to validate the values provided for the data contract. For example, when users enter the field value on the dialog and press the “OK” button the "validate" method of the data contract will be called.

From a code perspective, its implementation looks like:

class CustRecurrenceInvoiceDataContract implements SysOperationValidatable

In addition, you heed to override the "validate" method:

/// <summary>
/// Determines whether the parameters are valid.
/// </summary>
/// <returns>
/// True when the parameters are valid; otherwise, false.
/// </returns>
public boolean validate()
{
    boolean ok = true;

    // add your validation here

    return ok;
}


SysPackable

The SysPackable interface implementation forces the data contract class to create the "pack" and "unpack" methods which means that we can do serialization on it, like pack and unpack the variables defined in the #CurrentList for the #CurrentVersion macro. 

class EAMWorkOrderAddNoteDescriptionContract implements SysPackable
{
    Int   dummy;

    #define.CurrentVersion(1)
    #localmacro.CurrentList
        dummy
    #endmacro
}

public container pack()
{
    return [#CurrentVersion, #CurrentList];
}

public boolean unpack(container _packedClass)
{
    Version version = RunBase::getVersion(_packedClass);
    boolean ret = true;

    switch (version)
    {
        case #CurrentVersion:
            [version, #CurrentList] = _packedClass;
            break;

        default:
            ret = false;
    }

    return ret;
}

Tier-1(CHE) and Unified Developer Experience (UDE) environments build error: Another build is in progress.

In some cases you may face an issue with a model or project build in a Tier-1 environment.The error can be "another x++ build is currently running" or "another build is in progress". 

I guess, advice on how to solve this problem can be easily found on the Internet. For example, you can restart your virtual machine or kill the build process (xppcAgent) manually and try again. 

I believe it would be great to know the reason for this issue. In Visual Studio 2022, there is a setting called "Build Modules in Parallel". If it is enabled, you might constantly face the issue mentioned above.

So, it makes sense to check and deactivate this parameter in order to increase build stability in Tier-1 or Unified Developer Experience (UDE) environments.



D365 SCM warehouse mobile device development approach. Macros or constants.

As you might know there are a lot of controls and related macro commands in the WHS classes. It is not really convenient to search for macro commands in code, since they are not supported by the cross-reference feature.

In D365 Finance and Operations, there is an option that can help simplify the development of warehouse mobile flows and tracking using the existing commands. It is constants.

In my opinion, the best example of the mentioned option is the "ProcessGuideDataTypeNames" class. Inside the class you can find a lot of constants that are used in the mobile device flows. 

As you can see, macros are used at the class declaration level, but with a specific reference to a value in the WHSRF macro. As a result, you can use cross-references to find all the places in the code where the constants are used.

In general, constants have the following advantages over macros:
  • You can add a documentation comment to a constant but not to the value of a macro. Eventually, the language service will pick up this comment and provide a useful information to the user.
  • A constant is known by IntelliSense.
  • A constant is cross-referenced. Therefore, you can find all references for a specific constant but not for a macro.
  • A constant is subject to access modifiers. You can use the private, protected, and public modifiers. The accessibility of macros isn't rigorously defined.
  • Constant variables have scope, whereas macros don't have scope.
  • You can see the value of a constant or a read-only variable in the debugger.
  • Full control of the type for the constant.
  • You can also define constants as variables only. The compiler will maintain the invariant so that the value can't be modified.
  • A significant effect on the performance of the compiler.
Considering all the above, I would recommend you to use constants instead of macros in general, not only in WHS mobile device flow development.

How to run batch tasks using the SysOperation framework

Overview

As you may know, the system has batch tasks functionality. It can be used to create a chain of operations if you want to set an order of tasks execution according to certain criteria.

Issue description


When you create a new batch tasks you have to specify the "Class name" field. If you know the class name in advance you can put it directly and not wait for the lookup list. Theoretically not all classes are supposed to be used with the mentioned feature. In this casse, the following warning message will appear: 
"The specified class is not fully designed for use in the Batch job form. Execution might provide unexpected results."

If you ignore this message and try to define the class parameters you will see the error: 
"Access denied: Class name"

If we use the "RunBaseBatch" framework it is required to override the method "canGoBatchJournal" and set its return value to "true".

If you use the new SysOperation framework it is required to decorate the controlled class with the 
"[SysOperationJournaledParametersAttribute(true)]". 

[SysOperationJournaledParameters(true)]

class WHSReleaseOutboundShipmentOrderToWarehouseController

If you add this command the issue with the warning message "The specified class is not fully designed for use in the Batch job form. Execution might provide unexpected results." will be solved.

But if you try to set the parameters you will still see the error: "Access denied: Class name".

In order to solve it, it is requerd to add to the "new" method a reference to the service class and method. You can take a look at the "WHSReleaseOutboundShipmentOrderToWarehouseController" class to get an idea of the pattern.

void new(

    IdentifierName _className  = '',
    IdentifierName _methodName = '',
    SysOperationExecutionMode _executionMode = SysOperationExecutionMode::Synchronous)
{
    IdentifierName parmClassName    = _className != '' ? _className :                 
            classStr(WHSReleaseOutboundShipmentOrderToWarehouseService);

    IdentifierName parmMethodName = _methodName != '' ? _methodName : 
        methodStr(WHSReleaseOutboundShipmentOrderToWarehouseService, 
            autoReleaseOutboundShipmentOrders);

    super(parmClassName, parmMethodName, _executionMode);

    this.parmDialogCaption(
        WHSReleaseOutboundShipmentOrderToWarehouseController::description());
}

When the "new" method is executed, the framework looks at the "run" method and determines the contract based on the input parameters. The call to "startOperation" method causes the system to build the user interface and execute code based on the user’s actions. A dialog is created using data types specified in data contract member methods. This explains why the new method requires class and method names. The third parameter specifies the execution mode, which takes effect if we programmatically execute the controller. Synchronous execution runs in line with the current process unless the user chooses to run it in the background.

So the reason for the error "Access denied: Class name" was that the service class names and method names were not set by default in the "new" method. As a result, the default UI builder class did not have a reference to the contract class, so parameters could not be set for batch tasks.

In case you experience other challenges with the SysOperation batch tasks feature you can use "WHSReleaseOutboundShipmentOrderToWarehouse*" classes to guide you.

Dynamics 365 Finance and Operations. New development features in version 10.0.40 PU64.

In version 10.0.40 PU64 new features for developers have been introduced.

First of all, it is possible to add modified objects to the current project automatically. It means no more worries about adding objects to the right project. The modified objects will be added automatically. In order to enable this feature, the "Add files to solution on save" option should be enabled.


The second feature is related to the building process. It is possible to select the label file to be built.



All label files will be compiled by default. If you would like to build only labels or speed up the building process you can use this option.

Microsoft announced the deprecation of Visual Studio 2022 for X++ development starting with Platform Update 74 (10.0.49)

Microsoft announced the deprecation of Visual Studio 2022 for X++ development. Starting with Platform Update 74 (10.0.49), Visual Studio 20...