Index: main/waeup.kofa/trunk/src/waeup/kofa/university/batching.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/university/batching.py	(revision 9216)
+++ main/waeup.kofa/trunk/src/waeup/kofa/university/batching.py	(revision 9333)
@@ -211,56 +211,4 @@
         return
 
-class CourseProcessor(FacultyProcessor):
-    """A batch processor for ICourse objects.
-    """
-    grok.implements(IBatchProcessor)
-    grok.provides(IBatchProcessor)
-    grok.context(Interface)
-    util_name = 'courseprocessor'
-    grok.name(util_name)
-
-    name = u'Course Processor'
-    iface = ICourse
-    allowed_roles = Course.local_roles
-
-    location_fields = ['code', 'faculty_code', 'department_code']
-    factory_name = 'waeup.Course'
-
-    mode = None
-
-    def parentsExist(self, row, site):
-        if not 'faculties' in site.keys():
-            return False
-        if not row['faculty_code'] in site['faculties'].keys():
-            return False
-        faculty = site['faculties'][row['faculty_code']]
-        return row['department_code'] in faculty.keys()
-
-    def entryExists(self, row, site):
-        if not self.parentsExist(row, site):
-            return False
-        parent = self.getParent(row, site)
-        return row['code'] in parent.keys()
-
-    def getParent(self, row, site):
-        dept = site['faculties'][row['faculty_code']][row['department_code']]
-        return dept.courses
-
-    def getEntry(self, row, site):
-        if not self.entryExists(row, site):
-            return None
-        parent = self.getParent(row, site)
-        return parent.get(row['code'])
-
-    def addEntry(self, obj, row, site):
-        parent = self.getParent(row, site)
-        parent.addCourse(obj)
-        return
-
-    def delEntry(self, row, site):
-        parent = self.getParent(row, site)
-        del parent[row['code']]
-        return
-
 class CertificateProcessor(FacultyProcessor):
     """A batch processor for ICertificate objects.
@@ -289,18 +237,5 @@
 
     def checkHeaders(self, headerfields, mode='create'):
-        req = self.req[mode]
-        # Check for required fields...
-        for field in req:
-            if not field in headerfields:
-                raise FatalCSVError(
-                    "Need at least columns %s for import!" %
-                    ', '.join(["'%s'" % x for x in req]))
-        # Check for double fields. Cannot happen because this error is
-        # already catched in views
-        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.")
+        super(CertificateProcessor, self).checkHeaders(headerfields, mode)
         if mode == 'create':
             if not 'faculty_code' in headerfields \
@@ -359,4 +294,51 @@
         return
 
+class CourseProcessor(CertificateProcessor):
+    """A batch processor for ICourse objects.
+    """
+    grok.implements(IBatchProcessor)
+    grok.provides(IBatchProcessor)
+    grok.context(Interface)
+    util_name = 'courseprocessor'
+    grok.name(util_name)
+
+    name = u'Course Processor'
+    iface = ICourse
+    allowed_roles = Course.local_roles
+
+    location_fields = ['code']
+    factory_name = 'waeup.Course'
+
+    mode = None
+
+    def getParent(self, row, site):
+        if not 'faculties' in site.keys():
+            return None
+        # If both faculty and department codes are provided, use
+        # these to get parent.
+        if row.get('faculty_code',None) not in (None, IGNORE_MARKER) and \
+            row.get('department_code',None) not in (None, IGNORE_MARKER):
+            if not row['faculty_code'] in site['faculties'].keys():
+                return None
+            faculty = site['faculties'][row['faculty_code']]
+            if not row['department_code'] in faculty.keys():
+                return None
+            dept = faculty[row['department_code']]
+            return dept.courses
+        # If department code or faculty code is missing,
+        # use catalog to get parent. Makes only sense in update mode but
+        # does also work in create mode.
+        cat = queryUtility(ICatalog, name='courses_catalog')
+        results = list(
+            cat.searchResults(code=(row['code'], row['code'])))
+        if results:
+            return results[0].__parent__
+        return None
+
+    def addEntry(self, obj, row, site):
+        parent = self.getParent(row, site)
+        parent.addCourse(obj)
+        return
+
 class CertificateCourseProcessor(FacultyProcessor):
     """A batch processor for ICertificateCourse objects.
