@Override
  public Errors validateCreate(Long userId, Long documentId, Inventory inventory) {
    Errors errors = applicationContext.getBean(Errors.class);
    if (inventory == null) {
      errors.getErrorObjects().add(new ErrorObject("ERROR", "Resource invalid"));
      return errors;
    }

    User user = userService.findOne(userId);

    // <editor-fold desc="Validate goods receipt resource">

    Document document = documentService.findOne(userId, DocumentType.GOODS_RECEIPT, documentId);
    if (document == null) {
      errors.getErrorObjects().add(new ErrorObject("ERROR", "Invalid goods receipt resource"));
      return errors;
    }

    if (user.getTenant() != document.getTenant()) {
      errors.getErrorObjects().add(new ErrorObject("ERROR", "Access denied to this resource"));
      return errors;
    }

    if (!document.canEdit()) {
      errors
          .getErrorObjects()
          .add(new ErrorObject("ERROR", "Document closed, sorry you can not edit this document."));
      return errors;
    }

    if (!user.getUserSession().getTenantLevel()) {
      if (user.getUserSession().getBusinessUnit() != document.getBusinessUnit()) {
        errors.getErrorObjects().add(new ErrorObject("ERROR", "Access denied to this resource"));
        return errors;
      }
    }

    // </editor-fold>

    // TODO; remove this in future after refactoring how sales orders are posted
    if (!document.getInventoryList().isEmpty()) {
      if (document.getInventoryList().size() + 1 > 200) {
        errors
            .getErrorObjects()
            .add(
                new ErrorObject(
                    "ERROR",
                    "You can only have a maximum of 200 line items in your goods receipt"));
        return errors;
      }
    }

    // <editor-fold desc="Validate product resource">

    Product product = productService.findOne(userId, inventory.getProduct().getProductId());
    if (product == null) {
      errors.getErrorObjects().add(new ErrorObject("ERROR", "Invalid product resource"));
      return errors;
    }
    if (!product.getActive()) {
      errors
          .getErrorObjects()
          .add(new ErrorObject("ERROR", "Product inactive, please choose another product."));
      return errors;
    }

    if (product.getProductType() == null) {
      errors.getErrorObjects().add(new ErrorObject("ERROR", "Invalid product type"));
      return errors;
    }
    if (!product.getTrackInventory()) {
      errors.getErrorObjects().add(new ErrorObject("ERROR", "Invalid product type."));
      return errors;
    }

    // </editor-fold>

    // <editor-fold desc="Validate warehouse locator">

    if (inventory.getWarehouseLocator() == null) {
      errors.getErrorObjects().add(new ErrorObject("ERROR", "Warehouse storage can not be empty"));
      return errors;
    }

    WarehouseLocator warehouseLocator =
        warehouseLocatorService.findById(
            userId, inventory.getWarehouseLocator().getWarehouseLocatorId());

    if (warehouseLocator == null) {
      errors.getErrorObjects().add(new ErrorObject("ERROR", "Invalid warehouse storage"));
      return errors;
    }

    if (document.getWarehouse() != warehouseLocator.getWarehouse()) {
      errors
          .getErrorObjects()
          .add(new ErrorObject("ERROR", "Storage does not belong to this warehouse"));
      return errors;
    }

    if (!warehouseLocator.getActive()) {
      errors
          .getErrorObjects()
          .add(new ErrorObject("ERROR", "Storage inactive, please choose another storage."));
      return errors;
    }

    // </editor-fold>

    return errors;
  }
  @Override
  public Errors validateUpdateStorageAndQuantity(Long userId, Inventory inventory) {
    Errors errors = applicationContext.getBean(Errors.class);
    User user = userService.findOne(userId);
    if (inventory == null) {
      errors.getErrorObjects().add(new ErrorObject("ERROR", "Resource invalid"));
      return errors;
    }

    if (inventory.getLineNumber() == null) {
      errors.getErrorObjects().add(new ErrorObject("ERROR", "Invalid line number"));
      return errors;
    }

    // <editor-fold desc="Validate goods receipt line resource">

    Inventory dbInventory = inventoryService.findOne(userId, inventory.getInventoryId());
    if (dbInventory == null) {
      errors.getErrorObjects().add(new ErrorObject("ERROR", "Invalid goods receipt line resource"));
      return errors;
    }
    if (!dbInventory.getDocument().canEdit()) {
      errors
          .getErrorObjects()
          .add(new ErrorObject("ERROR", "Document closed, sorry you can not edit this document."));
      return errors;
    }
    if (user.getTenant() != dbInventory.getTenant()) {
      errors.getErrorObjects().add(new ErrorObject("ERROR", "Access to this resource denied"));
      return errors;
    }

    if (!user.getUserSession().getTenantLevel()) {
      if (user.getUserSession().getBusinessUnit() != dbInventory.getDocument().getBusinessUnit()) {
        errors.getErrorObjects().add(new ErrorObject("ERROR", "Access to this resource denied"));
        return errors;
      }
    }

    // </editor-fold>

    // <editor-fold desc="check and update warehouse storage">

    if (inventory.getWarehouseLocator() == null) {
      errors.getErrorObjects().add(new ErrorObject("ERROR", "Warehouse storage can not be empty"));
      return errors;
    }

    WarehouseLocator warehouseLocator =
        warehouseLocatorService.findById(
            userId, inventory.getWarehouseLocator().getWarehouseLocatorId());
    if (warehouseLocator == null) {
      errors.getErrorObjects().add(new ErrorObject("ERROR", "Invalid warehouse storage resource."));
      return errors;
    }

    if (!dbInventory.getWarehouse().getWarehouseLocators().contains(warehouseLocator)) {
      errors.getErrorObjects().add(new ErrorObject("ERROR", "Invalid warehouse storage resource."));
      return errors;
    }
    // </editor-fold>

    if (dbInventory.getQuantity() == null) {
      errors.getErrorObjects().add(new ErrorObject("ERROR", "Quantity can not be empty"));
      return errors;
    }

    if (dbInventory.getProduct().getUnitOfMeasure() == null) {
      errors.getErrorObjects().add(new ErrorObject("ERROR", "Invalid product unit of measure."));
      return errors;
    }

    if (inventory.getQuantity()
        < dbInventory.getProduct().getUnitOfMeasure().getMinimumQuantity()) {
      errors
          .getErrorObjects()
          .add(
              new ErrorObject(
                  "ERROR",
                  "Inventory quantity can not be less than "
                      + dbInventory.getProduct().getUnitOfMeasure().getMinimumQuantity()));
      return errors;
    }
    // <editor-fold desc="check if product has any attribute which is unique; means quantity can
    // only be 1">

    Boolean unique = false;
    if (dbInventory.getAttributeSetInstance() != null) {
      if (dbInventory.getAttributeSetInstance().getAttributeValueInstances() != null) {
        List<AttributeValueInstance> attributeValueInstanceList =
            dbInventory.getAttributeSetInstance().getAttributeValueInstances();
        if (attributeValueInstanceList != null) {
          if (!attributeValueInstanceList.isEmpty()) {
            for (AttributeValueInstance avi : attributeValueInstanceList) {
              if (avi.getProductAttribute().getUnique()) {
                unique = true;
              }
            }
          }
        }
      }
    }

    if (unique) {
      if (inventory.getQuantity() != 1) {
        errors.getErrorObjects().add(new ErrorObject("ERROR", "Invalid product quantity."));
        return errors;
      }
    }

    // </editor-fold>

    return errors;
  }
  @Override
  public Errors validateRemoveLine(Long userId, Long documentId, Inventory inventory) {
    Errors errors = applicationContext.getBean(Errors.class);
    User user = userService.findOne(userId);
    if (inventory == null) {
      errors.getErrorObjects().add(new ErrorObject("ERROR", "Resource invalid"));
      return errors;
    }

    // <editor-fold desc="Validate goods receipt resource">
    Document document = documentService.findOne(userId, DocumentType.GOODS_RECEIPT, documentId);

    if (document == null) {
      errors.getErrorObjects().add(new ErrorObject("ERROR", "Invalid goods receipt resource"));
      return errors;
    }

    if (user.getTenant() != document.getTenant()) {
      errors.getErrorObjects().add(new ErrorObject("ERROR", "Access denied to this resource"));
      return errors;
    }

    if (!document.canEdit()) {
      errors
          .getErrorObjects()
          .add(new ErrorObject("ERROR", "Document closed, sorry you can not edit this document."));
      return errors;
    }

    if (!user.getUserSession().getTenantLevel()) {
      if (user.getUserSession().getBusinessUnit() != document.getBusinessUnit()) {
        errors.getErrorObjects().add(new ErrorObject("ERROR", "Access denied to this resource"));
        return errors;
      }
    }

    // </editor-fold>

    // <editor-fold desc="Validate goods receipt line resource">
    Inventory dbInventory = inventoryService.findOne(userId, inventory.getInventoryId());

    if (dbInventory == null) {
      errors.getErrorObjects().add(new ErrorObject("ERROR", "Invalid goods receipt line resource"));
      return errors;
    }

    if (dbInventory.getGenerated() != null) {
      if (dbInventory.getGenerated()) {
        if (!ndovuSecurity.authorize(userId, "goodsReceiptLine.removeGeneratedLine")) {
          errors
              .getErrorObjects()
              .add(
                  new ErrorObject(
                      "ERROR",
                      "Sorry, you are not allowed to remove a goods receipt line which has been auto generated from a sales order."));
          return errors;
        }
      }
    }

    if (user.getTenant() != dbInventory.getTenant()) {
      errors.getErrorObjects().add(new ErrorObject("ERROR", "Access to this resource denied"));
      return errors;
    }

    if (!user.getUserSession().getTenantLevel()) {
      if (user.getUserSession().getBusinessUnit() != dbInventory.getDocument().getBusinessUnit()) {
        errors.getErrorObjects().add(new ErrorObject("ERROR", "Access to this resource denied"));
        return errors;
      }
    }

    if (!document.getInventoryList().contains(dbInventory)) {
      errors.getErrorObjects().add(new ErrorObject("ERROR", "Access to this resource denied"));
      return errors;
    }
    // </editor-fold>

    return errors;
  }
  @Override
  public Errors validateUpdateAttributeValues(
      Long userId, Long inventoryId, List<AttributeValueInstance> attributeValueInstances) {
    Errors errors = applicationContext.getBean(Errors.class);
    User user = userService.findOne(userId);
    if (attributeValueInstances == null) {
      errors.getErrorObjects().add(new ErrorObject("ERROR", "Resource invalid"));
      return errors;
    }

    // <editor-fold desc="Validate goods receipt line resource">
    Inventory inventory = inventoryService.findOne(userId, inventoryId);
    if (inventory == null) {
      errors.getErrorObjects().add(new ErrorObject("ERROR", "Invalid goods receipt line resource"));
      return errors;
    }
    if (!inventory.getDocument().canEdit()) {
      errors
          .getErrorObjects()
          .add(new ErrorObject("ERROR", "Document closed, sorry you can not edit this document."));
      return errors;
    }
    if (user.getTenant() != inventory.getTenant()) {
      errors.getErrorObjects().add(new ErrorObject("ERROR", "Access to this resource denied"));
      return errors;
    }

    if (!user.getUserSession().getTenantLevel()) {
      if (user.getUserSession().getBusinessUnit() != inventory.getDocument().getBusinessUnit()) {
        errors.getErrorObjects().add(new ErrorObject("ERROR", "Access to this resource denied"));
        return errors;
      }
    }

    // </editor-fold>

    if (attributeValueInstances == null) {
      errors.getErrorObjects().add(new ErrorObject("ERROR", "Invalid attribute values resource"));
      return errors;
    }

    if (!attributeValueInstances.isEmpty()) {
      for (AttributeValueInstance attributeValueInstance : attributeValueInstances) {
        AttributeValueInstance avi =
            attributeValueInstanceRepository.findOne(
                attributeValueInstance.getAttributeValueInstanceId());
        if (avi == null) {
          errors.getErrorObjects().add(new ErrorObject("ERROR", "Invalid attribute value"));
          return errors;
        }
        if (avi.getAttributeSetInstance() != inventory.getAttributeSetInstance()) {
          errors.getErrorObjects().add(new ErrorObject("ERROR", "Invalid attribute"));
          return errors;
        }

        // check if product attribute is active
        if (!avi.getProductAttribute().getActive()) {
          errors
              .getErrorObjects()
              .add(
                  new ErrorObject(
                      "ERROR",
                      avi.getProductAttribute().getCaption() + " product attribute is inactive "));
          return errors;
        }
        // check if attribute value is mandatory
        if (avi.getProductAttribute().getMandatory()) {
          if (avi.getProductAttribute().getIsDate()) {
            if (attributeValueInstance.getDateValue() == null) {
              errors
                  .getErrorObjects()
                  .add(
                      new ErrorObject(
                          "ERROR", avi.getProductAttribute().getCaption() + " can not be empty"));
              return errors;
            }
          } else if (avi.getProductAttribute().getIsNumber()) {
            if (attributeValueInstance.getNumberValue() == null) {
              errors
                  .getErrorObjects()
                  .add(
                      new ErrorObject(
                          "ERROR", avi.getProductAttribute().getCaption() + " can not be empty"));
              return errors;
            }
          } else {
            if (StringUtils.isBlank(attributeValueInstance.getValue())) {
              errors
                  .getErrorObjects()
                  .add(
                      new ErrorObject(
                          "ERROR", avi.getProductAttribute().getCaption() + " can not be empty"));
              return errors;
            }
          }

          // check if attribute value must be unique
          if (avi.getProductAttribute().getUnique()) {
            // check if attribute value already exists
            AttributeValueInstance dbAttributeValueInstance = new AttributeValueInstance();
            if (avi.getProductAttribute().getIsDate()) {
              dbAttributeValueInstance =
                  attributeValueInstanceRepository.findByProductAttributeAndDateValue(
                      avi.getProductAttribute(),
                      attributeValueInstance.getDateValue().withTimeAtStartOfDay());
            } else if (avi.getProductAttribute().getIsNumber()) {
              dbAttributeValueInstance =
                  attributeValueInstanceRepository.findByProductAttributeAndNumberValue(
                      avi.getProductAttribute(), attributeValueInstance.getNumberValue());
            } else {
              dbAttributeValueInstance =
                  attributeValueInstanceRepository.findByProductAttributeAndValue(
                      avi.getProductAttribute(), attributeValueInstance.getValue());
            }
            // check if same as the row being updated
            if (dbAttributeValueInstance != null) {
              if (dbAttributeValueInstance.getAttributeValueInstanceId()
                  != avi.getAttributeValueInstanceId()) {
                // TODO; Validation of unique instances (Tenant level, Business unit level)
                Double count =
                    inventoryService.countByAttributeInstanceAndDocumentTypeAndBusinessUnit(
                        userId,
                        dbAttributeValueInstance
                            .getAttributeSetInstance()
                            .getAttributeSetInstanceId(),
                        DocumentType.GOODS_RECEIPT,
                        inventory.getBusinessUnit().getBusinessUnitId());
                if (count > 0) {
                  errors
                      .getErrorObjects()
                      .add(
                          new ErrorObject(
                              "ERROR",
                              avi.getProductAttribute().getCaption()
                                  + " "
                                  + attributeValueInstance.getValue()
                                  + "  already exists"));
                  return errors;
                }
              }
            }
          }
        }
      }
    }
    return errors;
  }