=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataelement/DataElementCategoryService.java' --- dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataelement/DataElementCategoryService.java 2011-09-26 17:37:55 +0000 +++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataelement/DataElementCategoryService.java 2011-09-27 15:51:10 +0000 @@ -310,6 +310,23 @@ void generateOptionCombos( DataElementCategoryCombo categoryCombo ); /** + * Invokes updateOptionCombos( DataElementCategoryCombo ) for all category + * combos which the given category is a part of. + * + * @param category the DataElementCategory. + */ + void updateOptionCombos( DataElementCategory category ); + + /** + * Generates the complete set of category option combos for the given + * category combo and compares it to the set of persisted category option + * combos. Those which are not matched are persisted. + * + * @param categoryCombo the DataElementCategoryCombo. + */ + void updateOptionCombos( DataElementCategoryCombo categoryCombo ); + + /** * Populates all transient properties on each Operand in the given collection. * * @param operands the collection of Operands. === modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataelement/DefaultDataElementCategoryService.java' --- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataelement/DefaultDataElementCategoryService.java 2011-09-26 17:37:55 +0000 +++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataelement/DefaultDataElementCategoryService.java 2011-09-27 15:51:10 +0000 @@ -469,12 +469,41 @@ for ( DataElementCategoryOptionCombo optionCombo : categoryCombo.getOptionCombos() ) { + categoryCombo.getOptionCombos().add( optionCombo ); addDataElementCategoryOptionCombo( optionCombo ); } updateDataElementCategoryCombo( categoryCombo ); } + public void updateOptionCombos( DataElementCategory category ) + { + for ( DataElementCategoryCombo categoryCombo : getAllDataElementCategoryCombos() ) + { + if ( categoryCombo.getCategories().contains( category ) ) + { + updateOptionCombos( categoryCombo ); + } + } + } + + public void updateOptionCombos( DataElementCategoryCombo categoryCombo ) + { + List generatedOptionCombos = categoryCombo.generateOptionCombosList(); + Set persistedOptionCombos = categoryCombo.getOptionCombos(); + + for ( DataElementCategoryOptionCombo optionCombo : generatedOptionCombos ) + { + if ( !persistedOptionCombos.contains( optionCombo ) ) + { + categoryCombo.getOptionCombos().add( optionCombo ); + addDataElementCategoryOptionCombo( optionCombo ); + } + } + + updateDataElementCategoryCombo( categoryCombo ); + } + public int getDataElementCategoryCount() { return dataElementCategoryStore.getCount(); === modified file 'dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/dataelement/DataElementCategoryComboServiceTest.java' --- dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/dataelement/DataElementCategoryComboServiceTest.java 2011-09-26 15:17:15 +0000 +++ dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/dataelement/DataElementCategoryComboServiceTest.java 2011-09-27 15:51:10 +0000 @@ -53,6 +53,7 @@ private DataElementCategoryOption categoryOptionD; private DataElementCategoryOption categoryOptionE; private DataElementCategoryOption categoryOptionF; + private DataElementCategoryOption categoryOptionG; private DataElementCategory categoryA; private DataElementCategory categoryB; @@ -81,6 +82,7 @@ categoryOptionD = new DataElementCategoryOption( "OptionD" ); categoryOptionE = new DataElementCategoryOption( "OptionE" ); categoryOptionF = new DataElementCategoryOption( "OptionF" ); + categoryOptionG = new DataElementCategoryOption( "OptionG" ); categoryService.addDataElementCategoryOption( categoryOptionA ); categoryService.addDataElementCategoryOption( categoryOptionB ); @@ -88,6 +90,7 @@ categoryService.addDataElementCategoryOption( categoryOptionD ); categoryService.addDataElementCategoryOption( categoryOptionE ); categoryService.addDataElementCategoryOption( categoryOptionF ); + categoryService.addDataElementCategoryOption( categoryOptionG ); categoryA = new DataElementCategory( "CategoryA" ); categoryB = new DataElementCategory( "CategoryB" ); @@ -199,6 +202,86 @@ assertEquals( 8, optionCombos.size() ); + assertOptionCombos( optionCombos ); + } + + @Test + public void testUpdateCategoryOptionCombosA() + { + categoryComboA = new DataElementCategoryCombo( "CategoryComboA", categories ); + categoryService.addDataElementCategoryCombo( categoryComboA ); + + categoryService.generateOptionCombos( categoryComboA ); + + assertNotNull( categoryComboA.getOptionCombos() ); + assertEquals( 8, categoryComboA.getOptionCombos().size() ); + assertOptionCombos( categoryComboA.getOptionCombos() ); + + categoryC.getCategoryOptions().add( categoryOptionG ); + categoryOptionG.setCategory( categoryC ); + categoryService.updateDataElementCategory( categoryC ); + + categoryService.updateOptionCombos( categoryComboA ); + + assertNotNull( categoryComboA.getOptionCombos() ); + assertEquals( 12, categoryComboA.getOptionCombos().size() ); + assertOptionCombos( categoryComboA.getOptionCombos() ); + + assertTrue( categoryComboA.getOptionCombos().contains( createCategoryOptionCombo( categoryComboA, categoryOptionA, categoryOptionC, categoryOptionG ) ) ); + assertTrue( categoryComboA.getOptionCombos().contains( createCategoryOptionCombo( categoryComboA, categoryOptionA, categoryOptionD, categoryOptionG ) ) ); + assertTrue( categoryComboA.getOptionCombos().contains( createCategoryOptionCombo( categoryComboA, categoryOptionB, categoryOptionC, categoryOptionG ) ) ); + assertTrue( categoryComboA.getOptionCombos().contains( createCategoryOptionCombo( categoryComboA, categoryOptionB, categoryOptionD, categoryOptionG ) ) ); + } + + @Test + public void testUpdateCategoryOptionCombosB() + { + categoryComboA = new DataElementCategoryCombo( "CategoryComboA", categories ); + categoryService.addDataElementCategoryCombo( categoryComboA ); + + categoryService.generateOptionCombos( categoryComboA ); + + assertNotNull( categoryComboA.getOptionCombos() ); + assertEquals( 8, categoryComboA.getOptionCombos().size() ); + assertOptionCombos( categoryComboA.getOptionCombos() ); + + categoryService.updateOptionCombos( categoryComboA ); + + assertNotNull( categoryComboA.getOptionCombos() ); + assertEquals( 8, categoryComboA.getOptionCombos().size() ); + assertOptionCombos( categoryComboA.getOptionCombos() ); + } + + @Test + public void testUpdateCategoryOptionCombosC() + { + categoryComboA = new DataElementCategoryCombo( "CategoryComboA", categories ); + categoryService.addDataElementCategoryCombo( categoryComboA ); + + categoryService.generateOptionCombos( categoryComboA ); + + assertNotNull( categoryComboA.getOptionCombos() ); + assertEquals( 8, categoryComboA.getOptionCombos().size() ); + assertOptionCombos( categoryComboA.getOptionCombos() ); + + categoryC.getCategoryOptions().add( categoryOptionG ); + categoryOptionG.setCategory( categoryC ); + categoryService.updateDataElementCategory( categoryC ); + + categoryService.updateOptionCombos( categoryC ); + + assertNotNull( categoryComboA.getOptionCombos() ); + assertEquals( 12, categoryComboA.getOptionCombos().size() ); + assertOptionCombos( categoryComboA.getOptionCombos() ); + + assertTrue( categoryComboA.getOptionCombos().contains( createCategoryOptionCombo( categoryComboA, categoryOptionA, categoryOptionC, categoryOptionG ) ) ); + assertTrue( categoryComboA.getOptionCombos().contains( createCategoryOptionCombo( categoryComboA, categoryOptionA, categoryOptionD, categoryOptionG ) ) ); + assertTrue( categoryComboA.getOptionCombos().contains( createCategoryOptionCombo( categoryComboA, categoryOptionB, categoryOptionC, categoryOptionG ) ) ); + assertTrue( categoryComboA.getOptionCombos().contains( createCategoryOptionCombo( categoryComboA, categoryOptionB, categoryOptionD, categoryOptionG ) ) ); + } + + private void assertOptionCombos( Set optionCombos ) + { assertTrue( optionCombos.contains( createCategoryOptionCombo( categoryComboA, categoryOptionA, categoryOptionC, categoryOptionE ) ) ); assertTrue( optionCombos.contains( createCategoryOptionCombo( categoryComboA, categoryOptionA, categoryOptionC, categoryOptionF ) ) ); assertTrue( optionCombos.contains( createCategoryOptionCombo( categoryComboA, categoryOptionA, categoryOptionD, categoryOptionE ) ) ); === added file 'dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/ajax/jsonCategoryOption.vm' --- dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/ajax/jsonCategoryOption.vm 1970-01-01 00:00:00 +0000 +++ dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/ajax/jsonCategoryOption.vm 2011-09-27 17:05:48 +0000 @@ -0,0 +1,6 @@ +{ "dataElementCategoryOption": + { + "id": "$!{dataElementCategoryOption.id}", + "name": "$encoder.jsonEncode( $!{dataElementCategoryOption.name} )" + } +} \ No newline at end of file === modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/java/org/hisp/dhis/dd/action/category/AddDataElementCategoryAction.java' --- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/java/org/hisp/dhis/dd/action/category/AddDataElementCategoryAction.java 2010-09-04 07:26:32 +0000 +++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/java/org/hisp/dhis/dd/action/category/AddDataElementCategoryAction.java 2011-09-27 15:11:53 +0000 @@ -108,5 +108,4 @@ return SUCCESS; } - } === added file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/java/org/hisp/dhis/dd/action/category/AddDataElementCategoryOptionAction.java' --- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/java/org/hisp/dhis/dd/action/category/AddDataElementCategoryOptionAction.java 1970-01-01 00:00:00 +0000 +++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/java/org/hisp/dhis/dd/action/category/AddDataElementCategoryOptionAction.java 2011-09-27 17:05:48 +0000 @@ -0,0 +1,100 @@ +package org.hisp.dhis.dd.action.category; + +/* + * Copyright (c) 2004-2010, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.hisp.dhis.dataelement.DataElementCategory; +import org.hisp.dhis.dataelement.DataElementCategoryOption; +import org.hisp.dhis.dataelement.DataElementCategoryService; + +import com.opensymphony.xwork2.Action; + +/** + * @author Lars Helge Overland + */ +public class AddDataElementCategoryOptionAction + implements Action +{ + // ------------------------------------------------------------------------- + // Dependencies + // ------------------------------------------------------------------------- + + private DataElementCategoryService dataElementCategoryService; + + public void setDataElementCategoryService( DataElementCategoryService dataElementCategoryService ) + { + this.dataElementCategoryService = dataElementCategoryService; + } + + // ------------------------------------------------------------------------- + // Input + // ------------------------------------------------------------------------- + + private Integer categoryId; + + public void setCategoryId( Integer categoryId ) + { + this.categoryId = categoryId; + } + + private String name; + + public void setName( String name ) + { + this.name = name; + } + + // ------------------------------------------------------------------------- + // Output + // ------------------------------------------------------------------------- + + private DataElementCategoryOption dataElementCategoryOption; + + public DataElementCategoryOption getDataElementCategoryOption() + { + return dataElementCategoryOption; + } + + // ------------------------------------------------------------------------- + // Action implementation + // ------------------------------------------------------------------------- + + public String execute() + { + DataElementCategory category = dataElementCategoryService.getDataElementCategory( categoryId ); + + dataElementCategoryOption = new DataElementCategoryOption( name ); + dataElementCategoryOption.setCategory( category ); + category.getCategoryOptions().add( dataElementCategoryOption ); + + dataElementCategoryService.addDataElementCategoryOption( dataElementCategoryOption ); + dataElementCategoryService.updateDataElementCategory( category ); + dataElementCategoryService.updateOptionCombos( category ); + + return SUCCESS; + } +} === modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/java/org/hisp/dhis/dd/action/category/UpdateDataElementCategoryAction.java' --- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/java/org/hisp/dhis/dd/action/category/UpdateDataElementCategoryAction.java 2011-09-27 10:12:47 +0000 +++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/java/org/hisp/dhis/dd/action/category/UpdateDataElementCategoryAction.java 2011-09-27 15:11:53 +0000 @@ -32,6 +32,7 @@ import org.hisp.dhis.concept.ConceptService; import org.hisp.dhis.dataelement.DataElementCategory; +import org.hisp.dhis.dataelement.DataElementCategoryOption; import org.hisp.dhis.dataelement.DataElementCategoryService; import com.opensymphony.xwork2.Action; @@ -107,14 +108,15 @@ // CategoryOptions can only be sorted on update // --------------------------------------------------------------------- - dataElementCategory.getCategoryOptions().clear(); - + List options = new ArrayList(); + for ( String id : categoryOptions ) { - dataElementCategory.getCategoryOptions().add( - dataElementCategoryService.getDataElementCategoryOption( Integer.parseInt( id ) ) ); + options.add( dataElementCategoryService.getDataElementCategoryOption( Integer.parseInt( id ) ) ); } + dataElementCategory.setCategoryOptions( options ); + dataElementCategoryService.updateDataElementCategory( dataElementCategory ); return SUCCESS; === modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/resources/META-INF/dhis/beans.xml' --- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/resources/META-INF/dhis/beans.xml 2011-09-27 10:12:47 +0000 +++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/resources/META-INF/dhis/beans.xml 2011-09-27 15:52:56 +0000 @@ -600,6 +600,13 @@ + + + + + + @@ -670,11 +677,6 @@ - - - - - - + @@ -716,4 +718,8 @@ + + + + === modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/resources/org/hisp/dhis/dd/i18n_module.properties' --- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/resources/org/hisp/dhis/dd/i18n_module.properties 2011-09-08 15:28:20 +0000 +++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/resources/org/hisp/dhis/dd/i18n_module.properties 2011-09-27 17:05:48 +0000 @@ -343,7 +343,7 @@ concept_management = Concept management concept = Concept selected_name = Selected name -update_category_option = Update Category Option +update_category_option = Update category option move_selected = Move selected number_value_type = Number type int = Integer === modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/resources/struts.xml' --- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/resources/struts.xml 2011-09-27 12:16:53 +0000 +++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/resources/struts.xml 2011-09-27 17:05:48 +0000 @@ -620,6 +620,13 @@ F_DATAELEMENT_DELETE + + + /dhis-web-commons/ajax/jsonCategoryOption.vm + + F_DATAELEMENT_ADD + + /dhis-web-commons/ajax/jsonResponseSuccess.vm === modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/webapp/dhis-web-maintenance-datadictionary/javascript/category.js' --- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/webapp/dhis-web-maintenance-datadictionary/javascript/category.js 2011-09-27 13:25:44 +0000 +++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/webapp/dhis-web-maintenance-datadictionary/javascript/category.js 2011-09-27 17:05:48 +0000 @@ -3,25 +3,26 @@ { removeItem( categoryId, categoryName, i18n_confirm_delete, 'removeDataElementCategory.action' ); } - + function addCategoryOption() { - var value = getFieldValue( 'categoryOptionName' ); - if( value.length == 0 ) + var value = $( '#categoryOptionName' ).val(); + + if ( value.length == 0 ) { markInvalid( 'categoryOptionName', i18n_specify_category_option_name ); - } + } else if ( listContainsById( 'categoryOptionNames', value ) ) { markInvalid( 'categoryOptionName', i18n_category_option_name_already_exists ); - } + } else { jQuery.postJSON( 'validateDataElementCategoryOption.action', { name:value }, function( json ) { if ( json.response == 'success' ) { - addOption( 'categoryOptionNames', value, value ); + addOptionById( 'categoryOptionNames', value, value ); setFieldValue( 'categoryOptionName', '' ); } else @@ -32,40 +33,62 @@ } } +function addCategoryOptionToExistingCategory() +{ + var name = $( '#categoryOptionName' ).val(); + var id = $( '#id' ).val(); + + if ( name.length == 0 ) + { + markInvalid( 'categoryOptionName', i18n_specify_category_option_name ); + } + else if ( listContainsById( 'categoryOptions', name, true ) ) + { + markInvalid( 'categoryOptionName', i18n_category_option_name_already_exists ); + } + else + { + jQuery.postJSON( 'validateDataElementCategoryOption.action', { name:name, id:id }, function( json ) + { + if ( json.response == 'success' ) + { + saveCategoryOption( id, name ); + } + else + { + markInvalid( 'categoryOptionName', i18n_category_option_name_already_exists ); + } + } ); + } +} + function updateCategoryOption() { - try - { - var name = getFieldValue( 'categoryOptionName' ); - var id = getFieldValue( 'categoryOptions' ); - - if ( name.length == 0 ) - { - markInvalid( 'categoryOptionName', i18n_specify_category_option_name ); - } - else if ( listContainsById( 'categoryOptions', name, true ) ) - { - markInvalid( 'categoryOptionName', i18n_category_option_name_already_exists ); - } - else - { - jQuery.postJSON( 'validateDataElementCategoryOption.action', { name:name, id:id }, function( json ) - { - if( json.response == 'success' ) - { - updateCategoryOptionName(); - } - else - { - markInvalid( 'categoryOptionName', i18n_category_option_name_already_exists ); - } - } ); - } - } - catch ( e ) + var name = $( '#categoryOptionName' ).val(); + var id = $( '#categoryOptions :selected' ).val; + + if ( name.length == 0 ) { markInvalid( 'categoryOptionName', i18n_specify_category_option_name ); } + else if ( listContainsById( 'categoryOptions', name, true ) ) + { + markInvalid( 'categoryOptionName', i18n_category_option_name_already_exists ); + } + else + { + jQuery.postJSON( 'validateDataElementCategoryOption.action', { name:name, id:id }, function( json ) + { + if ( json.response == 'success' ) + { + updateCategoryOptionName(); + } + else + { + markInvalid( 'categoryOptionName', i18n_category_option_name_already_exists ); + } + } ); + } } function getSelectedCategoryOption() @@ -81,7 +104,18 @@ var url = 'updateDataElementCategoryOption.action?id=' + id + '&name=' + name; - $.postUTF8( url, {}, function() { + $.postUTF8( url, {}, function() + { $( '#categoryOptions :selected' ).text( name ); } ); } + +function saveCategoryOption( id, name ) +{ + var url = 'addDataElementCategoryOption.action'; + + $.postJSON( url, { categoryId:id, name:name }, function( json ) + { + addOptionById( 'categoryOptions', json.dataElementCategoryOption.id, name ); + } ); +} === modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/webapp/dhis-web-maintenance-datadictionary/multidimensional/updateDataElementCategoryForm.vm' --- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/webapp/dhis-web-maintenance-datadictionary/multidimensional/updateDataElementCategoryForm.vm 2011-09-27 12:16:53 +0000 +++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-datadictionary/src/main/webapp/dhis-web-maintenance-datadictionary/multidimensional/updateDataElementCategoryForm.vm 2011-09-27 16:31:45 +0000 @@ -57,7 +57,8 @@ - + +