Index: /main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_batching.py
===================================================================
--- /main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_batching.py	(revision 9293)
+++ /main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_batching.py	(revision 9294)
@@ -574,4 +574,366 @@
         shutil.rmtree(os.path.dirname(fin_file))
 
+class StudentStudyLevelProcessorTest(StudentImportExportSetup):
+
+    def setUp(self):
+        super(StudentStudyLevelProcessorTest, self).setUp()
+
+        # Import students with subobjects
+        student_file = os.path.join(self.workdir, 'sample_student_data.csv')
+        open(student_file, 'wb').write(STUDENT_SAMPLE_DATA)
+        num, num_warns, fin_file, fail_file = StudentProcessor().doImport(
+            student_file, STUDENT_HEADER_FIELDS)
+        shutil.rmtree(os.path.dirname(fin_file))
+
+        # Update study courses
+        studycourse_file = os.path.join(
+            self.workdir, 'sample_studycourse_data.csv')
+        open(studycourse_file, 'wb').write(STUDYCOURSE_SAMPLE_DATA)
+        processor = StudentStudyCourseProcessor()
+        num, num_warns, fin_file, fail_file = processor.doImport(
+            studycourse_file, STUDYCOURSE_HEADER_FIELDS,'update')
+        shutil.rmtree(os.path.dirname(fin_file))
+
+        self.processor = StudentStudyLevelProcessor()
+        self.csv_file = os.path.join(
+            self.workdir, 'sample_studylevel_data.csv')
+        open(self.csv_file, 'wb').write(STUDYLEVEL_SAMPLE_DATA)
+
+    def test_interface(self):
+        # Make sure we fulfill the interface contracts.
+        assert verifyObject(IBatchProcessor, self.processor) is True
+        assert verifyClass(
+            IBatchProcessor, StudentStudyLevelProcessor) is True
+
+    def test_checkConversion(self):
+        errs, inv_errs, conv_dict = self.processor.checkConversion(
+            dict(reg_number='1', level='220'))
+        self.assertEqual(len(errs),0)
+        errs, inv_errs, conv_dict = self.processor.checkConversion(
+            dict(reg_number='1', level='900'))
+        self.assertEqual(len(errs),1)
+        self.assertTrue(('level','no valid integer') in errs)
+        errs, inv_errs, conv_dict = self.processor.checkConversion(
+            dict(reg_number='1', level='xyz'))
+        self.assertEqual(len(errs),1)
+        self.assertTrue(('level','no integer') in errs)
+
+    def test_import(self):
+        num, num_warns, fin_file, fail_file = self.processor.doImport(
+            self.csv_file, STUDYLEVEL_HEADER_FIELDS,'create')
+        self.assertEqual(num_warns,2)
+        assert self.processor.entryExists(
+            dict(reg_number='1', level='100'), self.app) is True
+        studylevel = self.processor.getEntry(
+            dict(reg_number='1', level='100'), self.app)
+        self.assertEqual(studylevel.__parent__.certificate.code, u'CERT1')
+        self.assertEqual(studylevel.level_session, 2008)
+        self.assertEqual(studylevel.level_verdict, None)
+        self.assertEqual(studylevel.level, 100)
+        shutil.rmtree(os.path.dirname(fin_file))
+
+        logcontent = open(self.logfile).read()
+        # Logging message from updateEntry,
+        self.assertTrue(
+            'INFO - system - K1000000 - Study level '
+            'updated: level=100, level_verdict=C, level_session=2009'
+            in logcontent)
+
+    def test_import_update(self):
+        # We perform the same import twice,
+        # the second time in update mode. The number
+        # of warnings must be the same.
+        num, num_warns, fin_file, fail_file = self.processor.doImport(
+            self.csv_file, STUDYLEVEL_HEADER_FIELDS,'create')
+        shutil.rmtree(os.path.dirname(fin_file))
+        num, num_warns, fin_file, fail_file = self.processor.doImport(
+            self.csv_file, STUDYLEVEL_HEADER_FIELDS,'update')
+        self.assertEqual(num_warns,2)
+        shutil.rmtree(os.path.dirname(fin_file))
+
+class CourseTicketProcessorTest(StudentImportExportSetup):
+
+    def setUp(self):
+        super(CourseTicketProcessorTest, self).setUp()
+
+        # Import students with subobjects
+        student_file = os.path.join(self.workdir, 'sample_student_data.csv')
+        open(student_file, 'wb').write(STUDENT_SAMPLE_DATA)
+        num, num_warns, fin_file, fail_file = StudentProcessor().doImport(
+            student_file, STUDENT_HEADER_FIELDS)
+        shutil.rmtree(os.path.dirname(fin_file))
+
+        # Add course and certificate course
+        self.course = createObject('waeup.Course')
+        self.course.code = 'COURSE1'
+        self.course.semester = 1
+        self.course.credits = 10
+        self.course.passmark = 40
+        self.app['faculties']['fac1']['dep1'].courses.addCourse(
+            self.course)
+        self.app['faculties']['fac1']['dep1'].certificates[
+            'CERT1'].addCertCourse(
+            self.course, level=100)
+
+        # Update study courses
+        studycourse_file = os.path.join(
+            self.workdir, 'sample_studycourse_data.csv')
+        open(studycourse_file, 'wb').write(STUDYCOURSE_SAMPLE_DATA)
+        processor = StudentStudyCourseProcessor()
+        num, num_warns, fin_file, fail_file = processor.doImport(
+            studycourse_file, STUDYCOURSE_HEADER_FIELDS,'update')
+        shutil.rmtree(os.path.dirname(fin_file))
+
+        # Import study levels
+        processor = StudentStudyLevelProcessor()
+        studylevel_file = os.path.join(
+            self.workdir, 'sample_studylevel_data.csv')
+        open(studylevel_file, 'wb').write(STUDYLEVEL_SAMPLE_DATA)
+        num, num_warns, fin_file, fail_file = processor.doImport(
+            studylevel_file, STUDYLEVEL_HEADER_FIELDS,'create')
+        shutil.rmtree(os.path.dirname(fin_file))
+
+        self.processor = CourseTicketProcessor()
+        self.csv_file = os.path.join(
+            self.workdir, 'sample_courseticket_data.csv')
+        open(self.csv_file, 'wb').write(COURSETICKET_SAMPLE_DATA)
+
+    def test_interface(self):
+        # Make sure we fulfill the interface contracts.
+        assert verifyObject(IBatchProcessor, self.processor) is True
+        assert verifyClass(
+            IBatchProcessor, CourseTicketProcessor) is True
+
+    def test_checkConversion(self):
+        errs, inv_errs, conv_dict = self.processor.checkConversion(
+            dict(reg_number='1', code='COURSE1', level='220'))
+        self.assertEqual(len(errs),0)
+        errs, inv_errs, conv_dict = self.processor.checkConversion(
+            dict(reg_number='1', code='COURSE2', level='220'))
+        self.assertEqual(len(errs),1)
+        self.assertTrue(('code','non-existent') in errs)
+
+    def test_import(self):
+
+        num, num_warns, fin_file, fail_file = self.processor.doImport(
+            self.csv_file, COURSETICKET_HEADER_FIELDS,'create')
+
+        self.assertEqual(num_warns,2)
+        assert self.processor.entryExists(
+            dict(reg_number='1', level='100', code='COURSE1'),
+            self.app) is True
+        courseticket = self.processor.getEntry(
+            dict(reg_number='1', level='100', code='COURSE1'), self.app)
+        self.assertEqual(courseticket.__parent__.__parent__.certificate.code,
+                         u'CERT1')
+        self.assertEqual(courseticket.score, 1)
+        self.assertEqual(courseticket.mandatory, True)
+        self.assertEqual(courseticket.fcode, 'NA')
+        self.assertEqual(courseticket.dcode, 'NA')
+        self.assertEqual(courseticket.code, 'COURSE1')
+        self.assertEqual(courseticket.title, 'Unnamed Course')
+        self.assertEqual(courseticket.credits, 10)
+        self.assertEqual(courseticket.passmark, 40)
+        self.assertEqual(courseticket.semester, 1)
+        shutil.rmtree(os.path.dirname(fin_file))
+
+        logcontent = open(self.logfile).read()
+        # Logging message from updateEntry,
+        self.assertTrue(
+            'INFO - system - K1000000 - Course ticket in 100 updated: code=COURSE1, '
+            'mandatory=False, score=3'
+            in logcontent)
+
+    def test_import_update(self):
+        # We perform the same import twice,
+        # the second time in update mode. The number
+        # of warnings must be the same.
+        num, num_warns, fin_file, fail_file = self.processor.doImport(
+            self.csv_file, COURSETICKET_HEADER_FIELDS,'create')
+        shutil.rmtree(os.path.dirname(fin_file))
+        num, num_warns, fin_file, fail_file = self.processor.doImport(
+            self.csv_file, COURSETICKET_HEADER_FIELDS,'update')
+        self.assertEqual(num_warns,2)
+        shutil.rmtree(os.path.dirname(fin_file))
+
+    def test_import_remove(self):
+        # We perform the same import twice,
+        # the second time in remove mode. The number
+        # of warnings must be the same.
+        num, num_warns, fin_file, fail_file = self.processor.doImport(
+            self.csv_file, COURSETICKET_HEADER_FIELDS,'create')
+        shutil.rmtree(os.path.dirname(fin_file))
+        assert self.processor.entryExists(
+            dict(reg_number='1', level='100', code='COURSE1'), self.app) is True
+        num, num_warns, fin_file, fail_file = self.processor.doImport(
+            self.csv_file, COURSETICKET_HEADER_FIELDS,'remove')
+        self.assertEqual(num_warns,2)
+        assert self.processor.entryExists(
+            dict(reg_number='1', level='100', code='COURSE1'), self.app) is False
+        shutil.rmtree(os.path.dirname(fin_file))
+        logcontent = open(self.logfile).read()
+        self.assertTrue(
+            'INFO - system - K1000000 - Course ticket in 100 removed: COURSE1'
+            in logcontent)
+
+class PaymentProcessorTest(StudentImportExportSetup):
+
+    def setUp(self):
+        super(PaymentProcessorTest, self).setUp()
+
+        # Add student with payment
+        student = Student()
+        student.firstname = u'Anna'
+        student.lastname = u'Tester'
+        student.reg_number = u'123'
+        student.matric_number = u'234'
+        self.app['students'].addStudent(student)
+        self.student = self.app['students'][student.student_id]
+        payment = createObject(u'waeup.StudentOnlinePayment')
+        payment.p_id = 'p120'
+        self.student['payments'][payment.p_id] = payment
+
+        # Import students with subobjects
+        student_file = os.path.join(self.workdir, 'sample_student_data.csv')
+        open(student_file, 'wb').write(STUDENT_SAMPLE_DATA)
+        num, num_warns, fin_file, fail_file = StudentProcessor().doImport(
+            student_file, STUDENT_HEADER_FIELDS)
+        shutil.rmtree(os.path.dirname(fin_file))
+
+        self.processor = StudentOnlinePaymentProcessor()
+        self.csv_file = os.path.join(
+            self.workdir, 'sample_payment_data.csv')
+        open(self.csv_file, 'wb').write(PAYMENT_SAMPLE_DATA)
+        self.csv_file2 = os.path.join(
+            self.workdir, 'sample_create_payment_data.csv')
+        open(self.csv_file2, 'wb').write(PAYMENT_CREATE_SAMPLE_DATA)
+
+    def test_interface(self):
+        # Make sure we fulfill the interface contracts.
+        assert verifyObject(IBatchProcessor, self.processor) is True
+        assert verifyClass(
+            IBatchProcessor, StudentOnlinePaymentProcessor) is True
+
+    def test_getEntry(self):
+        assert self.processor.getEntry(
+            dict(student_id='ID_NONE', p_id='nonsense'), self.app) is None
+        assert self.processor.getEntry(
+            dict(student_id=self.student.student_id, p_id='p120'),
+            self.app) is self.student['payments']['p120']
+        assert self.processor.getEntry(
+            dict(student_id=self.student.student_id, p_id='XXXXXX112'),
+            self.app) is self.student['payments']['p120']
+
+    def test_delEntry(self):
+        assert self.processor.getEntry(
+            dict(student_id=self.student.student_id, p_id='p120'),
+            self.app) is self.student['payments']['p120']
+        self.assertEqual(len(self.student['payments'].keys()),1)
+        self.processor.delEntry(
+            dict(student_id=self.student.student_id, p_id='p120'),
+            self.app)
+        assert self.processor.getEntry(
+            dict(student_id=self.student.student_id, p_id='p120'),
+            self.app) is None
+        self.assertEqual(len(self.student['payments'].keys()),0)
+
+    def test_addEntry(self):
+        self.assertEqual(len(self.student['payments'].keys()),1)
+        payment1 = createObject(u'waeup.StudentOnlinePayment')
+        payment1.p_id = 'p234'
+        self.processor.addEntry(
+            payment1, dict(student_id=self.student.student_id, p_id='p234'),
+            self.app)
+        self.assertEqual(len(self.student['payments'].keys()),2)
+        self.assertEqual(self.student['payments']['p234'].p_id, 'p234')
+        payment2 = createObject(u'waeup.StudentOnlinePayment')
+        payment1.p_id = 'nonsense'
+        # payment1.p_id will be replaced if p_id doesn't start with 'p'
+        self.processor.addEntry(
+            payment2, dict(student_id=self.student.student_id, p_id='XXXXXX456'),
+            self.app)
+        self.assertEqual(len(self.student['payments'].keys()),3)
+        self.assertEqual(self.student['payments']['p560'].p_id, 'p560')
+
+    def test_checkConversion(self):
+        errs, inv_errs, conv_dict = self.processor.checkConversion(
+            dict(p_id='3816951266236341955'))
+        self.assertEqual(len(errs),0)
+        errs, inv_errs, conv_dict = self.processor.checkConversion(
+            dict(p_id='p1266236341955'))
+        self.assertEqual(len(errs),0)
+        errs, inv_errs, conv_dict = self.processor.checkConversion(
+            dict(p_id='nonsense'))
+        self.assertEqual(len(errs),1)
+        timestamp = ("%d" % int(time()*10000))[1:]
+        p_id = "p%s" % timestamp
+        errs, inv_errs, conv_dict = self.processor.checkConversion(
+            dict(p_id=p_id))
+        self.assertEqual(len(errs),0)
+
+    def test_import(self):
+        num, num_warns, fin_file, fail_file = self.processor.doImport(
+            self.csv_file, PAYMENT_HEADER_FIELDS,'create')
+        self.assertEqual(num_warns,0)
+        payment = self.processor.getEntry(dict(reg_number='1',
+            p_id='p2907979737440'), self.app)
+        self.assertEqual(payment.p_id, 'p2907979737440')
+        self.assertTrue(payment.p_current)
+        cdate = payment.creation_date.strftime("%Y-%m-%d %H:%M:%S")
+        self.assertEqual(cdate, "2010-11-26 18:59:33")
+        self.assertEqual(str(payment.creation_date.tzinfo),'UTC')
+        payment = self.processor.getEntry(dict(matric_number='100001',
+            p_id='p2907125937570'), self.app)
+        self.assertEqual(payment.p_id, 'p2907125937570')
+        self.assertFalse(payment.p_current)
+        cdate = payment.creation_date.strftime("%Y-%m-%d %H:%M:%S")
+        # Ooooh, still the old problem, see
+        # http://mail.dzug.org/mailman/archives/zope/2006-August/001153.html.
+        # WAT is interpreted as GMT-1 and not GMT+1
+        self.assertEqual(cdate, "2010-11-25 21:16:33")
+        self.assertEqual(str(payment.creation_date.tzinfo),'UTC')
+        shutil.rmtree(os.path.dirname(fin_file))
+        logcontent = open(self.logfile).read()
+        # Logging message from updateEntry
+        self.assertTrue(
+            'INFO - system - K1000001 - Payment ticket updated: '
+            'p_item=BTECHBDT, creation_date=2010-02-15 13:19:01+00:00, '
+            'p_category=schoolfee, amount_auth=19500.0, p_current=True, '
+            'p_id=p1266236341955, r_code=00, r_amount_approved=19500.0, '
+            'p_state=paid'
+            in logcontent)
+
+    def test_import_update(self):
+        # We perform the same import twice,
+        # the second time in update mode. The number
+        # of warnings must be the same.
+        num, num_warns, fin_file, fail_file = self.processor.doImport(
+            self.csv_file, PAYMENT_HEADER_FIELDS,'create')
+        shutil.rmtree(os.path.dirname(fin_file))
+        num, num_warns, fin_file, fail_file = self.processor.doImport(
+            self.csv_file, PAYMENT_HEADER_FIELDS,'update')
+        self.assertEqual(num_warns,0)
+        shutil.rmtree(os.path.dirname(fin_file))
+
+    def test_import_remove(self):
+        num, num_warns, fin_file, fail_file = self.processor.doImport(
+            self.csv_file, PAYMENT_HEADER_FIELDS,'create')
+        shutil.rmtree(os.path.dirname(fin_file))
+        num, num_warns, fin_file, fail_file = self.processor.doImport(
+            self.csv_file, PAYMENT_HEADER_FIELDS,'remove')
+        self.assertEqual(num_warns,0)
+        shutil.rmtree(os.path.dirname(fin_file))
+        logcontent = open(self.logfile).read()
+        self.assertTrue(
+            'INFO - system - K1000001 - Payment ticket removed: p1266236341955'
+            in logcontent)
+
+    def test_import_wo_pid(self):
+        num, num_warns, fin_file, fail_file = self.processor.doImport(
+            self.csv_file2, PAYMENT_CREATE_HEADER_FIELDS,'create')
+        self.assertEqual(num_warns,0)
+        shutil.rmtree(os.path.dirname(fin_file))
+        self.assertEqual(len(self.app['students']['X666666']['payments']), 50)
+
 class StudentVerdictProcessorTest(StudentImportExportSetup):
 
