Merge entity duplicity

In the case of duplicity in Customers, Vendors or Contacts, the system offers functionality to merge related record. To run it, go to the customer/vendor/contact card and run action “Merge With…”.

The setup window is opened for the entity from which the page was started. In the header, the opposite entity with which we want to merge has to be chosen, then the system lookup all relevant data and show them in the tab below.

In this tab, the user can see all conflict between the source entity and the selected one. If you want to save the value of any field from the selected entity, choose “Override”; otherwise, the value from the source entity will be used.

The last tab, “Related Tables”, shows all tables where the source or selected entity were found. The most important information is whether there are any conflicts in the column “Conflicts”. If there are any problems, in the next step it will be necessary to resolve them before the process of merging could be finished.

Whether there are any conflicts or not, continue with the action “Merge”. If there are some conflicts, resolve them and then run the “Merge” again. If there aren’t any, just confirm the merging process.

Dart & Flutter: Project Initialization

We have already prepared development environment and Android Emulators in previous articles. In this chapter, we will look at how to create a new project and on the generated files.

To start with, open Visual Studio Code (or any other support development environment) and run the command “Flutter: New Project”. Once we have created a new project, we should see something simulator to the picture below.

There are many files in the root directory in the newly generated project. Some of the files are generally known (like .gitignore, I will skip them) some are specific to Flutter.

Subdirectories

  • android
    • This directory contains all the Android related files. The most important things to know is that in this directory (directories), the resources like icons or images are stored.
    • From the configuration files, the most important is “AndroidManifest.xml” that specifies everything related just to Android (for example system permissions that to the app requires to run)
  • ios
    • Similar to the android directory. As these articles are focus on the Android app, I will skip the directory.
  • lib
    • The main folder where we should write the application code. There are some patterns of how the files should be structured in this directory on which we will look in some of the next articles.
  • test
    • This folder is used to store and manage testing code for the application.

Files in the root directory

  • .metadata
    • This file tracks properties of the Flutter project and should not be changed manually.
  • .packages
    • This file is generated automatically by Pub (Flutter package manager), and like the previous file, it should not be changed manually.
  • pubspec.lock
    • Similar file to .packages, used by Pub to get the concrete versions for every used package or dependency.
  • pubspec.yaml
    • This is probably the only file we will sometimes change in the future. The file specifies all flutter packages we have available. If we need to add a new third-party package (for example HTTP), we should do it in this file.
  • tka_demo.iml
    • Auto-Generated file specifying internal project properties.

main.dart file

 import 'package:flutter/material.dart';

 void main() {
   runApp(MyApp());
 }

 class MyApp extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
     return MaterialApp(
       title: 'Flutter Demo',
       theme: ThemeData(
         primarySwatch: Colors.blue,
         visualDensity: VisualDensity.adaptivePlatformDensity,
       ),
       home: MyHomePage(title: 'Flutter Demo Home Page'),
     );
   }
 }

 class MyHomePage extends StatefulWidget {
   MyHomePage({Key key, this.title}) : super(key: key);

   final String title;

   @override
   _MyHomePageState createState() => _MyHomePageState();
 }

 class _MyHomePageState extends State<MyHomePage> {
   int _counter = 0;

   void _incrementCounter() {
     setState(() {
       _counter++;
     });
   }

   @override
   Widget build(BuildContext context) {
     return Scaffold(
       appBar: AppBar(
         title: Text(widget.title),
       ),
       body: Center(
         child: Column(
           mainAxisAlignment: MainAxisAlignment.center,
           children: <Widget>[
             Text(
               'You have pushed the button this many times:',
             ),
             Text(
               '$_counter',
               style: Theme.of(context).textTheme.headline4,
             ),
           ],
         ),
       ),
       floatingActionButton: FloatingActionButton(
         onPressed: _incrementCounter,
         tooltip: 'Increment',
         child: Icon(Icons.add),
       ),
     );
   }
 }

Interfaces in AL (part 2)

This is 2. part from the article series about Interfaces in AL Language. See the first one.

In the first part of this series, I have described what Interfaces are and showed the example of how they can be created. In this article, I will show a more advanced example of interfaces and how we can handle with these structure within Visual Studio Code.

Snippets

As usual, Microsoft added shortcut (snippet) to help with creating a basic layout of the interface object. The snippet is called “tinterface”.

I also already mentioned that I prefer (and strongly recommend) snippets from Visual Studio Code plugin “waldo’s CRS AL Language Extension” created by Waldo. However, in this case, there is nothing special from the snippet provided by Microsoft.


 // tinterface
 interface MyInterface
 {
    procedure MyProcedure();
 }

 // tinterfacewaldo
 interface IMyInterface
 {
    procedure MyProcedure();
 }

Example

Now we can look at the example. Our goal is to create functionality to import and process different types of machine inspection files. The files are of two types – BLB and XXS. Both have a completely different structure, and also data that provide are different, so they need to be processed differently.

In the old approach, we would have added if/case statement on every place where we need a different behaviour. So if we had to add another format later, we would have to change the code on these places once again. Not anymore with interfaces!

So we will create a new object of type Interface. This is the only type where we do not assign object ID.

 interface "TKA Inspection Format"
 {
    procedure LoadFile(FilePath: Text);
    procedure ProcessFile(InspectionFile: Record "TKA Inspection File"): Boolean;
 }

