diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/MaterialReportingTask.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/MaterialReportingTask.java
index a39774bd1a83642e00889b8d08c46e38214838e9..42c006cbe8c79026eba4c44bbf117316935d8355 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/MaterialReportingTask.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/MaterialReportingTask.java
@@ -17,15 +17,19 @@
 package ch.systemsx.cisd.openbis.generic.server.task;
 
 import java.io.File;
+import java.io.Serializable;
 import java.sql.DatabaseMetaData;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.sql.Types;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -60,6 +64,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchField;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Material;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.MaterialAttributeSearchFieldKind;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.PropertyType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SearchCriteriaConnection;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SessionContextDTO;
 import ch.systemsx.cisd.openbis.generic.shared.util.DataTypeUtils;
@@ -74,31 +79,56 @@ public class MaterialReportingTask implements IMaintenanceTask
     @Private
     static final String MAPPING_FILE_KEY = "mapping-file";
 
-    @Private
-    static final class MappingInfo
+    private static final class Column
     {
-        private static final class Column
+        private final String name;
+
+        private final int index;
+
+        private DataTypeCode dataTypeCode;
+
+        Column(String name, int index)
         {
-            private final String name;
+            this.name = name;
+            this.index = index;
+        }
+    }
 
-            private final int index;
+    private enum IndexingSchema
+    {
+        INSERT(0, 2), UPDATE(-1, 1);
 
-            private DataTypeCode dataTypeCode;
+        private final int codeIndex;
 
-            Column(String name, int index)
-            {
-                this.name = name;
-                this.index = index;
-            }
+        private final int offset;
+
+        private IndexingSchema(int codeIndex, int offset)
+        {
+            this.codeIndex = codeIndex;
+            this.offset = offset;
+        }
+
+        public int getCodeIndex(int size)
+        {
+            return 1 + (size + 1 + codeIndex) % (size + 1);
         }
 
+        public int getPropertyIndexOffset()
+        {
+            return offset;
+        }
+    }
+
+    @Private
+    static final class MappingInfo
+    {
         private final String materialTypeCode;
 
         private final String tableName;
 
         private final String codeColumnName;
 
-        private final Map<String, Column> propertyMapping = new TreeMap<String, Column>();
+        private final Map<String, Column> propertyMapping = new LinkedHashMap<String, Column>();
 
         MappingInfo(String materialTypeCode, String tableName, String codeColumnName)
         {
@@ -117,6 +147,11 @@ public class MaterialReportingTask implements IMaintenanceTask
             return codeColumnName;
         }
 
+        boolean hasProperties()
+        {
+            return propertyMapping.isEmpty() == false;
+        }
+
         void addPropertyMapping(String propertyTypeCode, String propertyColumnName)
         {
             Column column = new Column(propertyColumnName, propertyMapping.size());
@@ -147,16 +182,42 @@ public class MaterialReportingTask implements IMaintenanceTask
                 }
                 column.dataTypeCode = dataTypeCode;
             }
+        }
 
+        Map<String, Map<String, Object>> groupByMaterials(List<Map<String, Object>> rows)
+        {
+            HashMap<String, Map<String, Object>> result =
+                    new HashMap<String, Map<String, Object>>();
+            for (Map<String, Object> map : rows)
+            {
+                String materialCode = map.remove(codeColumnName).toString();
+                result.put(materialCode, map);
+            }
+            return result;
+        }
+
+        String createSelectStatement(List<Material> materials)
+        {
+            StringBuilder builder = new StringBuilder("select * from ");
+            builder.append(tableName).append(" where ");
+            builder.append(codeColumnName).append(" in ");
+            String delim = "(";
+            for (Material material : materials)
+            {
+                builder.append(delim).append('\'').append(material.getCode()).append('\'');
+                delim = ", ";
+            }
+            builder.append(")");
+            return builder.toString();
         }
 
         String createInsertStatement()
         {
             StringBuilder builder = new StringBuilder("insert into ").append(tableName);
             builder.append(" (").append(codeColumnName);
-            for (Column nameAndIndex : propertyMapping.values())
+            for (Column column : propertyMapping.values())
             {
-                builder.append(", ").append(nameAndIndex.name);
+                builder.append(", ").append(column.name);
             }
             builder.append(") values(?");
             for (int i = 0; i < propertyMapping.size(); i++)
@@ -167,7 +228,21 @@ public class MaterialReportingTask implements IMaintenanceTask
             return builder.toString();
         }
 
