Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

We can see that the check permission method was quite fast but still retrieving last regular requisition takes about 8 seconds.

Code

The code that is used to create a new requisition is long and split into few methods and classes:

Requisition Controller

Code Block
languagejava
firstline163
linenumberstrue
public RequisitionDto initiate(@RequestParam(value = "program") UUID program,
                  @RequestParam(value = "facility") UUID facility,
                  @RequestParam(value = "suggestedPeriod", required = false) UUID suggestedPeriod,
                  @RequestParam(value = "emergency") boolean emergency) {
  if (null == facility || null == program) {
    throw new ValidationMessageException(
        new Message(MessageKeys.ERROR_INITIALIZE_MISSING_PARAMETERS));
  }

  permissionService.canInitRequisition(program, facility);
  facilitySupportsProgramHelper.checkIfFacilitySupportsProgram(facility, program);

  Requisition newRequisition = requisitionService
      .initiate(program, facility, suggestedPeriod, emergency);
  return requisitionDtoBuilder.build(newRequisition);
}

Most important things:

  • Line 172: check whether a user has permission to initiate a requisition

...

  • for the given program and facility. The service needs to call the reference-data service endpoint to check that,
  • Line 173: check if the given facility supports program.

...

  • The method retrieves facility data from the reference-data service (based on facility id from the request),
  • Line 175/176: the most important part of this endpoint. Details below,
  • Line 177: build DTO object based on business object. We don't use this object on UI. The created requisition is retrieved second time when the view is changed.

Requisition Service

Code Block
languagejava
firstline156
linenumberstrue
public Requisition initiate(UUID programId, UUID facilityId, UUID suggestedPeriodId,
                            boolean emergency) {
  Requisition requisition = RequisitionBuilder.newRequisition(
      facilityId, programId, emergency);
  requisition.setStatus(RequisitionStatus.INITIATED);

  ProcessingPeriodDto period = periodService
      .findPeriod(programId, facilityId, suggestedPeriodId, emergency);

  requisition.setProcessingPeriodId(period.getId());
  requisition.setNumberOfMonthsInPeriod(period.getDurationInMonths());

  FacilityDto facility = facilityReferenceDataService.findOne(facilityId);
  ProgramDto program = programReferenceDataService.findOne(programId);

  Collection<ApprovedProductDto> approvedProducts =
      approvedProductReferenceDataService.getApprovedProducts(
          facility.getId(), program.getId(), true);

  RequisitionTemplate requisitionTemplate = findRequisitionTemplate(programId);

  int numberOfPreviousPeriodsToAverage;
  List<Requisition> previousRequisitions;
  // numberOfPeriodsToAverage is always >= 2 or null
  if (requisitionTemplate.getNumberOfPeriodsToAverage() == null) {
    numberOfPreviousPeriodsToAverage = 0;
    previousRequisitions = getRecentRequisitions(requisition, 1);
  } else {
    numberOfPreviousPeriodsToAverage = requisitionTemplate.getNumberOfPeriodsToAverage() - 1;
    previousRequisitions =
        getRecentRequisitions(requisition, numberOfPreviousPeriodsToAverage);
  }

  if (numberOfPreviousPeriodsToAverage > previousRequisitions.size()) {
    numberOfPreviousPeriodsToAverage = previousRequisitions.size();
  }

  ProofOfDeliveryDto pod = getProofOfDeliveryDto(emergency, requisition);

  requisition.initiate(requisitionTemplate, approvedProducts, previousRequisitions,
      numberOfPreviousPeriodsToAverage, pod, authenticationHelper.getCurrentUser().getId());

  requisition.setAvailableNonFullSupplyProducts(approvedProductReferenceDataService
      .getApprovedProducts(facility.getId(), program.getId(), false)
      .stream()
      .map(ap -> ap.getOrderable().getId())
      .collect(Collectors.toSet()));

  requisitionRepository.save(requisition);
  return requisition;
}

Most important things:

  • Line 162/163: use period service to find appropriate processing period for the new requisition. Details below,
  • Line 168:

...

  • the method retrieves facility data from the reference-data service (the same data as in the controller layer),
  • Lines 177-191: the method try to find previous requisitions (based on setting from template). Full requisitions objects are retrieved from database. Details below,
  • Line 193: retrieve

...

  • Proof of Delivery object from the fulfillment service,
  • Lines 198-202: retrieve all non full supply products for the given program and facility.


