package com.qcadoo.mes.technologies.tree;
import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import com.google.common.collect.Lists;
import com.qcadoo.mes.basic.constants.ProductFields;
import com.qcadoo.mes.basic.constants.UnitConversionItemFieldsB;
import com.qcadoo.mes.technologies.TechnologyService;
import com.qcadoo.mes.technologies.constants.OperationFields;
import com.qcadoo.mes.technologies.constants.OperationProductInComponentFields;
import com.qcadoo.mes.technologies.constants.OperationProductOutComponentFields;
import com.qcadoo.mes.technologies.constants.TechnologiesConstants;
import com.qcadoo.mes.technologies.constants.TechnologyOperationComponentFields;
import com.qcadoo.model.api.DataDefinition;
import com.qcadoo.model.api.DataDefinitionService;
import com.qcadoo.model.api.Entity;
import com.qcadoo.model.api.search.SearchRestrictions;
import com.qcadoo.model.api.units.PossibleUnitConversions;
import com.qcadoo.model.api.units.UnitConversionService;
import com.qcadoo.view.api.ComponentState;
import com.qcadoo.view.api.ViewDefinitionState;
@Service
public class RemoveTOCService {
@Autowired
private TechnologyService technologyService;
@Autowired
private DataDefinitionService dataDefinitionService;
@Autowired
private UnitConversionService unitConversionService;
@Transactional
public boolean removeOnlySelectedOperation(final Entity tocToDelete, final ViewDefinitionState view) {
final Entity parent = tocToDelete.getBelongsToField(TechnologyOperationComponentFields.PARENT);
final List<Entity> operationsToRewrite = tocToDelete.getHasManyField(TechnologyOperationComponentFields.CHILDREN);
if (parent == null) {
if (operationsToRewrite.size() > 1) {
view.addMessage("technologies.technologyDetails.window.treeTab.technologyTree.error.cannotDeleteRoot",
ComponentState.MessageType.FAILURE);
return false;
} else if (operationsToRewrite.size() == 1) {
return createNewRoot(operationsToRewrite.get(0), tocToDelete, view);
} else {
deleteOldToc(tocToDelete);
return true;
}
}
final Optional<Entity> mainOutProductComponentToDelete = technologyService.tryGetMainOutputProductComponent(tocToDelete);
final List<Entity> originalInProducts = tocToDelete
.getHasManyField(TechnologyOperationComponentFields.OPERATION_PRODUCT_IN_COMPONENTS);
List<Entity> newInProducts = Lists.newArrayList();
for (Entity toc : operationsToRewrite) {
Optional<Entity> mainOutProductComponent = technologyService.tryGetMainOutputProductComponent(toc);
if (mainOutProductComponent.isPresent()) {
Optional<Entity> maybeInProductComponent = getInProductComponentFromProductComponent(originalInProducts,
mainOutProductComponent.get());
if (maybeInProductComponent.isPresent()) {
Entity inProductComponent = maybeInProductComponent.get();
newInProducts.add(inProductComponent);
}
}
setNewParent(toc, parent);
}
if (!rewriteInProductComponents(parent, newInProducts, mainOutProductComponentToDelete, view)) {
view.addMessage("technologies.technologyDetails.window.treeTab.technologyTree.error.cannotRewriteProducts",
ComponentState.MessageType.FAILURE);
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return false;
}
deleteOldToc(tocToDelete);
return true;
}
private boolean rewriteInProductComponents(Entity toc, List<Entity> productComponentsToAdd, Optional<Entity> productToDelete,
ViewDefinitionState view) {
List<Entity> originalInProducts = Lists
.newArrayList(toc.getHasManyField(TechnologyOperationComponentFields.OPERATION_PRODUCT_IN_COMPONENTS));
List<String> addedProducts = Lists.newArrayList();
if (productToDelete.isPresent()) {
Optional<Entity> originalProductToDelete = getInProductComponentFromProductComponent(originalInProducts,
productToDelete.get());
if (originalProductToDelete.isPresent()) {
originalInProducts.remove(originalProductToDelete.get());
}
}
for (Entity productComponentToAdd : productComponentsToAdd) {
Optional<Entity> maybeOriginalInProduct = getInProductComponentFromProductComponent(originalInProducts,
productComponentToAdd);
if (maybeOriginalInProduct.isPresent()) {
Entity originalInProduct = maybeOriginalInProduct.get();
sumQuantities(originalInProduct, productComponentToAdd);
} else {
setNewOperationComponent(productComponentToAdd, toc);
originalInProducts.add(productComponentToAdd);
addedProducts.add(productComponentToAdd.getBelongsToField(OperationProductInComponentFields.PRODUCT)
.getStringField(ProductFields.NUMBER));
}
}
toc.setField(TechnologyOperationComponentFields.OPERATION_PRODUCT_IN_COMPONENTS, originalInProducts);
Entity savedToc = toc.getDataDefinition().save(toc);
if (savedToc.isValid() && !addedProducts.isEmpty()) {
view.addMessage("technologies.technologyDetails.window.treeTab.technologyTree.success.productsRewrote",
ComponentState.MessageType.INFO, addedProducts.stream().collect(Collectors.joining(", ")),
toc.getBelongsToField(TechnologyOperationComponentFields.OPERATION).getStringField(OperationFields.NUMBER));
}
return savedToc.isValid();
}
private void sumQuantities(Entity originalInProduct, Entity productComponentToAdd) {
BigDecimal originalQuantity = originalInProduct.getDecimalField(OperationProductInComponentFields.QUANTITY);
BigDecimal quantityToAdd = productComponentToAdd.getDecimalField(OperationProductInComponentFields.QUANTITY);
String givenUnit = originalInProduct.getStringField(OperationProductInComponentFields.GIVEN_UNIT);
Entity product = originalInProduct.getBelongsToField(OperationProductInComponentFields.PRODUCT);
String baseUnit = product.getStringField(ProductFields.UNIT);
BigDecimal quantity = originalQuantity.add(quantityToAdd);
originalInProduct.setField(OperationProductInComponentFields.QUANTITY, quantity);
if (baseUnit.equals(givenUnit)) {
originalInProduct.setField(OperationProductInComponentFields.GIVEN_QUANTITY, quantity);
} else {
PossibleUnitConversions unitConversions = unitConversionService.getPossibleConversions(baseUnit,
searchCriteriaBuilder -> searchCriteriaBuilder
.add(SearchRestrictions.belongsTo(UnitConversionItemFieldsB.PRODUCT, product)));
if (unitConversions.isDefinedFor(givenUnit)) {
BigDecimal convertedQuantity = unitConversions.convertTo(quantity, givenUnit);
originalInProduct.setField(OperationProductInComponentFields.GIVEN_QUANTITY, convertedQuantity);
} else {
originalInProduct.addError(
originalInProduct.getDataDefinition().getField(OperationProductInComponentFields.GIVEN_QUANTITY),
"technologies.operationProductInComponent.validate.error.missingUnitConversion");
originalInProduct.setField(OperationProductInComponentFields.GIVEN_QUANTITY, null);
}
}
}
private boolean createNewRoot(Entity newRoot, final Entity tocToDelete, final ViewDefinitionState view) {
if (setNewParent(newRoot, null)) {
deleteOldToc(tocToDelete);
} else {
view.addMessage("technologies.technologyDetails.window.treeTab.technologyTree.error.cannotCreateNewRoot",
ComponentState.MessageType.FAILURE);
return false;
}
return true;
}
private void deleteOldToc(final Entity tocToDelete) {
getTocDataDefinition().delete(tocToDelete.getId());
}
private DataDefinition getTocDataDefinition() {
return dataDefinitionService.get(TechnologiesConstants.PLUGIN_IDENTIFIER,
TechnologiesConstants.MODEL_TECHNOLOGY_OPERATION_COMPONENT);
}
private boolean setNewParent(Entity toc, final Entity newParent) {
toc.setField(TechnologyOperationComponentFields.PARENT, newParent);
Entity saved = getTocDataDefinition().save(toc);
return saved.isValid();
}
private void setNewOperationComponent(Entity inProduct, final Entity newToc) {
inProduct.setField(OperationProductInComponentFields.OPERATION_COMPONENT, newToc);
inProduct.getDataDefinition().save(inProduct);
}
private Optional<Entity> getInProductComponentFromProductComponent(final List<Entity> originalInProducts,
final Entity productComponent) {
Long productId = productComponent.getBelongsToField(OperationProductOutComponentFields.PRODUCT).getId();
return originalInProducts.stream().filter(inProduct -> inProduct
.getBelongsToField(OperationProductInComponentFields.PRODUCT).getId().compareTo(productId) == 0).findAny();
}
}