-        BatchPreparedStatementSetter createSetter(final List<Material> materials)
+        String createUpdateStatement()
+        {
+            StringBuilder builder = new StringBuilder("update ").append(tableName);
+            String delim = " set ";
+            for (Column column : propertyMapping.values())
+            {
+                builder.append(delim).append(column.name).append("=?");
+                delim = ", ";
+            }
+            builder.append(" where ").append(codeColumnName).append("=?");
+            return builder.toString();
+        }
+
+        BatchPreparedStatementSetter createSetter(final List<Material> materials,
+                final IndexingSchema indexing)
         {
             return new BatchPreparedStatementSetter()
                 {
@@ -175,18 +250,31 @@ public class MaterialReportingTask implements IMaintenanceTask
                     {
                         Material material = materials.get(index);
                         List<IEntityProperty> properties = material.getProperties();
-                        ps.setObject(1, material.getCode());
+                        ps.setObject(indexing.getCodeIndex(propertyMapping.size()),
+                                material.getCode());
+                        int propertyIndexOffset = indexing.getPropertyIndexOffset();
                         for (int i = 0; i < propertyMapping.size(); i++)
                         {
-                            ps.setObject(i + 2, null);
+                            ps.setObject(i + propertyIndexOffset, null);
                         }
                         for (IEntityProperty property : properties)
                         {
-                            String code = property.getPropertyType().getCode();
-                            Column nameAndIndex = propertyMapping.get(code);
-                            if (nameAndIndex != null)
+                            PropertyType propertyType = property.getPropertyType();
+                            String code = propertyType.getCode();
+                            Column column = propertyMapping.get(code);
+                            if (column != null)
                             {
-                                ps.setObject(nameAndIndex.index + 2, property.tryGetAsString());
+                                String value = getValue(property);
+                                Serializable typedValue =
+                                        DataTypeUtils.convertValueTo(column.dataTypeCode, value);
+                                if (typedValue instanceof Date)
+                                {
+                                    ps.setObject(column.index + propertyIndexOffset, typedValue,
+                                            Types.TIMESTAMP);
+                                } else
+                                {
+                                    ps.setObject(column.index + propertyIndexOffset, typedValue);
+                                }
                             }
                         }
                     }
@@ -197,6 +285,24 @@ public class MaterialReportingTask implements IMaintenanceTask
                     }
                 };
         }
+
+        private String getValue(IEntityProperty property)
+        {
+            String value;
+            switch (property.getPropertyType().getDataType().getCode())
+            {
+                case CONTROLLEDVOCABULARY:
+                    value = property.getVocabularyTerm().getCodeOrLabel();
+                    break;
+                case MATERIAL:
+                    value = property.getMaterial().getCode();
+                    break;
+                default:
+                    value = property.getValue();
+            }
+            return value;
+        }
+
     }
 
     private static final Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION,
@@ -293,15 +399,6 @@ public class MaterialReportingTask implements IMaintenanceTask
         }
     }
 
-    @Private
-    void closeDatabaseConnections()
-    {
-        if (dbConfigurationContext != null)
-        {
-            dbConfigurationContext.closeConnections();
-        }
-    }
-
     public void execute()
     {
         SessionContextDTO contextOrNull = server.tryToAuthenticateAsSystem();
@@ -317,25 +414,62 @@ public class MaterialReportingTask implements IMaintenanceTask
         {
             String materialTypeCode = entry.getKey();
             final List<Material> materials = entry.getValue();
-            addAndUpdate(materialTypeCode, materials);
+            MappingInfo mappingInfo = mapping.get(materialTypeCode);
+            if (mappingInfo != null)
+            {
+                addOrUpdate(mappingInfo, materials);
+                operationLog.info(materials.size() + " materials of type " + materialTypeCode
+                        + " reported.");
+            }
         }
     }
 
