Implementation details

The implementation of this callout requires a custom outbound class and API endpoint. The outbound class facilitates outbound requests from WFM to the custom service used for Mobile Shift Offer. This class must implement the ShiftOfferCalloutService interface. To enable the callout, the outbound class is added to the SHIFT_OFFER_SVC registry parameter, using the bean ID for the class.

The outbound class is responsible for implementing this functionality:

  • Communicating shift offers from WFM to the custom service
  • Communicating shift offer cancellations to the custom service (optional)

See "SHIFT_OFFER_SVC" in the Infor Workforce Management Registry Parameter Reference Guide.

The API endpoint facilitates inbound requests from the custom service to complete Mobile Shift Offer transactions. The endpoint must call the ShiftOfferCallbackHandlerService API, which manages Mobile Shift Offer transactions in the database, to complete transactions. Customers implementing this callout must work with Infor Consulting Services to develop the required API endpoint. All custom API code must follow the required ISO processes and undergo a security scan.

The API endpoint is responsible for implementing this functionality:

  • Receiving employee claim requests and assigning offered shifts to the claiming employees, including assignment validations
  • Expiring shift offers that have timed out
  • Receiving employee shift rejection decisions from employee and communicating them back to WFM (optional)

Offering shifts

The outbound class receives a ShiftOfferCDOData object as input data from WFM when a user offers one or more shifts in the scheduling screens. This object contains data related to the Mobile Shift Offer transaction. These objects are included in this object:
Object Description
module Identifies if shifts being offered were created in MVS or LFSO.
empIds Contains the employee IDs for the employees that the shifts are being offered to.
shiftOfferShifts Contains a list of the shifts being offered in the transaction and data about each shift.
shiftOffer Contains these details about the shift offer transaction:
  • The date and time when the transaction was initiated
  • The number of minutes the user has selected as an offer window
  • If the user has selected the Auto-approve Compliance Warnings check box
  • The user ID of the user that initiated the transaction

The outbound class is responsible for passing shift offers from WFM to the custom service. The custom service can use any supported method to communicate the offered shifts to the selected employees. For each shift offered, the outbound class must output an external code that is used to identify the shift.

For example, the default ShiftOfferCalloutServiceImplJDA implementation uses this function to send the shift data and a list of employees' contact information, or empChannels, to the JDA service. This implementation outputs the response code from JDA as the external code used to identify the shift.
private fun sendShiftToBroadcastingPlatform(
            offer: ShiftOfferCDOData,
            empChannels: Map<Long, EmployeeShiftOfferChannel>,
            callbackUrl: String
    ): Map<Long, String> {

       val unassignedShiftIdToExtCode = HashMap<Long, String>()
        val offerWindow: Int? = offer.shiftOffer?.shiftOfferDurationInMin
        if (offerWindow == null) {
            logger.error("Skipping sendShiftToBroadcastingPlatform, expected non-null shift offer from core")
            return unassignedShiftIdToExtCode
        }

        for (shift in offer.shiftOfferShifts) {
            if (shift.unassignedShift?.unasgnShiftId == null) {
                logger.error("Skipping shift, expected non-null unassignedShift with non-null ID from core code")
                continue
            }
            val shiftexShiftId: String = sendShift(offer.empIds, shift.unassignedShift, offerWindow, callbackUrl, empChannels)
            unassignedShiftIdToExtCode[shift.unassignedShift.unasgnShiftId] = shiftexShiftId
            if (logger.isDebugEnabled) {
                logger.debug("offer shift: ${shift.unassignedShift}\n to JDA and received external code: $shiftexShiftId")
            }
        }
        return unassignedShiftIdToExtCode
    }

The outbound class can validate if employees are eligible to receive shift offers using the method getNoChannelEmployees. The method returns a list of employee IDs for employees that can receive a shift offer. This method also returns a list of employee names that cannot receive shift offers. A soft-stop message is displayed to the user that initiated the shift offer transaction if one or more selected employees are not eligible to receive shift offers. If no employees can receive a shift offer, the transaction is not allowed, and a validation message is displayed.

