Index: main/waeup.sirp/trunk/src/waeup/sirp/applicants/batching.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/applicants/batching.py	(revision 7266)
+++ main/waeup.sirp/trunk/src/waeup/sirp/applicants/batching.py	(revision 7268)
@@ -18,10 +18,13 @@
 """Batch processing for applicants.
 """
+import csv
 import grok
 from zope.interface import Interface
-from waeup.sirp.interfaces import IBatchProcessor
+from zope.component import queryUtility
+from zope.catalog.interfaces import ICatalog
+from waeup.sirp.interfaces import IBatchProcessor, IObjectConverter
 from waeup.sirp.utils.batching import BatchProcessor
 from waeup.sirp.applicants.interfaces import (
-    IApplicantsContainer, IApplicant)
+    IApplicantsContainer, IApplicant, IApplicantUpdateByRegNo)
 
 class ApplicantsContainerImporter(BatchProcessor):
@@ -78,27 +81,49 @@
     location_fields = []
     factory_name = 'waeup.Applicant'
-    location_fields = ['container_code', 'application_number']
+    location_fields = ['container_code']
 
     mode = None
 
     @property
-    def req(self):
-        result = dict(
-            create = ['container_code'] + self.required_fields,
-            update = self.location_fields,
-            remove = self.location_fields,
-        )
-        return result
+    def available_fields(self):
+        return sorted(list(set(
+            ['application_number','reg_no'] + getFields(
+                self.iface).keys())))
+
+    def checkHeaders(self, headerfields, mode='create'):
+        if not 'reg_no' in headerfields and not 'application_number' \
+            in headerfields:
+            raise FatalCSVError(
+                "Need at least columns application_number or reg_no ")
+        if mode == 'create':
+            for field in self.required_fields:
+                if not field in headerfields:
+                    raise FatalCSVError(
+                        "Need at least columns %s for import!" %
+                        ', '.join(["'%s'" % x for x in self.required_fields]))
+        # Check for fields to be ignored...
+        not_ignored_fields = [x for x in headerfields
+                              if not x.startswith('--')]
+        if len(set(not_ignored_fields)) < len(not_ignored_fields):
+            raise FatalCSVError(
+                "Double headers: each column name may only appear once.")
+        return True
+
+    def getLocator(self, row):
+        #if 'application_number' in row.keys() and row['application_number']:
+        if row.get('application_number',None):
+            return 'application_number'
+        elif 'reg_no' in row.keys() and row['reg_no']:
+            return 'reg_no'
+        else:
+            return None
+
+    def getParent(self, row, site):
+        if not 'applicants' in site.keys():
+            return False
+        return site['applicants'][row['container_code']]
 
     def parentsExist(self, row, site):
-        if not 'applicants' in site.keys():
-            return False
-        return row['container_code'] in site['applicants'].keys()
-
-    def entryExists(self, row, site):
-        return self.getEntry(row, site) is not None
-
-    def getParent(self, row, site):
-        return site['applicants'][row['container_code']]
+        return self.getParent(row, site) is not None
 
     def getEntry(self, row, site):
@@ -106,5 +131,19 @@
             return None
         parent = self.getParent(row, site)
-        return parent.get(row.get('application_number', None), None)
+        if self.getLocator(row) == 'application_number':
+            if row['application_number'] in parent:
+                applicant = parent[row['application_number']]
+                return applicant
+        elif self.getLocator(row) == 'reg_no':
+            reg_no = row['reg_no']
+            cat = queryUtility(ICatalog, name='applicants_catalog')
+            results = list(
+                cat.searchResults(reg_no=(reg_no, reg_no)))
+            if results:
+                return results[0]
+        return None
+
+    def entryExists(self, row, site):
+        return self.getEntry(row, site) is not None
 
     def addEntry(self, obj, row, site):
@@ -114,6 +153,36 @@
 
     def delEntry(self, row, site):
-        if self.entryExists(row, site):
+        applicant = self.getEntry(row, site)
+        if applicant is not None:
             parent = self.getParent(row, site)
-            del parent[row['application_number']]
+            del parent[applicant.application_number]
         pass
+
+    def getMapping(self, path, headerfields, mode):
+        """Get a mapping from CSV file headerfields to actually used fieldnames.
+        """
+        result = dict()
+        reader = csv.reader(open(path, 'rb'))
+        raw_header = reader.next()
+        for num, field in enumerate(headerfields):
+            if field not in ['container_code',
+                'application_number', 'reg_no'] and mode == 'remove':
+                continue
+            if field == u'--IGNORE--':
+                # Skip ignored columns in failed and finished data files.
+                continue
+            result[raw_header[num]] = field
+        return result
+
+    def checkConversion(self, row, mode='create'):
+        """Validates all values in row.
+        """
+        if mode in ['update', 'remove']:
+            if self.getLocator(row) == 'reg_no':
+                iface = IApplicantUpdateByRegNo
+        else:
+            iface = self.iface
+        converter = IObjectConverter(iface)
+        errs, inv_errs, conv_dict =  converter.fromStringDict(
+            row, self.factory_name)
+        return errs, inv_errs, conv_dict
Index: main/waeup.sirp/trunk/src/waeup/sirp/applicants/interfaces.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/applicants/interfaces.py	(revision 7266)
+++ main/waeup.sirp/trunk/src/waeup/sirp/applicants/interfaces.py	(revision 7268)
@@ -443,4 +443,16 @@
         )
 
+class IApplicantUpdateByRegNo(IApplicant):
+    """Representation of an applicant.
+
+    Skip regular reg_no validation if reg_no is used for finding
+    the applicant object.
+    """
+    reg_no = schema.TextLine(
+        title = u'Registration Number',
+        default = None,
+        required = False,
+        )
+
 class IApplicantOnlinePayment(IOnlinePayment):
     """An applicant payment via payment gateways.
