Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Extending Microsoft Dynamics 365 Finance and Supply Chain Management Cookbook

You're reading from   Extending Microsoft Dynamics 365 Finance and Supply Chain Management Cookbook Create and extend secure and scalable ERP solutions to improve business processes

Arrow left icon
Product type Paperback
Published in Mar 2020
Publisher Packt
ISBN-13 9781838643812
Length 534 pages
Edition 2nd Edition
Arrow right icon
Author (1):
Arrow left icon
Simon Buxton Simon Buxton
Author Profile Icon Simon Buxton
Simon Buxton
Arrow right icon
View More author details
Toc

Table of Contents (17) Chapters Close

Preface 1. Starting a New Project 2. Data Structures FREE CHAPTER 3. Creating the User Interface 4. Working with Form Logic and Frameworks 5. Application Extensibility 6. Writing for Extensibility 7. Advanced Data Handling 8. Business Events 9. Security 10. Data Management, OData, and Office 11. Consuming and Exposing Services 12. Unit Testing 13. Automated Build Management 14. Workflow Development 15. State Machines 16. Other Books You May Enjoy

Creating order header tables

Order and line tables are used whenever we need a worksheet to enter data that is later acted upon. Once they have been processed, they should no longer be required. Reports should act upon the transactions that the order created, such as inventory transactions, sales ledger transactions, invoices, and more.

Getting ready

Although we will be using the tables created earlier, this pattern can be followed with your own solution.

How to do it...

We will first create the worksheet header table, which will be a vehicle service order table:

  1. Create a new table named ConVMSVehicleServiceTable.
  2. Create a primary key EDT, ConVMSVehicleServiceId; this time, extend Num. Complete the Label and Help Text properties with appropriate labels.
  1. Drag the EDT from Solution Explorer to the Fields node of our table and rename it ServiceId.
  2. Complete the ServiceId field as an ID field: Mandatory = Yes, Allow Edit = No, and Allow Edit On Create = Yes.
  3. Complete the relation information on the ConVMSVehicleServiceId EDT.
  4. Create the primary key index as ServiceIdx with ServiceId as the only field.
  5. Set the Clustered Index and Primary Index properties as ServiceIdx.
  6. Drag the ConVMSVehicleId EDT to our table and rename it VehicleId.
  7. Make the VehicleId field mandatory set Ignore EDT relation to Yes.
The decision to make the field editable depends on the associated logic (referential integrity) and the business requirements. Ignoring the EDT relation is the best practice method, and forces us to create a relation on the table.
  1. Create a foreign key relation for ConVMSVehicleId to ConVMSVehicleTable.VehicleId. Dragging the table on to the Relations node can save some time, but this creates a normal relation and not a foreign key relation.
The cardinality should be OneMore as it is mandatory. On Delete should be Restricted on foreign key relations to main tables.
  1. Drag the Name EDT onto our table from Application Explorer.
  2. Create a new Base Enum for the service status, as defined here:
Property Value
Name ConVMSVehicleServiceStatus
Label Status (for example, @SYS36398 will suffice)
Help The service order status
Is Extensible True: remember we cannot use this for the ranking of relative comparisons (> or <) with this set
  1. Add the following elements:
Element Label
None No label so that it appears empty in the UI
Confirmed Confirmed
Complete Complete
Cancelled Cancelled
    1. Save and drag the new ConVMSVehicleServiceStatus enum to our table and rename it ServiceStatus.
    2. Make the ServiceStatus field read only. Allow Edit and Allow Edit On Create should be No. This is because Status fields should be controlled through business logic.
    3. Create the date EDTs ConVMSVehicleServiceDateReq "Requested service date" and ConVMSVehicleServiceDateConfirmed "Confirmed service date." The dates should extend TransDate. Label them appropriately and drag them to the new table.
    4. Rename the fields to ServiceDateRequested and ServiceDateConfirmed.
    5. Complete the table properties as shown here, which are common for all tables of this type:
    Property Value
    Label Vehicle service orders
    Title Field 1 ServiceId
    Title Field 2 Name
    Cache lookup Found
    Clustered Index ServiceIdx
    Primary Index ServiceIdx
    Table Group WorksheetHeader

    Created By

    Created Date

    TimeModified By

    Modified Date Time

    Yes
    Developer Documentation ConVMSVehicleServiceTable contains vehicle service order records
    Form Ref Blank until we have created the form
    1. Create the fields groups as follows:
    Group name Label Fields
    Overview Overview (@SYS9039)
    • ServiceId
    • VehicleId
    • Name
    • ServiceStatus
    Details

    Details (@SYS318405)

    You could also create a more helpful label of service details
    • ServiceId
    • VehicleId
    • Name
    • ServiceStatus
    ServiceDates Service dates
    • ServiceDateRequested
    • ServiceDateConfirmed
      1. Create the now usual Find and Exist methods using ServiceId as the key.
      2. You can also create your own validation on the service dates, using validateField. For example, check that the service dates can't be before today.
      3. We can also validate that the record itself can be saved. This introduces the validateWrite method. This is to enforce the requirement that only service orders at status confirmed or less can be changed; the method should be written as follows:
      public boolean validateWrite()
      {
      boolean ret;
      ret = super();
      ret = ret && this.CheckCanEdit();
      return ret;
      }
      public boolean CheckCanEdit()
      {
      if (!this.CanEdit())
      {
      //Service order cannot be changed.
      return checkFailed("@ConVMS:ServiceOrderCannotBeChanged");
      }
      return true;
      }
      public boolean CanEdit()
      {
      switch (this.ServiceStatus)
      {
      case ConVMSVehicleServiceStatus::None:
      case ConVMSVehicleServiceStatus::Confirmed:
      return true;
      }
      return false;
      }
      1. Finally, we will write a method that initializes the defaults from the main table record, that is, vehicle, when it is selected. Write the following two methods:
      public void InitFromVehicleTable(ConVMSVehicleTable _vehicle)
      {
      this.Name = _vehicle.Name;
      }
      public void modifiedField(FieldId _fieldId)
      {
      super(_fieldId);
      switch(_fieldId)
      {
      case fieldNum(ConVMSVehicleServiceTable, VehicleId):
      this.InitFromVehicleTable(ConVMSVehicleTable::Find(this.VehicleId));
      break;
      }
      }
      1. Save the table and close the editor tabs.

      How it works...

      There are few new concepts here. I'll start with the code structure at the end of the step list.

      The most important part of this code is that we didn't write this.ServiceStatus <= ConVMSVehicleServiceStatus::Confirmed. This is an extensible enum, and we can't be sure of the numeric value that the symbols have.

      The other part is that we have split what may seem to be a simple if statement in validateWrite into three methods. The reason is reusability. It is nicer to make a record read-only in the form than it is to throw an error when the user tries to save. So, we can use CanEdit to control whether the record is editable on the form, making all controls greyed out.

      Check methods are written to simplify the creation and maintenance of validation methods, and also to make the checks reusable, ergo consistent. Check methods are expected to return a silent true if the check passes, or to display an error should the check fail. The error is sent to the user using the checkFailed method, which does not throw an exception.

      The next method is the InitFrom style method. This is a very common technique and should always be used to initialize data from foreign tables. It may seem odd that we don't check that it exists first.

      This is deliberate. Records in SCM initialize so that all the fields are empty or zero (depending on the field type). So, if the record is not found, the values that are initialized will be made to be empty, which is desirable. Also, modifiedField occurs after the field is validated. So, the method won't be triggered should the user enter an invalid vehicle ID. If the vehicle is not mandatory, we may find the vehicle ID is empty; however, again, this is fine.

      There's more...

      The On Delete property for table relations is similar to the functionality controlled by the Delete Actions node on the table. The difference is that the Delete Action is placed on the parent table. This is a problem if the parent table is a standard table, as this is now locked for customization (over-layering). Using the On Delete property is therefore controlled in a much better location, even if the result is the same. Because of this, we should always use the same place for this, which should be the relation.

      We have the following options for both Delete Actions and the On Delete property:

      • None
      • Restricted
      • Cascade
      • Cascade + Restricted

      None has no effect, and effectively disables the delete action; this is useful if you want to specifically state "Do nothing" so someone else doesn't try to correct what seems to be an omission.

      Restricted will prevent the record from being deleted, if there are records in the related table that match the selected relation. This occurs within the validateDelete table event, which is called by the validateDelete form a data source event.

      Cascade will delete the record in the related table based on the relation; it is no use having a sales order line without a sales order. This is an extension to the delete table event.

      Cascade + Restricted is a little special. In a two-table scenario, it is the same as Restricted; it will stop the record from being deleted if a related record exists. However, if the record is being deleted as part of a cascade from a table related to it, which records will be deleted.

      You have been reading a chapter from
      Extending Microsoft Dynamics 365 Finance and Supply Chain Management Cookbook - Second Edition
      Published in: Mar 2020
      Publisher: Packt
      ISBN-13: 9781838643812
      Register for a free Packt account to unlock a world of extra content!
      A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
      Unlock this book and the full library FREE for 7 days
      Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
      Renews at $19.99/month. Cancel anytime
      Banner background image