View source | View content page | Page history | Printable version   

Projects:Copy From Orders Refactor

Contents

Copy from Orders Process Refactor.

The purpose of this document is to explain the functional and technical design followed in the refactoring of the "Copy From Order" process, to improve the performance and the user experience.

Bulbgraph.png   This feature is available starting from 3.0PR18Q1.

Introduction.

The "Copy From Order" process had some performance problems in environments with high volumes of information, as well as some functional limitations such as not being able to select more than one order at a time and having to navigate two windows to filter/select a particular order.

To cover these deficiencies and provide a more intuitive and functional interaction, this process was migrated to a new Process Definition called “Copy From Orders”. In this document, we will explain the main functional changes that were needed to fulfill these objectives and some technical topics to a better understanding of it.

This refactor cover some of these limitations explained on the design defect https://issues.openbravo.com/view.php?id=36466 and includes other functionalities that we going to explain with more detail in this document.

Design changes.

The User Interface was affected by some changes. We can say the most important are:

  1. Migration to a Pick and Edit window: The process "Copy From Order" was defined as a manual process and does not make use of the benefits of a Process Definition and Pick and Edit windows. The first thing done was to migrate part of the current process logic to a new Process Definition, using the Pick and Edit (PE) pattern in those windows that currently use it, and improves interaction with users.
  2. Allow to select more than one order at a time: With the old process only was possible to select one order at a time, which increases the amount of user interactions to add lines of several orders. This time the new process allows select more than one order at once.
  3. In the grid is possible to show and filter by the following information from the orders:

Information filtering.

With the manual process, the information was filtered by definition of the following filters: Doc No., Reference Order, Business Partner, From date, To date, Total Amount From, Total Amount To, Description.

When the Search button was pressed, then the grid was loaded with those results that matched the selected filters. Then user was able to only select a record within the grid.

Order selector of the old Copy From Order manual process

To improve the user experience, the grid was replaced by another one that allows multiple selection and the most common operations on the columns (filter, sort, reorder, etc.).

With this new structure, those defined filters in the manual process are not needed anymore and the filtering of any column can be done directly on the grid. So, the input filters were removed from the window. Also, the process is simplified to avoid an extra step before select the orders to be copied.

Copy from Orders Pick and Edit window

An important functional change included in this refactor is the fact that it is not possible to select orders that don’t belong to the tree of the legal entity of the order’s organization being copied to. This new behavior is included in 3.0PR18Q1 release.

Select orders belonging to the tree of the legal entity of the order’s organization

Additionally, if the PE is executed from the Sales Order window the grid is filtered by default with the sales orders of the legal entity tree of the order’s organization. Similar happens when the PE is called in the Purchase Order window, but in this case with purchase orders. In both cases the Sales Transaction column is filtered by Yes (Sales) or No (Purchases), and this filter can be cleared by the user to be able to see other orders not related to the transaction type of the order.

Copy from Orders Pick and Edit window

Rules followed when processing

The process copies information from the lines copied from and generate its own information according it header and environment settings. In this section, we going to do a summary of the most important rules followed when the process is executed:

At the end of the process, a message is shown with a successful message showing how many lines have been copied or with an error message if there is any error in the process.

Process shows the order lines count copied

Technical Changes

This refactor generates several changes in the database and core java classes. In this section, we are going to do a brief explanation of the most important changes.

Database changes:

Window definition

Was created a new Pick and Execute window to improve the performance in the load of the orders.

Also, was defined a new tab to it.

Defined as grid fields, similar than existing in the old “Copy From Order” manual process:

Defined a new Reference

Was created a new reference for the PE window.

And defined the window reference as:

Process Definition

Was created a new process definition:

And defined the process parameters.

Linked column to the new Process definition:

Were updated all the buttons using the "Create From Order" manual process to use the new process definition "Create From Orders". Was modified the CopyFromPO column from the C_Order table with the following information:

Java classes changes:

everal changes were done in the java classes to implements the design requirements. In this section, we will explain the created artifacts and how to extend the core’s logic by hooks.

Classes were created to separate the action handler and the processing logic, to make more easiest the understanding of the code and make the processing independent of the flows where it is used, and making possible to be used in programmatic codes like automated tests.

CopyFromOrdersActionHandler class.

The action handler class is used to execute the Copy From Orders process logic getting from parameters, the order being processed and the selected orders in the Pick and Edit window to be copied.

