@PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_ACB_ADMIN') or hasRole('ROLE_ACB_STAFF')")
  @Transactional(readOnly = false)
  public VendorDTO create(VendorDTO dto)
      throws EntityRetrievalException, EntityCreationException, JsonProcessingException {

    VendorDTO created = vendorDao.create(dto);

    List<CertificationBodyDTO> availableAcbs = acbManager.getAllForUser();
    if (availableAcbs != null && availableAcbs.size() > 0) {
      for (CertificationBodyDTO acb : availableAcbs) {
        VendorACBMapDTO vendorMappingToCreate = new VendorACBMapDTO();
        vendorMappingToCreate.setAcbId(acb.getId());
        vendorMappingToCreate.setVendorId(created.getId());
        vendorMappingToCreate.setTransparencyAttestation(dto.getTransparencyAttestation());
        vendorDao.createTransparencyMapping(vendorMappingToCreate);
      }
    }
    activityManager.addActivity(
        ActivityConcept.ACTIVITY_CONCEPT_VENDOR,
        created.getId(),
        "Vendor " + created.getName() + " has been created.",
        null,
        created);
    return created;
  }
  @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_ACB_ADMIN') or hasRole('ROLE_ACB_STAFF')")
  @Transactional(readOnly = false)
  public VendorDTO update(VendorDTO vendor)
      throws EntityRetrievalException, JsonProcessingException, EntityCreationException {

    VendorDTO before = getById(vendor.getId());
    VendorEntity result = vendorDao.update(vendor);

    // chplAdmin cannot update the transparency but any other role
    // allowed in this method can
    boolean isChplAdmin = false;
    Set<GrantedPermission> permissions = Util.getCurrentUser().getPermissions();
    for (GrantedPermission permission : permissions) {
      if (permission.getAuthority().equals("ROLE_ADMIN")) {
        isChplAdmin = true;
      }
    }

    if (!isChplAdmin) {
      List<CertificationBodyDTO> availableAcbs = acbManager.getAllForUser();
      if (availableAcbs != null && availableAcbs.size() > 0) {
        for (CertificationBodyDTO acb : availableAcbs) {
          VendorACBMapDTO existingMap =
              vendorDao.getTransparencyMapping(vendor.getId(), acb.getId());
          if (existingMap == null) {
            VendorACBMapDTO vendorMappingToUpdate = new VendorACBMapDTO();
            vendorMappingToUpdate.setAcbId(acb.getId());
            vendorMappingToUpdate.setVendorId(before.getId());
            vendorMappingToUpdate.setTransparencyAttestation(vendor.getTransparencyAttestation());
            vendorDao.createTransparencyMapping(vendorMappingToUpdate);
          } else {
            existingMap.setTransparencyAttestation(vendor.getTransparencyAttestation());
            vendorDao.updateTransparencyMapping(existingMap);
          }
        }
      }
    }
    VendorDTO after = new VendorDTO(result);
    after.setTransparencyAttestation(vendor.getTransparencyAttestation());

    activityManager.addActivity(
        ActivityConcept.ACTIVITY_CONCEPT_VENDOR,
        after.getId(),
        "Vendor " + vendor.getName() + " was updated.",
        before,
        after);

    return after;
  }
  @Override
  @PreAuthorize("hasRole('ROLE_ADMIN')")
  @Transactional(readOnly = false)
  public VendorDTO merge(List<Long> vendorIdsToMerge, VendorDTO vendorToCreate)
      throws EntityRetrievalException, JsonProcessingException, EntityCreationException {

    List<VendorDTO> beforeVendors = new ArrayList<VendorDTO>();
    for (Long vendorId : vendorIdsToMerge) {
      beforeVendors.add(vendorDao.getById(vendorId));
    }

    VendorDTO createdVendor = vendorDao.create(vendorToCreate);
    // - search for any products assigned to the list of vendors passed in
    List<ProductDTO> vendorProducts = productDao.getByVendors(vendorIdsToMerge);
    // - reassign those products to the new vendor
    for (ProductDTO product : vendorProducts) {
      product.setVendorId(createdVendor.getId());
      productDao.update(product);
    }
    // - mark the passed in vendors as deleted
    for (Long vendorId : vendorIdsToMerge) {
      List<CertificationBodyDTO> availableAcbs = acbManager.getAllForUser();
      if (availableAcbs != null && availableAcbs.size() > 0) {
        for (CertificationBodyDTO acb : availableAcbs) {
          vendorDao.deleteTransparencyMapping(vendorId, acb.getId());
        }
      }
      vendorDao.delete(vendorId);
    }

    activityManager.addActivity(
        ActivityConcept.ACTIVITY_CONCEPT_VENDOR,
        createdVendor.getId(),
        "Merged "
            + vendorIdsToMerge.size()
            + " vendors into new vendor '"
            + createdVendor.getName()
            + "'.",
        beforeVendors,
        createdVendor);

    return createdVendor;
  }
  @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_ACB_ADMIN') or hasRole('ROLE_ACB_STAFF')")
  @Transactional(readOnly = false)
  public void delete(Long vendorId)
      throws EntityRetrievalException, JsonProcessingException, EntityCreationException {

    VendorDTO toDelete = vendorDao.getById(vendorId);
    List<CertificationBodyDTO> availableAcbs = acbManager.getAllForUser();
    if (availableAcbs != null && availableAcbs.size() > 0) {
      for (CertificationBodyDTO acb : availableAcbs) {
        vendorDao.deleteTransparencyMapping(vendorId, acb.getId());
      }
    }
    vendorDao.delete(vendorId);
    activityManager.addActivity(
        ActivityConcept.ACTIVITY_CONCEPT_VENDOR,
        toDelete.getId(),
        "Vendor " + toDelete.getName() + " has been deleted.",
        toDelete,
        null);
  }
  @ApiOperation(
      value = "Update an existing certified product.",
      notes =
          "Updates the certified product after first validating the request. The logged in"
              + " user must have ROLE_ADMIN or ROLE_ACB_ADMIN and have administrative "
              + " authority on the ACB that certified the product. If a different ACB is passed in"
              + " as part of the request, an ownership change will take place and the logged in "
              + " user must have ROLE_ADMIN.")
  @RequestMapping(
      value = "/update",
      method = RequestMethod.POST,
      produces = "application/json; charset=utf-8")
  public @ResponseBody CertifiedProductSearchDetails updateCertifiedProduct(
      @RequestBody(required = true) CertifiedProductSearchDetails updateRequest)
      throws EntityCreationException, EntityRetrievalException, InvalidArgumentsException,
          JsonProcessingException, ValidationException {

    // make sure the ui didn't send any error or warning messages back
    updateRequest.setErrorMessages(new HashSet<String>());
    updateRequest.setWarningMessages(new HashSet<String>());
    // validate
    CertifiedProductValidator validator = validatorFactory.getValidator(updateRequest);
    if (validator != null) {
      validator.validate(updateRequest);
    }
    if (updateRequest.getErrorMessages() != null && updateRequest.getErrorMessages().size() > 0) {
      throw new ValidationException(
          updateRequest.getErrorMessages(), updateRequest.getWarningMessages());
    }

    CertifiedProductSearchDetails existingProduct =
        cpdManager.getCertifiedProductDetails(updateRequest.getId());
    Long acbId = new Long(existingProduct.getCertifyingBody().get("id").toString());
    Long newAcbId = new Long(updateRequest.getCertifyingBody().get("id").toString());

    if (newAcbId != null && acbId.longValue() != newAcbId.longValue()) {
      cpManager.changeOwnership(updateRequest.getId(), newAcbId);
      CertifiedProductSearchDetails changedProduct =
          cpdManager.getCertifiedProductDetails(updateRequest.getId());
      activityManager.addActivity(
          ActivityConcept.ACTIVITY_CONCEPT_CERTIFIED_PRODUCT,
          existingProduct.getId(),
          "Changed ACB ownership.",
          existingProduct,
          changedProduct);
      existingProduct = changedProduct;
    }

    CertifiedProductDTO toUpdate = new CertifiedProductDTO();
    toUpdate.setId(updateRequest.getId());
    if (updateRequest.getTestingLab() != null
        && !StringUtils.isEmpty(updateRequest.getTestingLab().get("id"))) {
      toUpdate.setTestingLabId(new Long(updateRequest.getTestingLab().get("id").toString()));
    }
    toUpdate.setCertificationBodyId(newAcbId);
    if (updateRequest.getPracticeType() != null
        && updateRequest.getPracticeType().get("id") != null) {
      toUpdate.setPracticeTypeId(new Long(updateRequest.getPracticeType().get("id").toString()));
    }
    if (updateRequest.getClassificationType() != null
        && updateRequest.getClassificationType().get("id") != null) {
      toUpdate.setProductClassificationTypeId(
          new Long(updateRequest.getClassificationType().get("id").toString()));
    }
    toUpdate.setProductVersionId(new Long(updateRequest.getProduct().get("versionId").toString()));
    toUpdate.setCertificationStatusId(
        new Long(updateRequest.getCertificationStatus().get("id").toString()));
    toUpdate.setCertificationEditionId(
        new Long(updateRequest.getCertificationEdition().get("id").toString()));
    toUpdate.setReportFileLocation(updateRequest.getReportFileLocation());
    toUpdate.setSedReportFileLocation(updateRequest.getSedReportFileLocation());
    toUpdate.setSedIntendedUserDescription(updateRequest.getSedIntendedUserDescription());
    toUpdate.setSedTestingEnd(updateRequest.getSedTestingEnd());
    toUpdate.setAcbCertificationId(updateRequest.getAcbCertificationId());
    toUpdate.setOtherAcb(updateRequest.getOtherAcb());
    toUpdate.setVisibleOnChpl(updateRequest.getVisibleOnChpl());
    toUpdate.setTermsOfUse(updateRequest.getTermsOfUse());
    toUpdate.setIcs(updateRequest.getIcs());
    toUpdate.setAccessibilityCertified(updateRequest.getAccessibilityCertified());
    toUpdate.setProductAdditionalSoftware(updateRequest.getProductAdditionalSoftware());

    toUpdate.setTransparencyAttestationUrl(updateRequest.getTransparencyAttestationUrl());

    // set the pieces of the unique id
    if (!StringUtils.isEmpty(updateRequest.getChplProductNumber())) {
      if (updateRequest.getChplProductNumber().startsWith("CHP-")) {
        toUpdate.setChplProductNumber(updateRequest.getChplProductNumber());
      } else {
        String chplProductId = updateRequest.getChplProductNumber();
        String[] chplProductIdComponents = chplProductId.split("\\.");
        if (chplProductIdComponents == null || chplProductIdComponents.length != 9) {
          throw new InvalidArgumentsException(
              "CHPL Product Id " + chplProductId + " is not in a format recognized by the system.");
        } else {
          toUpdate.setProductCode(chplProductIdComponents[4]);
          toUpdate.setVersionCode(chplProductIdComponents[5]);
          toUpdate.setIcsCode(chplProductIdComponents[6]);
          toUpdate.setAdditionalSoftwareCode(chplProductIdComponents[7]);
          toUpdate.setCertifiedDateCode(chplProductIdComponents[8]);
        }

        if (updateRequest.getCertificationDate() != null) {
          Date certDate = new Date(updateRequest.getCertificationDate());
          SimpleDateFormat dateCodeFormat = new SimpleDateFormat("yyMMdd");
          String dateCode = dateCodeFormat.format(certDate);
          toUpdate.setCertifiedDateCode(dateCode);
        }

        if (updateRequest.getCertificationResults() != null
            && updateRequest.getCertificationResults().size() > 0) {
          boolean hasSoftware = false;
          for (CertificationResult cert : updateRequest.getCertificationResults()) {
            if (cert.getAdditionalSoftware() != null && cert.getAdditionalSoftware().size() > 0) {
              hasSoftware = true;
            }
          }
          if (hasSoftware) {
            toUpdate.setAdditionalSoftwareCode("1");
          } else {
            toUpdate.setAdditionalSoftwareCode("0");
          }
        }
      }
    }

    toUpdate = cpManager.update(acbId, toUpdate);

    // update qms standards used
    List<CertifiedProductQmsStandardDTO> qmsStandardsToUpdate =
        new ArrayList<CertifiedProductQmsStandardDTO>();
    for (CertifiedProductQmsStandard newQms : updateRequest.getQmsStandards()) {
      CertifiedProductQmsStandardDTO dto = new CertifiedProductQmsStandardDTO();
      dto.setId(newQms.getId());
      dto.setApplicableCriteria(newQms.getApplicableCriteria());
      dto.setCertifiedProductId(toUpdate.getId());
      dto.setQmsModification(newQms.getQmsModification());
      dto.setQmsStandardId(newQms.getQmsStandardId());
      dto.setQmsStandardName(newQms.getQmsStandardName());
      qmsStandardsToUpdate.add(dto);
    }
    cpManager.updateQmsStandards(acbId, toUpdate, qmsStandardsToUpdate);

    // update targeted users
    List<CertifiedProductTargetedUserDTO> targetedUsersToUpdate =
        new ArrayList<CertifiedProductTargetedUserDTO>();
    for (CertifiedProductTargetedUser newTu : updateRequest.getTargetedUsers()) {
      CertifiedProductTargetedUserDTO dto = new CertifiedProductTargetedUserDTO();
      dto.setId(newTu.getId());
      dto.setCertifiedProductId(toUpdate.getId());
      dto.setTargetedUserId(newTu.getTargetedUserId());
      dto.setTargetedUserName(newTu.getTargetedUserName());
      targetedUsersToUpdate.add(dto);
    }
    cpManager.updateTargetedUsers(acbId, toUpdate, targetedUsersToUpdate);

    // update accessibility standards
    List<CertifiedProductAccessibilityStandardDTO> accessibilityStandardsToUpdate =
        new ArrayList<CertifiedProductAccessibilityStandardDTO>();
    for (CertifiedProductAccessibilityStandard newStd : updateRequest.getAccessibilityStandards()) {
      CertifiedProductAccessibilityStandardDTO dto = new CertifiedProductAccessibilityStandardDTO();
      dto.setId(newStd.getId());
      dto.setCertifiedProductId(toUpdate.getId());
      dto.setAccessibilityStandardId(newStd.getAccessibilityStandardId());
      dto.setAccessibilityStandardName(newStd.getAccessibilityStandardName());
      accessibilityStandardsToUpdate.add(dto);
    }
    cpManager.updateAccessibilityStandards(acbId, toUpdate, accessibilityStandardsToUpdate);

    // update certification date
    cpManager.updateCertificationDate(
        acbId, toUpdate, new Date(updateRequest.getCertificationDate()));

    // update product certifications
    cpManager.updateCertifications(acbId, toUpdate, updateRequest.getCertificationResults());

    // update CQMs
    List<CQMResultDetailsDTO> cqmDtos = new ArrayList<CQMResultDetailsDTO>();
    for (CQMResultDetails cqm : updateRequest.getCqmResults()) {
      if (!StringUtils.isEmpty(cqm.getCmsId())
          && cqm.getSuccessVersions() != null
          && cqm.getSuccessVersions().size() > 0) {
        for (String version : cqm.getSuccessVersions()) {
          CQMResultDetailsDTO cqmDto = new CQMResultDetailsDTO();
          cqmDto.setNqfNumber(cqm.getNqfNumber());
          cqmDto.setCmsId(cqm.getCmsId());
          cqmDto.setNumber(cqm.getNumber());
          cqmDto.setCmsId(cqm.getCmsId());
          cqmDto.setNqfNumber(cqm.getNqfNumber());
          cqmDto.setTitle(cqm.getTitle());
          cqmDto.setVersion(version);
          cqmDto.setSuccess(Boolean.TRUE);
          if (cqm.getCriteria() != null && cqm.getCriteria().size() > 0) {
            for (CQMResultCertification criteria : cqm.getCriteria()) {
              CQMResultCriteriaDTO dto = new CQMResultCriteriaDTO();
              dto.setCriterionId(criteria.getCertificationId());
              CertificationCriterionDTO certDto = new CertificationCriterionDTO();
              certDto.setNumber(criteria.getCertificationNumber());
              dto.setCriterion(certDto);
              cqmDto.getCriteria().add(dto);
            }
          }
          cqmDtos.add(cqmDto);
        }
      } else if (StringUtils.isEmpty(cqm.getCmsId())) {
        CQMResultDetailsDTO cqmDto = new CQMResultDetailsDTO();
        cqmDto.setNqfNumber(cqm.getNqfNumber());
        cqmDto.setCmsId(cqm.getCmsId());
        cqmDto.setNumber(cqm.getNumber());
        cqmDto.setCmsId(cqm.getCmsId());
        cqmDto.setNqfNumber(cqm.getNqfNumber());
        cqmDto.setTitle(cqm.getTitle());
        cqmDto.setSuccess(cqm.isSuccess());
        cqmDtos.add(cqmDto);
      }
    }
    cpManager.updateCqms(acbId, toUpdate, cqmDtos);

    CertifiedProductSearchDetails changedProduct =
        cpdManager.getCertifiedProductDetails(updateRequest.getId());
    cpManager.checkSuspiciousActivity(existingProduct, changedProduct);
    activityManager.addActivity(
        ActivityConcept.ACTIVITY_CONCEPT_CERTIFIED_PRODUCT,
        existingProduct.getId(),
        "Updated certified product " + changedProduct.getChplProductNumber() + ".",
        existingProduct,
        changedProduct);

    // search for the product by id to get it with all the updates
    return changedProduct;
  }