-    private void addAndUpdate(String materialTypeCode, final List<Material> materials)
+    private void addOrUpdate(MappingInfo mappingInfo, final List<Material> materials)
     {
-        MappingInfo mappingInfo = mapping.get(materialTypeCode);
-        if (mappingInfo != null)
+        String sql = mappingInfo.createSelectStatement(materials);
+        List<Map<String, Object>> rows = retrieveRowsToBeUpdated(sql);
+        Map<String, Map<String, Object>> reportedMaterials = mappingInfo.groupByMaterials(rows);
+        List<Material> newMaterials = new ArrayList<Material>();
+        List<Material> updateMaterials = new ArrayList<Material>();
+        for (Material material : materials)
+        {
+            Map<String, Object> reportedMaterial = reportedMaterials.get(material.getCode());
+            if (reportedMaterial != null)
+            {
+                updateMaterials.add(material);
+            } else
+            {
+                newMaterials.add(material);
+            }
+        }
+        if (updateMaterials.isEmpty() == false && mappingInfo.hasProperties())
+        {
+            String updateStatement = mappingInfo.createUpdateStatement();
+            jdbcTemplate.batchUpdate(updateStatement,
+                    mappingInfo.createSetter(updateMaterials, IndexingSchema.UPDATE));
+        }
+        if (newMaterials.isEmpty() == false)
         {
-            List<?> rows =
-                    jdbcTemplate.query("select * from " + mappingInfo.getTableName(),
-                            new ColumnMapRowMapper());
             String insertStatement = mappingInfo.createInsertStatement();
-            jdbcTemplate.batchUpdate(insertStatement, mappingInfo.createSetter(materials));
-            operationLog.info(materials.size() + " materials of type " + materialTypeCode
-                    + " reported.");
+            jdbcTemplate.batchUpdate(insertStatement,
+                    mappingInfo.createSetter(newMaterials, IndexingSchema.INSERT));
         }
     }
 
+    @SuppressWarnings("unchecked")
+    private List<Map<String, Object>> retrieveRowsToBeUpdated(String sql)
+    {
+        List<Map<String, Object>> rows = jdbcTemplate.query(sql, new ColumnMapRowMapper()
+            {
+                @Override
+                protected String getColumnKey(String columnName)
+                {
+                    return columnName.toLowerCase();
+                }
+            });
+        return rows;
+    }
+
     private Map<String, List<Material>> getRecentlyAddedOrChangedMaterials(String sessionToken)
     {
         DetailedSearchCriteria criteria = new DetailedSearchCriteria();
@@ -392,10 +526,10 @@ public class MaterialReportingTask implements IMaintenanceTask
                         splitAndCheck(line.substring(0, line.length() - 1).substring(1), ":", 2,
                                 factory);
                 String materialTypeCode =
-                        trimeAndCheck(splittedLine[0], factory, "material type code");
+                        trimAndCheck(splittedLine[0], factory, "material type code");
                 splittedLine = splitAndCheck(splittedLine[1], ",", 2, factory);
-                String tableName = trimeAndCheck(splittedLine[0], factory, "table name");
-                String codeColumnName = trimeAndCheck(splittedLine[1], factory, "code column name");
+                String tableName = trimAndCheck(splittedLine[0], factory, "table name");
+                String codeColumnName = trimAndCheck(splittedLine[1], factory, "code column name");
                 currentMappingInfo =
                         new MappingInfo(materialTypeCode, tableName.toLowerCase(),
                                 codeColumnName.toLowerCase());
@@ -404,7 +538,7 @@ public class MaterialReportingTask implements IMaintenanceTask
             {
                 String[] splittedLine = splitAndCheck(line, ":", 2, factory);
                 String propertyTypeCode =
-                        trimeAndCheck(splittedLine[0], factory, "property type code");
+                        trimAndCheck(splittedLine[0], factory, "property type code");
                 currentMappingInfo.addPropertyMapping(propertyTypeCode, splittedLine[1].trim()
                         .toLowerCase());
             } else
@@ -428,7 +562,7 @@ public class MaterialReportingTask implements IMaintenanceTask
         return splittedString;
     }
 
-    private static String trimeAndCheck(String string, ExecptionFactory factory, String name)
+    private static String trimAndCheck(String string, ExecptionFactory factory, String name)
     {
         String trimmedString = string.trim();
         if (trimmedString.length() == 0)
@@ -460,4 +594,13 @@ public class MaterialReportingTask implements IMaintenanceTask
         }
     }
 
