@Override
  @PlusTransactional
  public ResponseEvent<DpRequirementDetail> updateRequirement(
      RequestEvent<DpRequirementDetail> req) {
    try {
      AccessCtrlMgr.getInstance().ensureUserIsAdmin();

      Long dpReqId = req.getPayload().getId();
      DpRequirement existing = getDprDao().getById(dpReqId);
      if (existing == null) {
        return ResponseEvent.userError(DpRequirementErrorCode.NOT_FOUND);
      }

      DpRequirement newDpr = dprFactory.createDistributionProtocolRequirement(req.getPayload());
      OpenSpecimenException ose = new OpenSpecimenException(ErrorType.USER_ERROR);
      ensureUniqueReqConstraints(existing, newDpr, ose);
      ose.checkAndThrow();

      existing.update(newDpr);
      getDprDao().saveOrUpdate(existing);
      return ResponseEvent.response(DpRequirementDetail.from(existing));
    } catch (OpenSpecimenException ose) {
      return ResponseEvent.error(ose);
    } catch (Exception e) {
      return ResponseEvent.serverError(e);
    }
  }
  @Override
  @PlusTransactional
  public ResponseEvent<List<DpRequirementDetail>> getRequirements(RequestEvent<Long> req) {
    try {
      AccessCtrlMgr.getInstance().ensureUserIsAdmin();
      Long dpId = req.getPayload();
      DistributionProtocol dp = daoFactory.getDistributionProtocolDao().getById(dpId);
      if (dp == null) {
        return ResponseEvent.userError(DistributionProtocolErrorCode.NOT_FOUND);
      }

      List<DpRequirementDetail> reqDetails = DpRequirementDetail.from(dp.getRequirements());
      Map<Long, BigDecimal> distributedQty = getDprDao().getDistributedQtyByDp(dpId);
      for (DpRequirementDetail reqDetail : reqDetails) {
        BigDecimal qty = distributedQty.get(reqDetail.getId());
        reqDetail.setDistributedQty(qty == null ? BigDecimal.ZERO : qty);
      }

      return ResponseEvent.response(reqDetails);
    } catch (OpenSpecimenException ose) {
      return ResponseEvent.error(ose);
    } catch (Exception e) {
      return ResponseEvent.serverError(e);
    }
  }
  @Override
  @PlusTransactional
  public ResponseEvent<DpRequirementDetail> getRequirement(RequestEvent<Long> req) {
    try {
      AccessCtrlMgr.getInstance().ensureUserIsAdmin();
      DpRequirement existing = getDprDao().getById(req.getPayload());
      if (existing == null) {
        return ResponseEvent.userError(DpRequirementErrorCode.NOT_FOUND);
      }

      return ResponseEvent.response(DpRequirementDetail.from(existing));
    } catch (OpenSpecimenException ose) {
      return ResponseEvent.error(ose);
    } catch (Exception e) {
      return ResponseEvent.serverError(e);
    }
  }
  @Override
  @PlusTransactional
  public ResponseEvent<DpRequirementDetail> createRequirement(
      RequestEvent<DpRequirementDetail> req) {
    try {
      AccessCtrlMgr.getInstance().ensureUserIsAdmin();

      DpRequirement dpr = dprFactory.createDistributionProtocolRequirement(req.getPayload());

      OpenSpecimenException ose = new OpenSpecimenException(ErrorType.USER_ERROR);
      ensureUniqueReqConstraints(null, dpr, ose);
      ose.checkAndThrow();

      getDprDao().saveOrUpdate(dpr);
      return ResponseEvent.response(DpRequirementDetail.from(dpr));
    } catch (OpenSpecimenException ose) {
      return ResponseEvent.error(ose);
    } catch (Exception e) {
      return ResponseEvent.serverError(e);
    }
  }