Code Block
languagejava
firstline581
linenumberstrue
private List<Requisition> getRecentRequisitions(Requisition requisition, int amount) {
  List<ProcessingPeriodDto> previousPeriods =
      periodService.findPreviousPeriods(requisition.getProcessingPeriodId(), amount);

  List<Requisition> recentRequisitions = new ArrayList<>();
  for (ProcessingPeriodDto period : previousPeriods) {
    List<Requisition> requisitionsByPeriod = getRequisitionsByPeriod(requisition, period);
    if (!requisitionsByPeriod.isEmpty()) {
      Requisition requisitionByPeriod = requisitionsByPeriod.get(0);
      recentRequisitions.add(requisitionByPeriod);
    }
  }
  return recentRequisitions;
}

Most important things:

  • Line 582/583: use period service to find previous periods. Amount parameter depends on property from the requisition template. Details below,
  • Lines 585-592: find requisitions with the given facility, program and period (full object is retrieved here!).

Period service

Code Block
languagejava
firstline190
linenumberstrue
public ProcessingPeriodDto findPeriod(UUID programId, UUID facilityId, UUID suggestedPeriodId,
                                      Boolean emergency) {
  ProcessingPeriodDto period;

  if (emergency) {
    List<ProcessingPeriodDto> periods = getCurrentPeriods(programId, facilityId);

    if (periods.isEmpty()) {
      throw new ValidationMessageException(new Message(ERROR_INCORRECT_SUGGESTED_PERIOD));
    }

    period = periods.get(0);
  } else {
    period = findTheOldestPeriod(programId, facilityId);
  }

  if (period == null
      || (null != suggestedPeriodId && !suggestedPeriodId.equals(period.getId()))) {
    throw new ValidationMessageException(new Message(
        ERROR_PERIOD_SHOULD_BE_OLDEST_AND_NOT_ASSOCIATED));
  }

  Collection<ProcessingScheduleDto> schedules =
      scheduleReferenceDataService.searchByProgramAndFacility(programId, facilityId);

  if (schedules == null || schedules.isEmpty()) {
    throw new ContentNotFoundMessageException(new Message(
        ERROR_REQUISITION_GROUP_PROGRAM_SCHEDULE_WITH_PROGRAM_AND_FACILITY_NOT_FOUND));
  }

  ProcessingScheduleDto scheduleDto = schedules.iterator().next();

  if (!scheduleDto.getId().equals(period.getProcessingSchedule().getId())) {
    throw new ValidationMessageException(new Message(
        ERROR_PERIOD_MUST_BELONG_TO_THE_SAME_SCHEDULE));
  }

  return period;
}

Most important things:

  • Line 195: retrieve current periods for emergency requisition.

...

  • The method retrieves all periods based on program and facility.

...

  • It filters retrieved list by finding only periods that are in the current date. For example if currently we have 10th November 2016 then only periods that have start date before (or equal to) current date and end date after (or equal to) current date will be accepted.
  • Line 203: find the oldest period for regular requisition. Retrieve last regular requisition (full object!), retrieve all periods based on program and facility, for each period try to find requisition (full object!). Select period that has no requisition (so retrieved requisitions are basically not used).
  • Line 212/213: find processing schedule based on program and facility, only to check if it exists.


Code Block
languagejava
firstline148
linenumberstrue
public List<ProcessingPeriodDto> findPreviousPeriods(UUID periodId, int amount) {
  // retrieve data from reference-data
  ProcessingPeriodDto period = getPeriod(periodId);

  if (null == period) {
    return Collections.emptyList();
  }

  Collection<ProcessingPeriodDto> collection = search(
      period.getProcessingSchedule().getId(), period.getStartDate()
  );

  if (null == collection || collection.isEmpty()) {
    return Collections.emptyList();
  }

  // create a list...
  List<ProcessingPeriodDto> list = new ArrayList<>(collection);
  // ...remove the latest period from the list...
  list.removeIf(p -> p.getId().equals(periodId));
  // .. and sort elements by startDate property DESC.
  list.sort((one, two) -> ObjectUtils.compare(two.getStartDate(), one.getStartDate()));

  if (amount > list.size()) {
    return list;
  }
  return list.subList(0, amount);
}

Most important things:

  • Line 150: retrieve single processing period based on ID,
  • Line 156/157/158: find all periods based on processing schedule and start date,
  • Line 169: the list has been sorted by start date.

Solutions

  • Modify domain object that they will not retrieve related data automatically for example a requisition should not retrieve related line items,
  • Try to retrieve the given object only one time, for example currently facility object is retrieved two times,
  • Return only main information about a requisition to the client,
  • Avoid retrieving data from database only to check if those data exists,