/* * Copyright 2008-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.broadleafcommerce.inventory.admin.server.service.handler; import java.lang.reflect.InvocationTargetException; import java.text.ParseException; import java.util.ConcurrentModificationException; import java.util.Map; import javax.annotation.Resource; import org.apache.commons.lang.math.NumberUtils; import org.broadleafcommerce.inventory.dao.InventoryDao; import org.broadleafcommerce.inventory.domain.Inventory; import org.broadleafcommerce.inventory.exception.ConcurrentInventoryModificationException; import org.broadleafcommerce.inventory.exception.InventoryUnavailableException; import org.broadleafcommerce.inventory.service.InventoryService; import org.broadleafcommerce.openadmin.dto.Entity; import org.broadleafcommerce.openadmin.dto.FieldMetadata; import org.broadleafcommerce.openadmin.dto.Property; import org.broadleafcommerce.openadmin.server.service.ValidationException; import org.broadleafcommerce.openadmin.server.service.persistence.module.RecordHelper; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; /** * Used in order to create a new transaction when trying to save inventory. Needed to create separate Spring bean component * for this logic because in order to be apart of a different transaction, the new transactional method has to be apart of * a different bean. * * This component should only be used by the {@link InventoryCustomPersistenceHandler#update()} method. * * @author Phillip Verheyden (phillipuniverse) * */ public class AdminInventoryPersister { @Resource(name = "blInventoryDao") protected InventoryDao inventoryDao; @Resource(name = "blInventoryService") protected InventoryService inventoryService; /** * Creates a new transaction and attempts a read, populate, update within the transaction. Retry logic should only * look for a {@link ConcurrentModificationException}. All other exceptions are thrown as a result of attempting to * populate the {@link Inventory} object from the <b>adminProperties</b> passed in. * * @param inventory * @param entity * @param adminProperties * @param helper * @throws ConcurrentInventoryModificationException * @throws ClassNotFoundException * @throws InstantiationException * @throws ParseException * @throws NoSuchMethodException * @throws InvocationTargetException * @throws IllegalAccessException * @throws NumberFormatException * @throws ValidationException */ @Transactional(propagation = Propagation.REQUIRES_NEW, value = "blTransactionManager", rollbackFor = { InventoryUnavailableException.class, ConcurrentInventoryModificationException.class }) public Inventory saveAdminInventory(Long inventoryId, Entity entity, Map<String, FieldMetadata> adminProperties, RecordHelper helper) throws ConcurrentInventoryModificationException, NumberFormatException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, ParseException, InstantiationException, ClassNotFoundException, ValidationException { Inventory inventory = inventoryDao.readForUpdateById(inventoryId); inventory = populateInventory(inventory, entity, adminProperties, helper); //validation failure, return immediately if (inventory == null) { return null; } return inventoryService.save(inventory); } /** * Attempts to populate the given Inventory instance with the merged properties from the admin. Returns null if the * given <b>adminInstance</b> fails validation. If validation fails, invokers should return the entity that was passed * in since this will contain the validation failures. * * @param adminInstance * @param entity * @param inventoryProperties * @param helper * @return * @throws NumberFormatException * @throws IllegalAccessException * @throws InvocationTargetException * @throws NoSuchMethodException * @throws ParseException * @throws InstantiationException * @throws ClassNotFoundException * @throws ValidationException */ protected Inventory populateInventory(Inventory adminInstance, Entity entity, Map<String, FieldMetadata> inventoryProperties, RecordHelper helper) throws NumberFormatException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, ParseException, InstantiationException, ClassNotFoundException, ValidationException { adminInstance = (Inventory) helper.createPopulatedInstance(adminInstance, entity, inventoryProperties, false); Integer quantityAvailableChange = 0; Integer quantityAvailableOnHandChange = 0; Property[] properties = entity.getProperties(); for (Property property : properties) { if (InventoryCustomPersistenceHandler.QUANTITY_AVAILABLE_CHANGE_FIELD_NAME.equals(property.getName())) { quantityAvailableChange = NumberUtils.toInt(property.getValue()); } else if (InventoryCustomPersistenceHandler.QUANTITY_ON_HAND_CHANGE_FIELD_NAME.equals(property.getName())) { quantityAvailableOnHandChange = NumberUtils.toInt(property.getValue()); } } adminInstance.setQuantityAvailable(adminInstance.getQuantityAvailable() + quantityAvailableChange); adminInstance.setQuantityOnHand(adminInstance.getQuantityOnHand() + quantityAvailableOnHandChange); if (adminInstance.getQuantityAvailable() < 0) { entity.setValidationFailure(true); entity.addValidationError(InventoryCustomPersistenceHandler.QUANTITY_AVAILABLE_CHANGE_FIELD_NAME, "quantityAvailableIsNegative"); return null; } else if (adminInstance.getQuantityOnHand() < 0) { entity.setValidationFailure(true); entity.addValidationError(InventoryCustomPersistenceHandler.QUANTITY_ON_HAND_CHANGE_FIELD_NAME, "quantityOnHandIsNegative"); return null; } //avoid lazy initialization exceptions; initialize the ProductOptionValues for this Inventory's Sku adminInstance.getSku().getProductOptionValues().size(); return adminInstance; } }