Now, we create Codeunit that implements our interface. To define which interface we are implementing, we use construction “Codeunit ID OBJECTNAME implement INTERFACE 1, INTERFACE 2, …”

We can see that the interface name is underlined and error messages are shown. As we use an interface, we have to define (implement…) every method from this interface(s). That can be done using “Quick Fixes” – a yellow light bulb or using a combination of Ctrl + .

 codeunit 50100 "TKA BLB Inspec. Format Mgnt." implements "TKA Inspection Format"
 {
    procedure LoadFile(FilePath: Text)
    begin
    end;
    procedure ProcessFile(InspectionFile: Record "TKA Inspection File"): Boolean
    begin
    end;
 }

Now we have a basic structure of the Codeunit that implements our interface. We can add some functionality and create another one in the same way with different functionality.

 codeunit 50100 "TKA BLB Inspec. Format Mgnt." implements "TKA Inspection Format"
 {
    procedure LoadFile(FilePath: Text)
    begin
        ImportFileFromXML(FilePath);
    end;
    procedure ProcessFile(InspectionFile: Record "TKA Inspection File"): Boolean
    begin
        DoSomething();
        DoTask1();
        exit(IsOk());
    end;
 }
 codeunit 50101 "TKA XXS Inspec. Format Mgnt." implements "TKA Inspection Format"
 {
    procedure LoadFile(FilePath: Text)
    var
        TKAFileManagement: Codeunit "TKA File Management";
    begin
        TKAFileManagement.DecryptFile(FilePath);
        while TKAFileManagement.HasNextLine() do  
            TKAFileManagement.ImportDecryptedLine(FilePath);
    end;
    procedure ProcessFile(InspectionFile: Record "TKA Inspection File"): Boolean
    begin
        DoSomethingDifferent();
        if HasError() then
            exit(false);

        DoFinalThing();
        exit(true);
    end;
 }

And finally, how to benefit from the interface. When we need to call any of the methods in the interface, we just need to define which implementation of the interface we are using.

 codeunit 50102 "TKA Some Settings"
 {
    procedure TKAInspectionFormatFactory(var TKAInspectionFile: Interface "TKA Inspection Format")
    begin
        // Get specific Implementation based on settings (for example)

        if Something() then
            TKAInspectionFile := TKABLBInspecFormatMgnt;
        else
            TKAInspectionFile := TKAXXSInspecFormatMgnt;
    end;
 }

 codeunit 50101 "TKA Some Mgnt. Object"
 {
    procedure ImportAndProcessFile()
    var
        TKAInspectionFile: Record "TKA Inspection File"

        TKAInspectionFormat: Interface "TKA Inspection Format";
        FilePath: Text;
    begin
        ...
        TKAInspectionFormatFactory(TKAInspectionFormat);
        if TKAInspectionFormat.LoadFile(FilePath) then begin
            ...
            TKAInspectionFormat.ProcessFile(TKAInspectionFile);
        end;
        ...
    end;
 }

Now we have a solution that requires only one change in method TKAInspectionFormatFactory if we need to add new supported file format.

In the next part, I will explain how to use Interface together with Enums – that is the most universal usage for an interface that is currently available in Business Central.

Microsoft Dynamics 365 Business Central 2020 Wave 1 has been released!

The new version of Microsoft Dynamics 365 Business Central has just been released! Version is called 2020 Wave 1 and its internal number is 17.0.

I already described some of the most anticipated functions, and I probably describe some other in the upcoming weeks. However, the full list of major changes can be found directly on Microsoft Docs.

Interfaces in AL (part 1)

With Microsoft Dynamics 365 Business Central 2020 Wave 1 (respectively with Runtime 5.0) a new object type has been introduced – Interfaces. This term is well known from other programming languages and probably a lot of readers have already used them in some way.

The general definition of the interface object could be found anywhere on the Internet. For example, the definition on Wikipedia:

The term interface is used to define an abstract type that contains no data but defines behaviours as method signatures. A class having code and data for all the methods corresponding to that interface and declaring so is said to implement that interface.

An interface is thus a type definition; anywhere an object can be exchanged (for example, in a function or method call) the type of the object to be exchanged can be defined in terms of one of its implemented interfaces or base-classes rather than specifying the specific class. This approach means that any class that implements that interface can be used.

Usually a method defined in an interface contains no code and thus cannot itself be called; it must be implemented by non-abstract code to be run when it is invoked.

https://en.wikipedia.org/wiki/Interface_(computing)

Okay, but what does it mean in term of programming in AL language? The interface described in the quotation above is the new special object type “Interface” and the “non-abstract class” is, in our case, Codeunit object with a special construction that defines that the Codeunites implements an Interface.

 interface "TKA Our Interface Object"
 {
    procedure MyProcedureDefinedInTheInterfaceObject();
 }

 codeunit 50001 "TKA Our Codeunit Object" implements "TKA Our Interface Object"
 {
    procedure MyProcedureDefinedInTheInterfaceObject();
    begin
        // TODO Implementation of procedure MyProcedureDefinedInTheInterfaceObject
    end;
 }

In the next part of this article series, I will describe Interfaces on the more advanced example.