@@ -640,369 +1002,13 @@
             '2008,A,True,100007,200,Study level object is missing.\r\n'
             )
-        shutil.rmtree(os.path.dirname(fin_file))
-
-
-class StudentStudyLevelProcessorTest(StudentImportExportSetup):
-
-    def setUp(self):
-        super(StudentStudyLevelProcessorTest, self).setUp()
-
-        # Import students with subobjects
-        student_file = os.path.join(self.workdir, 'sample_student_data.csv')
-        open(student_file, 'wb').write(STUDENT_SAMPLE_DATA)
-        num, num_warns, fin_file, fail_file = StudentProcessor().doImport(
-            student_file, STUDENT_HEADER_FIELDS)
-        shutil.rmtree(os.path.dirname(fin_file))
-
-        # Update study courses
-        studycourse_file = os.path.join(
-            self.workdir, 'sample_studycourse_data.csv')
-        open(studycourse_file, 'wb').write(STUDYCOURSE_SAMPLE_DATA)
-        processor = StudentStudyCourseProcessor()
-        num, num_warns, fin_file, fail_file = processor.doImport(
-            studycourse_file, STUDYCOURSE_HEADER_FIELDS,'update')
-        shutil.rmtree(os.path.dirname(fin_file))
-
-        self.processor = StudentStudyLevelProcessor()
-        self.csv_file = os.path.join(
-            self.workdir, 'sample_studylevel_data.csv')
-        open(self.csv_file, 'wb').write(STUDYLEVEL_SAMPLE_DATA)
-
-    def test_interface(self):
-        # Make sure we fulfill the interface contracts.
-        assert verifyObject(IBatchProcessor, self.processor) is True
-        assert verifyClass(
-            IBatchProcessor, StudentStudyLevelProcessor) is True
-
-    def test_checkConversion(self):
-        errs, inv_errs, conv_dict = self.processor.checkConversion(
-            dict(reg_number='1', level='220'))
-        self.assertEqual(len(errs),0)
-        errs, inv_errs, conv_dict = self.processor.checkConversion(
-            dict(reg_number='1', level='900'))
-        self.assertEqual(len(errs),1)
-        self.assertTrue(('level','no valid integer') in errs)
-        errs, inv_errs, conv_dict = self.processor.checkConversion(
-            dict(reg_number='1', level='xyz'))
-        self.assertEqual(len(errs),1)
-        self.assertTrue(('level','no integer') in errs)
-
-    def test_import(self):
-        num, num_warns, fin_file, fail_file = self.processor.doImport(
-            self.csv_file, STUDYLEVEL_HEADER_FIELDS,'create')
-        self.assertEqual(num_warns,2)
-        assert self.processor.entryExists(
-            dict(reg_number='1', level='100'), self.app) is True
-        studylevel = self.processor.getEntry(
-            dict(reg_number='1', level='100'), self.app)
-        self.assertEqual(studylevel.__parent__.certificate.code, u'CERT1')
-        self.assertEqual(studylevel.level_session, 2008)
-        self.assertEqual(studylevel.level_verdict, None)
-        self.assertEqual(studylevel.level, 100)
-        shutil.rmtree(os.path.dirname(fin_file))
-
         logcontent = open(self.logfile).read()
