/* * This file is part of LibrePlan * * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia * Copyright (C) 2010-2011 Igalia, S.L. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.libreplan.web.materials; import static org.libreplan.business.common.exceptions.ValidationException.invalidValue; import static org.libreplan.web.I18nHelper._; import java.util.ArrayList; import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.libreplan.business.common.IntegrationEntity; import org.libreplan.business.common.daos.IConfigurationDAO; import org.libreplan.business.common.entities.EntityNameEnum; import org.libreplan.business.common.exceptions.InstanceNotFoundException; import org.libreplan.business.common.exceptions.ValidationException; import org.libreplan.business.materials.daos.IMaterialAssignmentDAO; import org.libreplan.business.materials.daos.IMaterialCategoryDAO; import org.libreplan.business.materials.daos.IMaterialDAO; import org.libreplan.business.materials.daos.IUnitTypeDAO; import org.libreplan.business.materials.entities.Material; import org.libreplan.business.materials.entities.MaterialCategory; import org.libreplan.business.materials.entities.UnitType; import org.libreplan.web.common.IntegrationEntityModel; import org.libreplan.web.common.concurrentdetection.OnConcurrentModification; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.zkoss.ganttz.util.MutableTreeModel; /** * @author Vova Perebykivskyi <vova@libreplan-enterprise.com> */ @Service @Scope(BeanDefinition.SCOPE_PROTOTYPE) @OnConcurrentModification(goToPage = "/materials/materials.zul") public class MaterialsModel extends IntegrationEntityModel implements IMaterialsModel { @Autowired IMaterialCategoryDAO categoryDAO; @Autowired IMaterialDAO materialDAO; @Autowired IUnitTypeDAO unitTypeDAO; @Autowired IConfigurationDAO configurationDAO; @Autowired IMaterialAssignmentDAO materialAssignmentDAO; MutableTreeModel<MaterialCategory> materialCategories = MutableTreeModel.create(MaterialCategory.class); private List<UnitType> unitTypes = new ArrayList<>(); private MaterialCategory currentMaterialCategory; private Map<MaterialCategory, String> oldCodes = new HashMap<>(); private Map<Material, String> oldMaterialCodes = new HashMap<>(); @Override @Transactional(readOnly = true) public MutableTreeModel<MaterialCategory> getMaterialCategories() { if (materialCategories.isEmpty()) { initializeMaterialCategories(); } return materialCategories; } @Override @Transactional(readOnly=true) public void reloadMaterialCategories() { materialCategories = MutableTreeModel.create(MaterialCategory.class); initializeMaterialCategories(); } private void initializeMaterialCategories() { final List<MaterialCategory> categories = categoryDAO.getAllRootMaterialCategories(); for (MaterialCategory materialCategory: categories) { initializeMaterials(materialCategory.getMaterials()); materialCategories.addToRoot(materialCategory); addCategories(materialCategory, materialCategory.getSubcategories()); storeOldCodes(materialCategory); } } private void storeOldCodes(MaterialCategory materialCategory) { /* It stores temporally the autogenerated code */ if (materialCategory.isCodeAutogenerated()) { oldCodes.put(materialCategory, materialCategory.getCode()); for (Material child : materialCategory.getMaterials()) { oldMaterialCodes.put(child, child.getCode()); } } } private void initializeMaterials(Set<Material> materials) { for (Material each: materials) { each.getDescription(); if (each.getUnitType() != null) { each.getUnitType().getMeasure(); } } } private void addCategories(MaterialCategory materialCategory, Set<MaterialCategory> categories) { for (MaterialCategory category: categories) { initializeMaterials(category.getMaterials()); materialCategories.add(materialCategory, category); storeOldCodes(category); final Set<MaterialCategory> subcategories = category.getSubcategories(); if (subcategories != null) { addCategories(category, subcategories); } } } @Override @Transactional(readOnly=true) public List<Material> getMaterials(MaterialCategory materialCategory) { List<Material> result = new ArrayList<>(); result.addAll(materialCategory.getMaterials()); return result; } @Override @Transactional(readOnly=true) public void addMaterialCategory(MaterialCategory parent, String categoryName) throws ValidationException { Validate.notNull(categoryName); Boolean generateCode = configurationDAO.getConfiguration().getGenerateCodeForMaterialCategories(); MaterialCategory child = MaterialCategory.createUnvalidated("", _(categoryName)); if ( generateCode ) { setCurrentMaterialCategory(child); setDefaultCode(); } child.setCodeAutogenerated(generateCode); final MaterialCategory materialCategory = findMaterialCategory(child); if ( materialCategory != null ) { throw new ValidationException(invalidValue( _("{0} already exists", materialCategory.getName()), "name", materialCategory.getName(), materialCategory)); } child.setParent(parent); if ( parent == null ) { materialCategories.addToRoot(child); } else { materialCategories.add(parent, child); } } private MaterialCategory findMaterialCategory(final MaterialCategory category) { for (MaterialCategory mc : materialCategories.asList()) { if ( equalsMaterialCategory(mc, category) ) { return mc; } } return null; } private boolean equalsMaterialCategory(MaterialCategory obj1, MaterialCategory obj2) { String name1 = StringUtils.deleteWhitespace(obj1.getName().toLowerCase()); String name2 = StringUtils.deleteWhitespace(obj2.getName().toLowerCase()); return name1.equals(name2); } @Override @Transactional public void confirmRemoveMaterialCategory(MaterialCategory materialCategory) { /* Remove from list of material categories */ materialCategories.remove(materialCategory); /* Remove from its parent */ final MaterialCategory parent = materialCategory.getParent(); if (parent != null) { materialCategory.getParent().removeSubcategory(materialCategory); } final Long idMaterialCategory = materialCategory.getId(); /* It's not a yet-to-save element */ if (idMaterialCategory != null) { /* It has a parent, in this case is enough with saving parent (all-delete-orphan) */ if (parent != null) { categoryDAO.save(materialCategory.getParent()); } else { /* It was a root element, should be deleted from DB */ try { categoryDAO.remove(idMaterialCategory); } catch (InstanceNotFoundException e) { throw new RuntimeException(); } } reloadMaterialCategories(); } } @Override public void addMaterialToMaterialCategory(MaterialCategory materialCategory) { Material material = Material.create(""); material.setCategory(materialCategory); materialCategory.addMaterial(material); } @Override @Transactional public void confirmSave() throws ValidationException { final List<MaterialCategory> categories = materialCategories.asList(); checkNoCodeRepeatedAtNewMaterials(categories); Integer numberOfDigits = getNumberOfDigitsCode(); generateMaterialCodesIfIsNecessary(categories, numberOfDigits); for (MaterialCategory each : categories) { categoryDAO.save(each); } } private void generateMaterialCodesIfIsNecessary(List<MaterialCategory> categories, Integer numberOfDigits) { for (MaterialCategory category: categories) { if ( category.isCodeAutogenerated() ) { category.generateMaterialCodes(numberOfDigits); } } } private void checkNoCodeRepeatedAtNewMaterials(final List<MaterialCategory> categories) throws ValidationException { List<Material> allMaterials = MaterialCategory.getAllMaterialsWithoutAutogeneratedCodeFrom(categories); Map<String, Material> byCode = new HashMap<>(); for (Material each : allMaterials) { if ( byCode.containsKey(each.getCode()) ) { throw new ValidationException(sameCodeMessage(each, byCode.get(each.getCode()))); } byCode.put(each.getCode(), each); } } private String sameCodeMessage(Material first, Material second) { return _( "both {0} of category {1} and {2} of category {3} have the same code", asStringForUser(first), first.getCategory().getName(), asStringForUser(second), second.getCategory().getName()); } private String asStringForUser(Material material) { return String.format("{code: %s, description: %s}", material.getCode(), material.getDescription()); } @Override public void removeMaterial(Material material) { material.getCategory().removeMaterial(material); } @Override @Transactional(readOnly = true) public Collection<? extends Material> getMaterials() { List<Material> result = new ArrayList<>(); for (MaterialCategory each: getMaterialCategories().asList()) { result.addAll(each.getMaterials()); } return result; } @Override @Transactional(readOnly = true) public void loadUnitTypes() { List<UnitType> result = new ArrayList<>(); for (UnitType each : unitTypeDAO.findAll()) { each.getMeasure(); result.add(each); } this.unitTypes = result; } public List<UnitType> getUnitTypes() { return this.unitTypes; } @Override @Transactional(readOnly = true) public boolean canRemoveMaterial(Material material) { return material.isNewObject() || materialAssignmentDAO.getByMaterial(material).size() == 0; } @Override protected void restoreOldCodes() { getCurrentEntity().setCode(oldCodes.get(getCurrentEntity())); for (Material child : ((MaterialCategory) getCurrentEntity()).getMaterials()) { if (!child.isNewObject()) { child.setCode(oldMaterialCodes.get(child)); } } } public void setCodeAutogenerated(boolean codeAutogenerated, MaterialCategory materialCategory) throws ConcurrentModificationException { setCurrentMaterialCategory(materialCategory); setCodeAutogenerated(codeAutogenerated); } @Override public Boolean isGenerateCodeOld() { return getCurrentEntity() != null && oldCodes.get(getCurrentEntity()) != null; } public EntityNameEnum getEntityName() { return EntityNameEnum.MATERIAL_CATEGORY; } public Set<IntegrationEntity> getChildren() { return new HashSet<>(); } public IntegrationEntity getCurrentEntity() { return this.currentMaterialCategory; } public void setCurrentMaterialCategory(MaterialCategory materialCategory) { this.currentMaterialCategory = materialCategory; } }