+    @Private
+    void closeDatabaseConnections()
+    {
+        if (dbConfigurationContext != null)
+        {
+            dbConfigurationContext.closeConnections();
+        }
+    }
+
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/util/DataTypeUtils.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/util/DataTypeUtils.java
index fd88ede018c1c55a9d7c85e15cfb5695f3ef8ba5..2072df8b2fcbd73d5585a5b90ca067a787cc1aed 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/util/DataTypeUtils.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/util/DataTypeUtils.java
@@ -18,16 +18,22 @@ package ch.systemsx.cisd.openbis.generic.shared.util;
 
 import java.io.Serializable;
 import java.sql.Types;
+import java.text.ParseException;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
 
+import org.apache.commons.lang.time.DateUtils;
+
 import ch.systemsx.cisd.common.shared.basic.utils.StringUtils;
 import ch.systemsx.cisd.openbis.generic.shared.basic.BasicConstant;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataTypeCode;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DateTableCell;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DoubleTableCell;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ISerializableComparable;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IntegerTableCell;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.StringTableCell;
+import ch.systemsx.cisd.openbis.generic.shared.util.SimplePropertyValidator.SupportedDatePattern;
 
 /**
  * Utility functions around data types.
@@ -81,9 +87,32 @@ public class DataTypeUtils
                 }
             }
         },
+        TIMESTAMP(DataTypeCode.TIMESTAMP)
+        {
+            @Override
+            public ISerializableComparable doConversion(String value)
+            {
+                return new DateTableCell(doSimpleConversion(value));
+            }
+
+            @Override
+            public Date doSimpleConversion(String value)
+            {
+                try
+                {
+                    String pattern = SupportedDatePattern.CANONICAL_DATE_PATTERN.getPattern();
+                    return DateUtils.parseDate(value, new String[]
+                        { pattern });
+                } catch (ParseException ex)
+                {
+                    throw new IllegalArgumentException("Is not a date in canonical format: "
+                            + value);
+                }
+            }
+        },
         STRING(DataTypeCode.VARCHAR, DataTypeCode.MULTILINE_VARCHAR, DataTypeCode.BOOLEAN,
                 DataTypeCode.XML, DataTypeCode.CONTROLLEDVOCABULARY, DataTypeCode.MATERIAL,
-                DataTypeCode.HYPERLINK, DataTypeCode.TIMESTAMP)
+                DataTypeCode.HYPERLINK)
         {
             @Override
             public ISerializableComparable doConversion(String value)
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/task/MaterialReportingTaskTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/task/MaterialReportingTaskTest.java
index 7ad1e3ca7f8a01e6cc85245f4115a71c3ba62fca..0c2cefdecaa299f513162d3e7fd54119184aaa16 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/task/MaterialReportingTaskTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/task/MaterialReportingTaskTest.java
@@ -21,12 +21,15 @@ import java.sql.Connection;
 import java.sql.SQLException;
 import java.sql.Statement;
 import java.util.Arrays;
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
+import java.util.TreeMap;
 
 import org.jmock.Expectations;
 import org.jmock.Mockery;
+import org.jmock.Sequence;
 import org.springframework.jdbc.core.ColumnMapRowMapper;
 import org.springframework.jdbc.core.JdbcTemplate;
 import org.testng.annotations.AfterMethod;
@@ -42,9 +45,11 @@ import ch.systemsx.cisd.common.test.RecordingMatcher;
 import ch.systemsx.cisd.dbmigration.DatabaseConfigurationContext;
 import ch.systemsx.cisd.openbis.generic.server.ICommonServerForInternalUse;
 import ch.systemsx.cisd.openbis.generic.server.task.MaterialReportingTask.MappingInfo;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataTypeCode;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Material;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.builders.MaterialBuilder;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.builders.VocabularyTermBuilder;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SessionContextDTO;
 
 /**
@@ -87,7 +92,8 @@ public class MaterialReportingTaskTest extends AbstractFileSystemTestCase
         createTestDatabase();
         createTables(
                 "create table report1 (id bigint, code varchar(20), description varchar(200))",
-                "create table report2 (code varchar(20), prop1 varchar(200), prop2 varchar(200))");
+                "create table report2 (code varchar(20), rank integer, greetings varchar(200), "
+                        + "size double precision, organism varchar(100), material varchar(30), timestamp timestamp)");
         dbConfigContext.closeConnections();
         mappingFile = new File(workingDirectory, "mapping-file.txt");
         properties = new Properties();
@@ -117,7 +123,7 @@ public class MaterialReportingTaskTest extends AbstractFileSystemTestCase
         MappingInfo mappingInfo1 = mapping.get("T1");
         assertEquals("insert into table1 (code) values(?)", mappingInfo1.createInsertStatement());
         MappingInfo mappingInfo2 = mapping.get("T2");
-        assertEquals("insert into table2 (code, prop1, prop2) values(?, ?, ?)",
+        assertEquals("insert into table2 (code, prop2, prop1) values(?, ?, ?)",
                 mappingInfo2.createInsertStatement());
         assertEquals(2, mapping.size());
     }
@@ -326,17 +332,24 @@ public class MaterialReportingTaskTest extends AbstractFileSystemTestCase
     }
 
     @Test
-    public void test() throws Exception
+    public void testInsert() throws Exception
     {
         FileUtilities.writeToFile(mappingFile, "# my mapping\n[T1:REPORT1,CODE]\n\n"
-                + "[T2: REPORT2, code]\nP2: prop2\nP1:prop1");
+                + "[T2: REPORT2, code]\nM:MATERIAL\nS:size\nP2: GREETINGS\nP1:RANK\nORG:ORGANISM\n"
+                + "T:timestamp");
         materialReportingTask.setUp("", properties);
         final Material m1 =
                 new MaterialBuilder().code("M1").type("T1").property("P1", "42").getMaterial();
-        final Material m2 =
-                new MaterialBuilder().code("M2").type("T2").property("P1", "42").getMaterial();
+        MaterialBuilder mb2 = new MaterialBuilder().code("M2").type("T2");
+        mb2.property("P1").type(DataTypeCode.INTEGER).value(42);
+        mb2.property("S").type(DataTypeCode.REAL).value(1e7 + 0.5);
+        mb2.property("ORG").type(DataTypeCode.CONTROLLEDVOCABULARY)
+                .value(new VocabularyTermBuilder("FLY").getTerm());
+        mb2.property("M").type(DataTypeCode.MATERIAL).value(m1);
+        mb2.property("T").type(DataTypeCode.TIMESTAMP).value(new Date(24 * 3600L * 1000L * 33));
+        final Material m2 = mb2.getMaterial();
         final Material m3 =
-                new MaterialBuilder().code("M3").type("T2").property("P2", "137").getMaterial();
+                new MaterialBuilder().code("M3").type("T2").property("P2", "hello").getMaterial();
         final RecordingMatcher<DetailedSearchCriteria> criteriaRecorder =
                 new RecordingMatcher<DetailedSearchCriteria>();
         context.checking(new Expectations()
@@ -354,18 +367,93 @@ public class MaterialReportingTaskTest extends AbstractFileSystemTestCase
 
         materialReportingTask.execute();
 
-        List<?> result =
-                new JdbcTemplate(dbConfigContext.getDataSource()).query("select * from report1",
-                        new ColumnMapRowMapper());
-        assertEquals("[{id=null, code=M1, description=null}]", result.toString());
-        result =
-                new JdbcTemplate(dbConfigContext.getDataSource()).query(
-                        "select * from report2 order by code", new ColumnMapRowMapper());
-        assertEquals("[{code=M2, prop1=null, prop2=42}, {code=M3, prop1=137, prop2=null}]",
-                result.toString());
+        List<?> result = loadTable("report1");
+        assertEquals("[{code=M1, description=null, id=null}]", result.toString());
+        result = loadTable("report2");
+        assertEquals("[{code=M2, greetings=null, material=M1, organism=FLY, rank=42, "
+                + "size=1.00000005E7, timestamp=1970-02-03 01:00:00.0}, "
+                + "{code=M3, greetings=hello, material=null, organism=null, rank=null, "
+                + "size=null, timestamp=null}]", result.toString());
         context.assertIsSatisfied();
     }
 
+    @Test
+    public void testInsertUpdate() throws Exception
+    {
+        FileUtilities.writeToFile(mappingFile, "[T1:REPORT1,CODE]\n"
+                + "[T2: REPORT2, code]\nM:MATERIAL\nS:size\nP1:RANK\nORG:ORGANISM\n"
+                + "P2: GREETINGS\nT:timestamp");
+        materialReportingTask.setUp("", properties);
+        final Material m1 =
+                new MaterialBuilder().code("M1").type("T1").property("P1", "42").getMaterial();
+        MaterialBuilder mb2 = new MaterialBuilder().code("M2").type("T2");
+        mb2.property("P1").type(DataTypeCode.INTEGER).value(42);
+        final Material m2 = mb2.getMaterial();
+        final Material m3 =
+                new MaterialBuilder().code("M3").type("T2").property("P2", "hello").getMaterial();
+        MaterialBuilder mb2v2 = new MaterialBuilder().code("M2").type("T2");
+        mb2v2.property("P1").type(DataTypeCode.INTEGER).value(137);
+        mb2v2.property("ORG").type(DataTypeCode.CONTROLLEDVOCABULARY)
+                .value(new VocabularyTermBuilder("TIGER").label("Tiger").getTerm());
+        final Material m2v2 = mb2v2.property("P2", "blabla").getMaterial();
+        final Material m4 =
+                new MaterialBuilder().code("M4").type("T2").property("P2", "hi").getMaterial();
+        final RecordingMatcher<DetailedSearchCriteria> criteriaRecorder =
+                new RecordingMatcher<DetailedSearchCriteria>();
+        final Sequence sequence = context.sequence("materials");
+        context.checking(new Expectations()
+            {
+                {
+                    exactly(2).of(server).tryToAuthenticateAsSystem();
+                    SessionContextDTO session = new SessionContextDTO();
+                    session.setSessionToken(SESSION_TOKEN);
+                    will(returnValue(session));
+
+                    one(server).searchForMaterials(with(SESSION_TOKEN), with(criteriaRecorder));
+                    will(returnValue(Arrays.asList(m1, m2, m3)));
+                    inSequence(sequence);
+
+                    one(server).searchForMaterials(with(SESSION_TOKEN), with(criteriaRecorder));
+                    will(returnValue(Arrays.asList(m1, m2v2, m4)));
+                    inSequence(sequence);
+                }
+            });
+
+        materialReportingTask.execute();
+        materialReportingTask.execute();
+
+        List<?> result = loadTable("report1");
+        assertEquals("[{code=M1, description=null, id=null}]", result.toString());
+        result = loadTable("report2");
+        assertEquals("[{code=M2, greetings=blabla, material=null, organism=Tiger, "
+                + "rank=137, size=null, timestamp=null}, "
+                + "{code=M3, greetings=hello, material=null, organism=null, "
+                + "rank=null, size=null, timestamp=null}, "
+                + "{code=M4, greetings=hi, material=null, organism=null, "
+                + "rank=null, size=null, timestamp=null}]", result.toString());
+        context.assertIsSatisfied();
+    }
+
+    private List<?> loadTable(String tableName)
+    {
+        return new JdbcTemplate(dbConfigContext.getDataSource()).query("select * from " + tableName
+                + " order by code", new ColumnMapRowMapper()
+            {
+                @SuppressWarnings("rawtypes")
+                @Override
+                protected Map createColumnMap(int columnCount)
+                {
+                    return new TreeMap();
+                }
+
+                @Override
+                protected String getColumnKey(String columnName)
+                {
+                    return columnName.toLowerCase();
+                }
+            });
+    }
+
     private void createTables(String... creationStatements) throws Exception
     {
         Connection connection = null;
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/builders/VocabularyTermBuilder.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/builders/VocabularyTermBuilder.java
index 493852e792a90520fdfad4f1b65992e0960da2bc..251980543a17de8a472ada80c01bf14c57e4d0f7 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/builders/VocabularyTermBuilder.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/builders/VocabularyTermBuilder.java
@@ -20,21 +20,27 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.VocabularyTerm;
 
 /**
  * Builder for {@link VocabularyTerm} instance.
- *
+ * 
  * @author Franz-Josef Elmer
  */
 public class VocabularyTermBuilder
 {
     private final VocabularyTerm term = new VocabularyTerm();
-    
+
     public VocabularyTermBuilder(String code)
     {
         term.setCode(code);
     }
 
+    public VocabularyTermBuilder label(String label)
+    {
+        term.setLabel(label);
+        return this;
+    }
+
     public VocabularyTerm getTerm()
     {
         return term;
     }
-    
+
 }