protected JSONObject doExecute(Map<String, Object> parameters, String content) {
    JSONObject jsonRequest = null;
    try {
      // Request Parameters
      jsonRequest = new JSONObject(content);
      final String requestedAction = getRequestedAction(jsonRequest);
      JSONArray selectedOrders = getSelectedOrders(jsonRequest);
      Order processingOrder = getProcessingOrder(jsonRequest);
 
      if (requestedActionIsDoneAndThereAreSelectedOrders(requestedAction, selectedOrders)) {
        // CopyFromOrdersProcess is instantiated using Weld so it can use Dependency Injection
        CopyFromOrdersProcess copyFromOrdersProcess = WeldUtils
            .getInstanceFromStaticBeanManager(CopyFromOrdersProcess.class);
        int createdOrderLinesCount = copyFromOrdersProcess.copyOrderLines(processingOrder,
            selectedOrders);
        jsonRequest.put(MESSAGE, getSuccessMessage(createdOrderLinesCount));
      }
    }

It is important to remarks that this solution is using Weld and can be instantiated the CopyFromOrdersProcess class using dependency injection. In the next topics, we going to explain it and the java classes structure with more details.

CopyFromOrdersProcess class.

This process copies the Order Lines of the selected Orders into the Order that is being processed and provides support for hooks.

public class CopyFromOrdersProcess {
  @Inject
  @Any
  private Instance<CopyFromOrdersProcessImplementationInterface> copyFromOrdersProcessHooks;
(...)

The copyFromOrdersProcessHooks object injects any instance of the CopyFromOrdersProcessImplementationInterface interface and executes particular logic of all of them in a sequence defined by the order field of the hook.

This logic is executed by the executeHooks() method. It iterates all injected instances of the CopyFromOrdersProcessImplementationInterface interface, sort them by the order defined on each one, and then executes each particular logic invoking the exec() method.

private void executeHooks(final OrderLine orderLine, OrderLine newOrderLine) {
    if (copyFromOrdersProcessHooks != null) {
      final List<CopyFromOrdersProcessImplementationInterface> hooks = new ArrayList<>();
      for (CopyFromOrdersProcessImplementationInterface hook : copyFromOrdersProcessHooks
          .select(new ComponentProvider.Selector(
              CopyFromOrdersProcessImplementationInterface.COPY_FROM_ORDER_PROCESS_HOOK_QUALIFIER))) {
        if (hook != null) {
          hooks.add(hook);
        }
      }
 
      Collections.sort(hooks, new CopyFromOrdersHookComparator());
      for (CopyFromOrdersProcessImplementationInterface hook : hooks) {
        hook.exec(processingOrder, orderLine, newOrderLine);
      }
    }
  }
(...)

As reference of how to implement an specific functionality in a hook, can be found as part of the classes included on Core as part of this project. For example, the process follows the following steps: It retrieves the Order Lines of each Order that are not Discounts or are Non-Stocked BOM Products and for each one:

  1. Update Order and Order Line related information (implemented on UpdateOrderLineInformation.class)
  2. Copy product and attributes (implemented on UpdateProductAndAttributes.class)
  3. Calculate amounts and UOM's. (implemented on UpdateQuantitiesAndUOMs.class)
  4. Calculate Prices based on price list (implemented on UpdatePricesAndAmounts.class)
  5. Recalculate Taxes (implemented on UpdateTax.class)

As you can see, each class implements the CopyFromOrdersProcessImplementationInterface interface and executes a concrete functionality when the process is executed.

How to extend Core’s functionality with hooks.

If you want to extend the core implementation included on this refactor with your own logic, you must take into account the following.

Add the following annotations to the class:

@Dependent
@Qualifier(CopyFromOrdersProcessImplementationInterface.COPY_FROM_ORDER_PROCESS_HOOK_QUALIFIER)

The class must implement the CopyFromOrdersProcessImplementationInterface interface:

public class NewHook implements CopyFromOrdersProcessImplementationInterface

Taking as example the UpdateTax hook implementation:

@Dependent
@Qualifier(CopyFromOrdersProcessImplementationInterface.COPY_FROM_ORDER_PROCESS_HOOK_QUALIFIER)
class UpdateTax implements CopyFromOrdersProcessImplementationInterface {
  private static final Logger log = LoggerFactory.getLogger(UpdateTax.class);
 
  @Override
  public int getOrder() {
    return -10;
  }
 
  @Override
  public void exec(final Order processingOrder, final OrderLine orderLine, OrderLine newOrderLine) {
    TaxRate tax = OBDal.getInstance().getProxy(TaxRate.class,
        getCurrentTaxId(newOrderLine.getProduct(), processingOrder));
    newOrderLine.setTax(tax);
  }

When a new hook is implemented you need to override the following two methods:

Example of a hook creation.

According to the explained above, if we want to implement a hook, for instance that updates the new order line description with the “Testing” string, and it is needed to be executed after the core actions and between other customized hooks (some with defined execution order less and greater than 10), a possible implementation of it could be:

@Dependent
@Qualifier(CopyFromOrdersProcessImplementationInterface.COPY_FROM_ORDER_PROCESS_HOOK_QUALIFIER)
   
     @Override
     public int getOrder() {
       return 10;
     }
 
     @Override
     public void exec(Order processingOrder, OrderLine orderLine, OrderLine newOrderLine) {
       newOrderLine.setDescription("Testing");
     }
}

In this example it is enough to fulfill the requirements, but it can be as simple as the logic you want to execute.

Test Cases

This project has a Test Link document associated. You can see it to more references of test cases.

https://testlink.openbravo.com/testlink/linkto.php?tprojectPrefix=Communit&item=testsuite&id=40965

Performance evaluation.

Before the migration of the process, was well known that when the old Copy From Order process was running on an environment with huge amount of orders (>=150k rows) it consumed 1.5GB of heap memory and sometimes it takes too long or even didn’t finish just to open the old selector.

This problem was automatically fixed with the use of the Copy From Orders Pick and Edit, it makes the load of the grid almost instantaneous no matter the records quantities matching the filtering criteria.

Also, the user experience was improved, the UI was simplified and improved the interaction and orders filtering.

Retrieved from "http://wiki.openbravo.com/wiki/Projects:Copy_From_Orders_Refactor"

This page has been accessed 1,611 times. This page was last modified on 28 November 2017, at 09:12. Content is available under Creative Commons Attribution-ShareAlike 2.5 Spain License.