Consignment receipts

Confirm each consignment a facility acknowledges in DHIS2 to the warehouse system of the central store.

This page concludes the Supply chain series. When a consignment from the central store arrives at a facility, the storekeeper scans the consignment number on its packing list in the Consignment Receipt Acknowledgement program, which records the receipt as an event. The service below reads the receipts recorded since its previous run and confirms each one to the warehouse system, which then knows the consignment was delivered.

The configuration

consignments.ini holds the program and the data element of the consignment number, the cache key under which the service keeps its place, and the time its first run starts reading from. facilities.ini is a config table with the code the warehouse system knows each facility by:

# config/user-conf/consignments.ini

[receipts]
program = Lq8wZ3nVb6X
element_number = Gd2kP7sYt9R
marker_key = dhis2.supply.receipts.updated-after
start = 2026-10-01T00:00:00
# config/user-conf/facilities.ini

[DHIS2]
DiszpKrYNg8 = FAC-00412
ImspTQPwCqd = FAC-00418

Read the receipts

The receipts are the program's events updated since the previous run, oldest first:

# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

class ConfirmReceipts(Service):
    name = 'dhis2.supply.confirm-receipts'

    def read_receipts(self, updated_after:'str') -> 'list':

        receipts = self.config.consignments.receipts
        fields = 'event,orgUnit,occurredAt,updatedAt,dataValues[dataElement,value]'

        params = {
            'program': receipts.program,
            'updatedAfter': updated_after,
            'order': 'updatedAt:asc',
            'paging': 'false',
            'fields': fields,
        }

        conn = self.rest['DHIS2 Events']
        response = conn.get(params=params)

        out = response.data['events']
        return out

Confirm each one

A confirmation carries the consignment number, the facility under the warehouse system's code, and the date of receipt. Its payload follows the API of the warehouse system - the one below is an example:

    def confirm(self, receipt:'dict') -> 'None':

        element_number = self.config.consignments.receipts.element_number
        uid = receipt['orgUnit']
        facility = self.config.facilities.translate(source='DHIS2', code=uid)

        # A facility the warehouse system has no code for is logged and left out ..
        if facility is None:
            self.logger.warning('No facility code for %s', uid)
            return

        # .. and every other receipt is confirmed with its consignment number.
        number = ''
        for data_value in receipt['dataValues']:
            if data_value['dataElement'] == element_number:
                number = data_value['value']

        confirmation = {
            'receipt': receipt['event'],
            'consignment': number,
            'facility': facility,
            'received_at': receipt['occurredAt'],
        }

        conn = self.rest['Central Store']
        conn.post(confirmation)

The UID of the event goes with each confirmation, so that the warehouse system recognises a receipt it has already confirmed.

Keep the place

The service keeps the update time of the newest receipt in the cache, as the zoonotic notifications service does:

    def handle(self) -> 'None':

        receipts_config = self.config.consignments.receipts
        marker = self.cache.get(receipts_config.marker_key)

        # Start where the previous run ended ..
        if marker is None:
            updated_after = receipts_config.start
        else:
            updated_after = marker.decode('utf8')

        receipts = self.read_receipts(updated_after)

        # .. confirm each receipt, moving the place past it.
        for receipt in receipts:
            self.confirm(receipt)
            updated_at = receipt['updatedAt']
            self.cache.set(receipts_config.marker_key, updated_at)

The place moves after each receipt, so a run that stops halfway, for instance because the warehouse system is unavailable, resumes with the receipt it could not confirm.

The schedule

The service runs from a scheduler job with an interval of fifteen minutes:

Consignment receipts job

See also

FeatureWhat it does
SchedulerInterval-based jobs
Config tablesTranslate the codes of one party into another's

Learn more