Index: main/waeup.ikoba/trunk/src/waeup/ikoba/payments/export.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/payments/export.py	(revision 12777)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/payments/export.py	(revision 12777)
@@ -0,0 +1,76 @@
+## $Id$
+##
+## Copyright (C) 2012 Uli Fouquet & Henrik Bettermann
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##
+"""Exporters for payments.
+"""
+import grok
+from zope.securitypolicy.interfaces import IPrincipalRoleMap
+from waeup.ikoba.interfaces import ICSVExporter
+from waeup.ikoba.interfaces import MessageFactory as _
+from waeup.ikoba.utils.batching import ExporterBase
+from waeup.ikoba.utils.helpers import iface_names
+from waeup.ikoba.payments.interfaces import IPayment
+
+
+class PaymentExporterBase(grok.GlobalUtility, ExporterBase):
+    """Exporter for payments.
+
+    This is a baseclass.
+    """
+    grok.baseclass()
+    iface = None
+    title = None
+
+    @property
+    def fields(self):
+        return tuple(sorted(iface_names(self.iface)))
+
+    def mangle_value(self, value, name, context=None):
+        """Hook for mangling values in derived classes
+        """
+        if name == 'payment_items' and value is not None:
+            value = [eval(entry.to_string()) for entry in value]
+        return super(PaymentExporterBase, self).mangle_value(
+            value, name, context)
+
+    def export(self, payments, filepath=None):
+        """Export `payments`, an iterable, as CSV file.
+
+        If `filepath` is ``None``, a raw string with CSV data is returned.
+        """
+        writer, outfile = self.get_csv_writer(filepath)
+        for payment in payments:
+            self.write_item(payment, writer)
+        return self.close_outfile(filepath, outfile)
+
+    def export_all(self, site, filepath=None):
+        """Export payments in paymentscontainer into filepath as CSV data.
+
+        If `filepath` is ``None``, a raw string with CSV data is returned.
+        """
+        payments = site.get('payments', {})
+        return self.export(payments.values(), filepath)
+
+
+class PaymentExporter(PaymentExporterBase):
+    """Exporter for payments.
+
+    """
+    grok.implements(ICSVExporter)
+    grok.name('payments')
+    iface = IPayment
+    title = _(u'Payments')
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/payments/tests/test_browser.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/payments/tests/test_browser.py	(revision 12776)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/payments/tests/test_browser.py	(revision 12777)
@@ -1,3 +1,3 @@
-## $Id: test_browser.py 12762 2015-03-14 13:49:29Z henrik $
+## $Id$
 ##
 ## Copyright (C) 2015 Uli Fouquet & Henrik Bettermann
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/payments/tests/test_export.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/payments/tests/test_export.py	(revision 12777)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/payments/tests/test_export.py	(revision 12777)
@@ -0,0 +1,130 @@
+## $Id$
+##
+## Copyright (C) 2014 Uli Fouquet & Henrik Bettermann
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##
+"""
+Test the payment exporter.
+"""
+import os
+import shutil
+import tempfile
+import unittest
+import grok
+import datetime
+from decimal import Decimal
+from cStringIO import StringIO
+from zope.component import queryUtility, getUtility, createObject
+from zope.component.hooks import setSite, clearSite
+from zope.event import notify
+from zope.interface.verify import verifyObject, verifyClass
+from waeup.ikoba.interfaces import (
+    ICSVExporter, IExtFileStore, IFileStoreNameChooser)
+from waeup.ikoba.app import Company
+from waeup.ikoba.products.productoptions import ProductOption
+from waeup.ikoba.payments.export import PaymentExporter
+from waeup.ikoba.payments.payment import Payment
+from waeup.ikoba.payments.interfaces import IPayer, IPayable, STATE_PAID
+from waeup.ikoba.testing import FunctionalLayer, FunctionalTestCase
+
+
+class PaymentExporterTest(FunctionalTestCase):
+
+    layer = FunctionalLayer
+
+    def setUp(self):
+        super(PaymentExporterTest, self).setUp()
+        self.dc_root = tempfile.mkdtemp()
+        self.workdir = tempfile.mkdtemp()
+        app = Company()
+        app['datacenter'].setStoragePath(self.dc_root)
+        self.getRootFolder()['app'] = app
+        self.app = self.getRootFolder()['app']
+        setSite(app)
+        self.logfile = os.path.join(
+            self.app['datacenter'].storage, 'logs', 'main.log')
+        self.outfile = os.path.join(self.workdir, 'myoutput.csv')
+
+        # Setup customer with contract and payment
+        customer = createObject('waeup.Customer')
+        self.app['customers'].addCustomer(customer)
+        contract = createObject('waeup.SampleContract')
+        contract.contract_id = u'CON1'
+        contract.title = u'My Contract'
+        prodoption = ProductOption()
+        prodoption.title = u'Any product option'
+        prodoption.fee = Decimal('88.8')
+        prodoption.currency = 'EUR'
+        contract.product_options = [prodoption, ]
+        customer['contracts'].addContract(contract)
+        payer = IPayer(customer)
+        payable = IPayable(contract)
+        self.payment = Payment(payer, payable)
+        self.payment.gateway_service = 'demo_creditcard'
+        self.payment.state = STATE_PAID
+        self.app['payments'][self.payment.payment_id] = self.payment
+        return
+
+    def tearDown(self):
+        super(PaymentExporterTest, self).tearDown()
+        shutil.rmtree(self.workdir)
+        shutil.rmtree(self.dc_root)
+        clearSite()
+        return
+
+    def test_ifaces(self):
+        # make sure we fullfill interface contracts
+        obj = PaymentExporter()
+        verifyObject(ICSVExporter, obj)
+        verifyClass(ICSVExporter,PaymentExporter)
+        return
+
+    def test_get_as_utility(self):
+        # we can get an payment exporter as utility
+        result = queryUtility(ICSVExporter, name="payments")
+        self.assertTrue(result is not None)
+        return
+
+    def test_export(self):
+        # we can really export a payment
+        exporter = PaymentExporter()
+        exporter.export([self.payment], self.outfile)
+        result = open(self.outfile, 'rb').read()
+        self.assertTrue(
+            'creation_date,currency,gateway_service,payable_id,payee_id,'
+            'payer_id,payment_date,payment_id,state,title\r\n'
+            in result)
+        self.assertMatches(
+            '...2015-03-16 16:07:33.273297#,EUR,demo_creditcard,CON1,'
+            ',K1000000,,%s,64,My Contract...' % self.payment.payment_id,
+            result
+            )
+        return
+
+    def test_export_all(self):
+        # we can really export all payments
+        exporter = PaymentExporter()
+        exporter.export_all(self.app, self.outfile)
+        result = open(self.outfile, 'rb').read()
+        self.assertTrue(
+            'creation_date,currency,gateway_service,payable_id,payee_id,'
+            'payer_id,payment_date,payment_id,state,title\r\n'
+            in result)
+        self.assertMatches(
+            '...2015-03-16 16:07:33.273297#,EUR,demo_creditcard,CON1,'
+            ',K1000000,,%s,64,My Contract...' % self.payment.payment_id,
+            result
+            )
+        return
Index: main/waeup.ikoba/trunk/src/waeup/ikoba/utils/utils.py
===================================================================
--- main/waeup.ikoba/trunk/src/waeup/ikoba/utils/utils.py	(revision 12776)
+++ main/waeup.ikoba/trunk/src/waeup/ikoba/utils/utils.py	(revision 12777)
@@ -135,4 +135,5 @@
         'users',
         'products',
+        'payments',
         'customers',
         'customersampledocuments',