-        # Logging message from updateEntry,
-        self.assertTrue(
-            'INFO - system - K1000000 - Study level '
-            'updated: level=100, level_verdict=C, level_session=2009'
-            in logcontent)
-
-    def test_import_update(self):
-        # We perform the same import twice,
-        # the second time in update mode. The number
-        # of warnings must be the same.
-        num, num_warns, fin_file, fail_file = self.processor.doImport(
-            self.csv_file, STUDYLEVEL_HEADER_FIELDS,'create')
-        shutil.rmtree(os.path.dirname(fin_file))
-        num, num_warns, fin_file, fail_file = self.processor.doImport(
-            self.csv_file, STUDYLEVEL_HEADER_FIELDS,'update')
-        self.assertEqual(num_warns,2)
-        shutil.rmtree(os.path.dirname(fin_file))
-
-class CourseTicketProcessorTest(StudentImportExportSetup):
-
-    def setUp(self):
-        super(CourseTicketProcessorTest, self).setUp()
-
-        # Import students with subobjects
-        student_file = os.path.join(self.workdir, 'sample_student_data.csv')
-        open(student_file, 'wb').write(STUDENT_SAMPLE_DATA)
-        num, num_warns, fin_file, fail_file = StudentProcessor().doImport(
-            student_file, STUDENT_HEADER_FIELDS)
-        shutil.rmtree(os.path.dirname(fin_file))
-
-        # Add course and certificate course
-        self.course = createObject('waeup.Course')
-        self.course.code = 'COURSE1'
-        self.course.semester = 1
-        self.course.credits = 10
-        self.course.passmark = 40
-        self.app['faculties']['fac1']['dep1'].courses.addCourse(
-            self.course)
-        self.app['faculties']['fac1']['dep1'].certificates[
-            'CERT1'].addCertCourse(
-            self.course, level=100)
-
-        # Update study courses
-        studycourse_file = os.path.join(
-            self.workdir, 'sample_studycourse_data.csv')
-        open(studycourse_file, 'wb').write(STUDYCOURSE_SAMPLE_DATA)
-        processor = StudentStudyCourseProcessor()
-        num, num_warns, fin_file, fail_file = processor.doImport(
-            studycourse_file, STUDYCOURSE_HEADER_FIELDS,'update')
-        shutil.rmtree(os.path.dirname(fin_file))
-
-        # Import study levels
-        processor = StudentStudyLevelProcessor()
-        studylevel_file = os.path.join(
-            self.workdir, 'sample_studylevel_data.csv')
-        open(studylevel_file, 'wb').write(STUDYLEVEL_SAMPLE_DATA)
-        num, num_warns, fin_file, fail_file = processor.doImport(
-            studylevel_file, STUDYLEVEL_HEADER_FIELDS,'create')
-        shutil.rmtree(os.path.dirname(fin_file))
-
-        self.processor = CourseTicketProcessor()
-        self.csv_file = os.path.join(
-            self.workdir, 'sample_courseticket_data.csv')
-        open(self.csv_file, 'wb').write(COURSETICKET_SAMPLE_DATA)
-
-    def test_interface(self):
-        # Make sure we fulfill the interface contracts.
-        assert verifyObject(IBatchProcessor, self.processor) is True
-        assert verifyClass(
-            IBatchProcessor, CourseTicketProcessor) is True
-
-    def test_checkConversion(self):
-        errs, inv_errs, conv_dict = self.processor.checkConversion(
-            dict(reg_number='1', code='COURSE1', level='220'))
-        self.assertEqual(len(errs),0)
-        errs, inv_errs, conv_dict = self.processor.checkConversion(
-            dict(reg_number='1', code='COURSE2', level='220'))
-        self.assertEqual(len(errs),1)
-        self.assertTrue(('code','non-existent') in errs)
-
-    def test_import(self):
-
-        num, num_warns, fin_file, fail_file = self.processor.doImport(
-            self.csv_file, COURSETICKET_HEADER_FIELDS,'create')
-
-        self.assertEqual(num_warns,2)
-        assert self.processor.entryExists(
-            dict(reg_number='1', level='100', code='COURSE1'),
-            self.app) is True
-        courseticket = self.processor.getEntry(
-            dict(reg_number='1', level='100', code='COURSE1'), self.app)
-        self.assertEqual(courseticket.__parent__.__parent__.certificate.code,
-                         u'CERT1')
-        self.assertEqual(courseticket.score, 1)
-        self.assertEqual(courseticket.mandatory, True)
-        self.assertEqual(courseticket.fcode, 'NA')
-        self.assertEqual(courseticket.dcode, 'NA')
-        self.assertEqual(courseticket.code, 'COURSE1')
-        self.assertEqual(courseticket.title, 'Unnamed Course')
-        self.assertEqual(courseticket.credits, 10)
-        self.assertEqual(courseticket.passmark, 40)
-        self.assertEqual(courseticket.semester, 1)
-        shutil.rmtree(os.path.dirname(fin_file))
-
-        logcontent = open(self.logfile).read()
-        # Logging message from updateEntry,
-        self.assertTrue(
-            'INFO - system - K1000000 - Course ticket in 100 updated: code=COURSE1, '
-            'mandatory=False, score=3'
-            in logcontent)
-
-    def test_import_update(self):
-        # We perform the same import twice,
-        # the second time in update mode. The number
-        # of warnings must be the same.
-        num, num_warns, fin_file, fail_file = self.processor.doImport(
-            self.csv_file, COURSETICKET_HEADER_FIELDS,'create')
-        shutil.rmtree(os.path.dirname(fin_file))
-        num, num_warns, fin_file, fail_file = self.processor.doImport(
-            self.csv_file, COURSETICKET_HEADER_FIELDS,'update')
-        self.assertEqual(num_warns,2)
-        shutil.rmtree(os.path.dirname(fin_file))
-
-    def test_import_remove(self):
-        # We perform the same import twice,
-        # the second time in remove mode. The number
-        # of warnings must be the same.
-        num, num_warns, fin_file, fail_file = self.processor.doImport(
-            self.csv_file, COURSETICKET_HEADER_FIELDS,'create')
-        shutil.rmtree(os.path.dirname(fin_file))
-        assert self.processor.entryExists(
-            dict(reg_number='1', level='100', code='COURSE1'), self.app) is True
-        num, num_warns, fin_file, fail_file = self.processor.doImport(
-            self.csv_file, COURSETICKET_HEADER_FIELDS,'remove')
-        self.assertEqual(num_warns,2)
-        assert self.processor.entryExists(
-            dict(reg_number='1', level='100', code='COURSE1'), self.app) is False
-        shutil.rmtree(os.path.dirname(fin_file))
-        logcontent = open(self.logfile).read()
-        self.assertTrue(
-            'INFO - system - K1000000 - Course ticket in 100 removed: COURSE1'
-            in logcontent)
-
-class PaymentProcessorTest(StudentImportExportSetup):
-
-    def setUp(self):
-        super(PaymentProcessorTest, self).setUp()
-
-        # Add student with payment
-        student = Student()
-        student.firstname = u'Anna'
-        student.lastname = u'Tester'
-        student.reg_number = u'123'
-        student.matric_number = u'234'
-        self.app['students'].addStudent(student)
-        self.student = self.app['students'][student.student_id]
-        payment = createObject(u'waeup.StudentOnlinePayment')
-        payment.p_id = 'p120'
-        self.student['payments'][payment.p_id] = payment
-
-        # Import students with subobjects
-        student_file = os.path.join(self.workdir, 'sample_student_data.csv')
-        open(student_file, 'wb').write(STUDENT_SAMPLE_DATA)
-        num, num_warns, fin_file, fail_file = StudentProcessor().doImport(
-            student_file, STUDENT_HEADER_FIELDS)
-        shutil.rmtree(os.path.dirname(fin_file))
-
-        self.processor = StudentOnlinePaymentProcessor()
-        self.csv_file = os.path.join(
-            self.workdir, 'sample_payment_data.csv')
-        open(self.csv_file, 'wb').write(PAYMENT_SAMPLE_DATA)
-        self.csv_file2 = os.path.join(
-            self.workdir, 'sample_create_payment_data.csv')
-        open(self.csv_file2, 'wb').write(PAYMENT_CREATE_SAMPLE_DATA)
-
-    def test_interface(self):
-        # Make sure we fulfill the interface contracts.
-        assert verifyObject(IBatchProcessor, self.processor) is True
-        assert verifyClass(
-            IBatchProcessor, StudentOnlinePaymentProcessor) is True
-
-    def test_getEntry(self):
-        assert self.processor.getEntry(
-            dict(student_id='ID_NONE', p_id='nonsense'), self.app) is None
-        assert self.processor.getEntry(
-            dict(student_id=self.student.student_id, p_id='p120'),
-            self.app) is self.student['payments']['p120']
-        assert self.processor.getEntry(
-            dict(student_id=self.student.student_id, p_id='XXXXXX112'),
-            self.app) is self.student['payments']['p120']
-
-    def test_delEntry(self):
-        assert self.processor.getEntry(
-            dict(student_id=self.student.student_id, p_id='p120'),
-            self.app) is self.student['payments']['p120']
-        self.assertEqual(len(self.student['payments'].keys()),1)
-        self.processor.delEntry(
-            dict(student_id=self.student.student_id, p_id='p120'),
-            self.app)
-        assert self.processor.getEntry(
-            dict(student_id=self.student.student_id, p_id='p120'),
-            self.app) is None
-        self.assertEqual(len(self.student['payments'].keys()),0)
-
-    def test_addEntry(self):
-        self.assertEqual(len(self.student['payments'].keys()),1)
-        payment1 = createObject(u'waeup.StudentOnlinePayment')
-        payment1.p_id = 'p234'
-        self.processor.addEntry(
-            payment1, dict(student_id=self.student.student_id, p_id='p234'),
-            self.app)
-        self.assertEqual(len(self.student['payments'].keys()),2)
-        self.assertEqual(self.student['payments']['p234'].p_id, 'p234')
-        payment2 = createObject(u'waeup.StudentOnlinePayment')
-        payment1.p_id = 'nonsense'
-        # payment1.p_id will be replaced if p_id doesn't start with 'p'
-        self.processor.addEntry(
-            payment2, dict(student_id=self.student.student_id, p_id='XXXXXX456'),
-            self.app)
-        self.assertEqual(len(self.student['payments'].keys()),3)
-        self.assertEqual(self.student['payments']['p560'].p_id, 'p560')
-
-    def test_checkConversion(self):
-        errs, inv_errs, conv_dict = self.processor.checkConversion(
-            dict(p_id='3816951266236341955'))
-        self.assertEqual(len(errs),0)
-        errs, inv_errs, conv_dict = self.processor.checkConversion(
-            dict(p_id='p1266236341955'))
-        self.assertEqual(len(errs),0)
-        errs, inv_errs, conv_dict = self.processor.checkConversion(
-            dict(p_id='nonsense'))
-        self.assertEqual(len(errs),1)
-        timestamp = ("%d" % int(time()*10000))[1:]
-        p_id = "p%s" % timestamp
-        errs, inv_errs, conv_dict = self.processor.checkConversion(
-            dict(p_id=p_id))
-        self.assertEqual(len(errs),0)
-
-    def test_import(self):
-        num, num_warns, fin_file, fail_file = self.processor.doImport(
-            self.csv_file, PAYMENT_HEADER_FIELDS,'create')
-        self.assertEqual(num_warns,0)
-        payment = self.processor.getEntry(dict(reg_number='1',
-            p_id='p2907979737440'), self.app)
-        self.assertEqual(payment.p_id, 'p2907979737440')
-        self.assertTrue(payment.p_current)
-        cdate = payment.creation_date.strftime("%Y-%m-%d %H:%M:%S")
-        self.assertEqual(cdate, "2010-11-26 18:59:33")
-        self.assertEqual(str(payment.creation_date.tzinfo),'UTC')
-        payment = self.processor.getEntry(dict(matric_number='100001',
-            p_id='p2907125937570'), self.app)
-        self.assertEqual(payment.p_id, 'p2907125937570')
-        self.assertFalse(payment.p_current)
-        cdate = payment.creation_date.strftime("%Y-%m-%d %H:%M:%S")
-        # Ooooh, still the old problem, see
-        # http://mail.dzug.org/mailman/archives/zope/2006-August/001153.html.
-        # WAT is interpreted as GMT-1 and not GMT+1
-        self.assertEqual(cdate, "2010-11-25 21:16:33")
-        self.assertEqual(str(payment.creation_date.tzinfo),'UTC')
-        shutil.rmtree(os.path.dirname(fin_file))
-        logcontent = open(self.logfile).read()
-        # Logging message from updateEntry
-        self.assertTrue(
-            'INFO - system - K1000001 - Payment ticket updated: '
-            'p_item=BTECHBDT, creation_date=2010-02-15 13:19:01+00:00, '
-            'p_category=schoolfee, amount_auth=19500.0, p_current=True, '
-            'p_id=p1266236341955, r_code=00, r_amount_approved=19500.0, '
-            'p_state=paid'
-            in logcontent)
-
-    def test_import_update(self):
-        # We perform the same import twice,
-        # the second time in update mode. The number
-        # of warnings must be the same.
-        num, num_warns, fin_file, fail_file = self.processor.doImport(
-            self.csv_file, PAYMENT_HEADER_FIELDS,'create')
-        shutil.rmtree(os.path.dirname(fin_file))
-        num, num_warns, fin_file, fail_file = self.processor.doImport(
-            self.csv_file, PAYMENT_HEADER_FIELDS,'update')
-        self.assertEqual(num_warns,0)
-        shutil.rmtree(os.path.dirname(fin_file))
-
-    def test_import_remove(self):
-        num, num_warns, fin_file, fail_file = self.processor.doImport(
-            self.csv_file, PAYMENT_HEADER_FIELDS,'create')
-        shutil.rmtree(os.path.dirname(fin_file))
-        num, num_warns, fin_file, fail_file = self.processor.doImport(
-            self.csv_file, PAYMENT_HEADER_FIELDS,'remove')
-        self.assertEqual(num_warns,0)
-        shutil.rmtree(os.path.dirname(fin_file))
-        logcontent = open(self.logfile).read()
-        self.assertTrue(
-            'INFO - system - K1000001 - Payment ticket removed: p1266236341955'
-            in logcontent)
-
-    def test_import_wo_pid(self):
-        num, num_warns, fin_file, fail_file = self.processor.doImport(
-            self.csv_file2, PAYMENT_CREATE_HEADER_FIELDS,'create')
-        self.assertEqual(num_warns,0)
-        shutil.rmtree(os.path.dirname(fin_file))
-        self.assertEqual(len(self.app['students']['X666666']['payments']), 50)
-
+        self.assertMatches(
+            '... INFO - system - X666666 - Study course updated: current_verdict=0...',
+            logcontent)
+        self.assertMatches(
+            '... INFO - system - X666666 - Returned...',
+            logcontent)
+
+        shutil.rmtree(os.path.dirname(fin_file))
 
 def test_suite():
