This document provides an analysis of using version 3 extension mechanisms to address a current supply chain requirement of an OpenLMIS v2 implementation that traditionally would have led to forking in that code-base. The scenario described below was brought to the Technical Committee on October 25, 2016.
There are two organizations supplying commodities in country: MSL (ministry) and CHAZ (charity). Of these most of the commodities in a requisition will be supplied through MSL. However for a few of these commodities (e.g. 5 to 6), and only at some facilities in the country, CHAZ will actually supply those commodities. The original requisitioning facility will fill out one Requisition and submit it and have it authorized. When the district supervisor goes to approve it however, they will essentially be approving a Requisition which will be split into two: one for MSL and one for CHAZ. Each of the subsequent Requisitions will then have independent approval steps and fulfillment from the other. It would both be desirable to have a single requisitioned product be wholly or in-part fulfilled by MSL and CHAZ. It's also likely that the district supervisor will want to be able to see which line items will by fulfilled by MSL and which will be fulfilled by CHAZ.
This type of workflow introduces a number of important changes:
- A Requisition may be split into multiple Requisitions.
- The concept of a Supplying Partner is introduced to distinguish MSL from CHAZ.
- From the perspective of the original Requisitioning facility, we must distinguish that some of the Products will be fulfilled by one of the known Supplying Partners.
- For some product's that that Requisitioning facility is requesting, Supplying Partners may wish to split a single Requisitioned product so that Supplying Partners each would fulfill just a percentage of the original approved quantity.
- an Approver would want to see in the UI what quantities they we're approving based on the Supplying Partner that will fulfill them
It makes sense that a requisition, submitted by a last mile facility, may eventually need to be split into 2 or more requisitions depending on the partners in country that are all jointly fulfilling the supplying needs within a country. Splitting the requisition and moving each through separate approval mechanisms makes more sense than some potential alternatives, such as turning it into multiple orders or doing so by line item.
An overview for what this structure might look like:
In this example Clinic A is the original requisitioning facility and it initiates and submits Requisition A. Until Requisition A is approved by the District Malaria Supervision, it follows a traditional approval workflow. When approval occurs at District Malaria Supervision, Requisition A is split into independent Requisitions A1 and A2. Requisition A is essentially done, its status reflects this with the status split, and unlike the normal workflow it itself will not ever be released and converted to an order. From this point forward Requisition's A1 and A2 start in status approved and are filed on behalf of Clinic A for the original processing period at the MSL and CHAZ supervisor nodes respectively. These supervisory nodes are the start of new supervision structures that keep Requisition A1 and A2 independent and the structure configurable depending on the partner. Both Requisition A1 and A2 end when they reach the top level supervisory node in their respective structures which directs each to being converted to orders at different warehouses - just as a traditional approval workflow did.
Concepts Introduced through Extension Mechanisms
The following concepts are introduced to help achieve the scenario's desired functionality. The approach laid out here is a rough estimate and assumes we'd be starting with concepts that existed in OpenLMIS v2. It also assumes that the community process of adding extension mechanisms in the right places occurs in an expedient manner. This more closely mimics a real life scenario where an implementation approaches the community with the scenario as stated, and the community must react to add one or more of the known extension mechanisms (Independent Service, Extension Point, Extra Data, Reference UI module) in order for the implementation to achieve the desired functionality without forking the code.
In the following concepts, two actors are identified to make clear the basic roles and responsibilities to achieving the extension:
- Community developer is someone making changes to shared OpenLMIS code-base (central team or implementer)
- Implementer is an implementation that needs to extend the OpenLMIS code base for their particular scenario.
Supplying Partner - Service
A Supplying Partner entity needs to be introduced so that we may configure for Products to be Requisitioned through different partners. e.g. MSL and CHAZ. This independent service would use the Reference Data service to reference products and facilities. It would define Supplying Partners and provide API endpoints for those. The extension would also provide its own business rules and configurations to manage which supplying partners are able to supply which products and how much quantity at which facilities.
Extension Mechanism: Independent Service
The Facility Type Approved Product screen will need to be extended to allow for a user to tag products approved for use at a facility as supplied through the Supplying Partner. The extended UI will add this information to the screen and store it in a Facility Approved Product's Extra Data.
- Ref UI built by Implementer
- Extra Data extension on Facility Type Approved Product
Requisition Screen - UI
The Requisition Screen would need to be extended so that an Approver of Requisition A could see which line items (or % thereof) would be fulfilled by which Supplying Partner. Additionally some configuration might be desired so that MSL supervisors could see details of the original requisition.
- Extend Reference UI for Requisition screen
A supervisory node (SN) typically only has one parent. When an SN doesn't have a parent, it's the top level supervision for all of the children below it. In order to facilitate splitting, we need to add to a Supervisory Node that it could have partner nodes. These partner nodes are used by the Requisition Approver to determine when to split a Requisition into new supervision lines.
- Extra Data on Supervisory Node
- UI & Configuration
Requisition Approver - Extension Point & Module
A Requisition Approver Extension Point, would form the basis of allowing the implementer to build an Extension Module that can change the original behavior of a requisition approval action. This is needed so that the implementer may catch the Approval at the District Malaria Supervision node and split the Requisition into A1 and A2.
- Extension Point for Requisition Approval
- Extension Module for Requisition Approval to appropriately split a Requisition and associate them with new supervision nodes
Once a Requisition is split, it'll likely follow that we will want to track which Requisition was created as a result of another Requisition's split. Extra data would be added to the Requisition entity to enable that.
- Extra data added to Requisition
Requisition Status - Extension Point & Module
The Requisition Service needs new a status for a Requisition - i.e. split. The current design would need to be moved to more of an Event / Messaging approach if the set of statuses, and which ones were used, were not known ahead of time for all implementations.
- Extension Point for handling Requisition Event's for split.
Actions and Timeline
- Add Extra Data on Facility Type Approved Product in Reference Data Service
- Add Extra Data on Supervisory Node in Reference Data Service
- Add Extra Data on Requisition in Requisition Service
- Add Extension Point for Requisition Approver in Requisition Service
- Add Extension Point for processing Requisition Event's (requires refactor of Requisition Status away from Enum)
- Create Supplying Partner Service
- Create Extension Module for Requisition Approver ExtPt
- Create Extension Module for processing Requisition Event's for split status
Packaging Components with Extensions
Each implementation of OpenLMIS 3 uses
openlmis-ref-distro to spin up and connect each docker container, including extensions and custom components. The
docker-compose.yml file from the reference distribution can be customized by each implementation to add additional services, such as
supplyingpartner, or to configure which extension modules are used.
Let's zoom in to look in more detail at one service, openlmis-requisition, and its extensions.
OpenLMIS services provide extension points and an extension image that allows any number of extension modules to be wired in to the service.
docker-compose.yml file is where an Extension Image is enabled and configured for use with it's Service. In this example, that file would include a community openlmis-requisition service as well as a private requisition Extension Image.
Community refers to the use of a provided OpenLMIS component from the open source project without customization or forking. In this scenario, openlmis-requisition and Extension A are both Community components. Extension A might be disabled in the custom configuration for a given implementation, as might Extensions C, D and others.
Each code repository has Jenkins jobs to build it and publish artifacts. Service's are published as production runnable Docker Images on Docker Hub as well as JARs published to the Maven Central Repository (OSSRH). Extension Modules are published as JARs to Maven. An Extension Image is published as a Docker Image to Docker Hub.
Extension Modules are able to use Extension Points built into the Java code of the Service they extend. They are able to call Java methods of that Service as well as HTTP endpoints of that Service, as well as utilize Service Discovery to determine which other Components are running to use those HTTP APIs.
We encourage all community components (extensions included) to use the OpenLMIS GitHub, Jenkins, Sonar, Docker Hub and Maven repository. Getting easy and free access to this tooling is one benefit of contributing back to the community. Private extensions that are not shared with the community would need to use their own Git host and establish their own Jenkins jobs, Sonar, Docker Hub and Maven, or equivalent. For more information about this, see the Modular Architecture / Reusability / Implementation Models.
By avoiding code forks of the Community components, upgrade paths are easier to manage over time. For example, when a new version of openlmis-requisition is available, a version of this implementation that still uses the private extensions can be built, tested and deployed.
Templates and examples for each of these components are provided with OpenLMIS 3.0 to encourage this kind of extension by the community.