For example, the default ShiftOfferCalloutServiceImplJDA implementation places employees that have at least one contact method specified in their employee profile in the list of employees that can receive shift offers. Employees that do not have any contact methods are placed in the list of employees that cannot receive shift offers.

 override fun getNoChannelEmployees(input: ShiftOfferInputData.SocGetNoChannelEmployeesInput): ShiftOfferOutputData.SocGetNoChannelEmployeesOutput {
        val finalEmpIds: MutableList<Long> = ArrayList()
        val empNames: MutableList<EmpName> = mutableListOf()
        for (empId in input.offer.empIds) {
            val empShiftOfferChannels = this.getEmployeeShiftOfferChannels(empId)
            if (empShiftOfferChannels.channelValues.isNullOrEmpty()) {
                val emp = employeeService.getEmployeeById(empId)
                empNames.add(EmpName(emp.empLastname, emp.empFirstname))
            } else {
                finalEmpIds.add(empId)
            }
        }
        empNames.sort()
        val empNameStsList: MutableList<String> = ArrayList(empNames.size)
        for (empName in empNames) {
            empNameStsList.add(empName.lastName + ", " + empName.firstName)
        }
        return ShiftOfferOutputData.SocGetNoChannelEmployeesOutput(empNameStsList, finalEmpIds)
    }

    private fun getEmployeeShiftOfferChannels(empId: Long): EmployeeShiftOfferChannel {
        val chnlValueMap: MutableMap<SchShiftOfferChannel, String> = mutableMapOf()
        chnlValueMap.putAll(populateEmpChannelValue(empId, JDAShiftOfferChannel.EMAIL))
        chnlValueMap.putAll(populateEmpChannelValue(empId, JDAShiftOfferChannel.SMS))
        chnlValueMap.putAll(populateEmpChannelValue(empId, JDAShiftOfferChannel.IVR))
        val channel = EmployeeShiftOfferChannel()
        channel.empId = empId
        channel.channelValues = chnlValueMap
        return channel
    }

Assigning claimed shifts

Employees who are selected in a shift offer transaction can claim the offered shift. The mechanism that employees can use to claim shifts, such as a link in an email message, is handled by the custom service. The custom service must send claim requests to the custom API endpoint. The API endpoint is responsible for assigning the shift to the claiming employees, including handling any associated validations.

Expiring shift offers

Users specify an offer window when they initiate a shift offer. This determines the number of minutes after the shift offer when employees can claim the shift. The outbound class may provide the specified offer window to the custom service to indicate when it should expire the shift offer. When shift offers expire, the custom service can send a request to the custom API endpoint. The API endpoint is responsible for expiring the shift offer.

Shift expiry is optional in cases where shift offer expiry is not supported by the service provider. In those cases, the API endpoint must validate shift claims to ensure that shifts that are claimed after the shift's start time are not assigned to claiming employees.

Recording employee rejections

Employees may reject a shift offer, depending on the options supported by the custom service provider. The custom service can send a request for employee rejections to the custom API endpoint. The API endpoint can record the rejections or perform other functions, depending on the customer requirements. Handling employee rejections is optional.

Canceling shift offers

The outbound class receives a SocCancelInput object as an input from WFM when a user cancels an existing shift offer in the scheduling screens. This object includes the external code used to identify the shift. The outbound class determines what to do when shift offers are canceled.

For example, the default ShiftOfferCalloutServiceImplJDA implementation sends the shift offer cancellation to the JDA service using the external code and checks the response for errors.
override fun cancelShiftOffer(input: ShiftOfferInputData.SocCancelInput): ShiftOfferOutputData.SocCancelOutput {
        val stub = getShiftexServiceStub()
        val cancelShft = CancelShiftReq()
        cancelShft.shiftId = input.externalCode
        val response = stub.cancelShift(cancelShft)
        if (!MSG_OK.equals(response.errorCode, ignoreCase = true)) {
            var errorCode = response.errorCode
            errorCode = FileUtil.preventPathTraversal(errorCode)
            errorCode = Encode.forHtml(errorCode)
            logger.debug("CancelShiftResp : ")
            logger.debug("error code : $errorCode")
            throw WBException("Shiftex error occurred when canceling shift: $errorCode")
        }
        return ShiftOfferOutputData.SocCancelOutput()
    }

Shift offer cancellation is an optional feature. If your implementation does not support shift offer cancellations, it is recommended to hide the Cancel Mobile Offer buttons in the scheduling screens using security group settings.