## $Id: browser.py 12809 2015-03-21 13:31:16Z henrik $
##
## 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
##
"""UI components for customers and related components.
"""
import grok
import os
from urllib import urlencode
from zope.event import notify
from zope.i18n import translate
from zope.catalog.interfaces import ICatalog
from zope.component import queryUtility, getUtility, createObject
from zope.security import checkPermission
from hurry.workflow.interfaces import (
    IWorkflowInfo, IWorkflowState, InvalidTransitionError)
from waeup.ikoba.interfaces import MessageFactory as _
from waeup.ikoba.interfaces import (
    IContactForm, IObjectHistory, IIkobaObject, IIkobaUtils, IExtFileStore,
    IPasswordValidator, IUserAccount,
    STARTED, VERIFIED, REJECTED, EXPIRED, CREATED, REQUESTED,
    APPROVED, PROVISIONALLY, AWAITING)
from waeup.ikoba.widgets.datewidget import (
    FriendlyDateWidget, FriendlyDateDisplayWidget,
    FriendlyDatetimeDisplayWidget)
from waeup.ikoba.browser.layout import (
    IkobaPage, IkobaEditFormPage, IkobaAddFormPage, IkobaDisplayFormPage,
    NullValidator, jsaction, action, UtilityView)
from waeup.ikoba.browser.pages import ContactAdminForm
from waeup.ikoba.browser.breadcrumbs import Breadcrumb
from waeup.ikoba.browser.interfaces import ICaptchaManager
from waeup.ikoba.mandates.mandate import PasswordMandate
from waeup.ikoba.payments.payment import (
    get_payments_from_payer_id,
    get_payments_from_payable_id,
    find_payable_from_payable_id,
    get_payment_providers
    )
from waeup.ikoba.payments.interfaces import (
    IPaymentGatewayServicesLister, IPaymentGatewayService, IPayer, IPayable,
    payment_states
    )
from waeup.ikoba.payments.catalog import search as search_payments
from waeup.ikoba.widgets.hrefwidget import HREFDisplayWidget
from waeup.ikoba.utils.helpers import (
    get_current_principal, format_date)
from waeup.ikoba.customers.interfaces import (
    ICustomer, ICustomersContainer, ICustomerRequestPW, ICustomersUtils,
    ICustomerDocument, ICustomerDocumentsContainer, ICustomerCreate,
    ICustomerPDFDocument, IContractsContainer, IContract,
    IContractSelectProduct,
    )
from waeup.ikoba.customers.catalog import search as search_customers
from waeup.ikoba.customers.workflow import PAYMENT_TRANSITIONS


grok.context(IIkobaObject)

WARNING_CUST = _('You can not edit some data after final submission.'
            ' You really want to submit?')

WARNING_DOC = _('You can not edit your document after final submission.'
            ' You really want to submit?')

WARNING_CON = _('You can not edit your contract after final submission.'
            ' You really want to submit?')


# Save function used for save methods in pages
def msave(view, **data):
    changed_fields = view.applyData(view.context, **data)
    # Turn list of lists into single list
    if changed_fields:
        changed_fields = reduce(lambda x,y: x+y, changed_fields.values())
    if 'product_object' in changed_fields and data['product_object'] is not None:
        view.context.last_product_id = data['product_object'].product_id
        notify(grok.ObjectModifiedEvent(view.context))
    fields_string = ' + '.join(changed_fields)
    view.flash(_('Form has been saved.'))
    if fields_string:
        view.context.writeLogMessage(
            view, '%s - saved: %s' % (view.context.__name__, fields_string))
    return


def emit_lock_message(view):
    """Flash a lock message.
    """
    view.flash(_('The requested form is locked (read-only).'), type="warning")
    view.redirect(view.url(view.context))
    return

def isCustomer(principal):
    return getattr(principal, 'user_type', None) == 'customer'


class CustomersBreadcrumb(Breadcrumb):
    """A breadcrumb for the customers container.
    """
    grok.context(ICustomersContainer)
    title = _('Customers')

    @property
    def target(self):
        user = get_current_principal()
        if getattr(user, 'user_type', None) == 'customer':
            return None
        return self.viewname


class CustomerBreadcrumb(Breadcrumb):
    """A breadcrumb for the customer container.
    """
    grok.context(ICustomer)

    def title(self):
        return self.context.display_fullname


class CustomersContainerPage(IkobaPage):
    """The standard view for customer containers.
    """
    grok.context(ICustomersContainer)
    grok.name('index')
    grok.require('waeup.viewCustomersContainer')
    grok.template('containerpage')
    label = _('Find customers')
    search_button = _('Find customer(s)')
    pnav = 4

    def update(self, *args, **kw):
        form = self.request.form
        self.hitlist = []
        if form.get('searchtype', None) == 'suspended':
            self.searchtype = form['searchtype']
            self.searchterm = None
        elif 'searchterm' in form and form['searchterm']:
            self.searchterm = form['searchterm']
            self.searchtype = form['searchtype']
        elif 'old_searchterm' in form:
            self.searchterm = form['old_searchterm']
            self.searchtype = form['old_searchtype']
        else:
            if 'search' in form:
                self.flash(_('Empty search string'), type="warning")
            return
        self.hitlist = search_customers(query=self.searchterm,
            searchtype=self.searchtype, view=self)
        if not self.hitlist:
            self.flash(_('No customer found.'), type="warning")
        return


class CustomersContainerManagePage(IkobaPage):
    """The manage page for customer containers.
    """
    grok.context(ICustomersContainer)
    grok.name('manage')
    grok.require('waeup.manageCustomer')
    grok.template('containermanagepage')
    pnav = 4
    label = _('Manage customer section')
    search_button = _('Find customer(s)')
    remove_button = _('Remove selected')

    def update(self, *args, **kw):
        form = self.request.form
        self.hitlist = []
        if form.get('searchtype', None) == 'suspended':
            self.searchtype = form['searchtype']
            self.searchterm = None
        elif 'searchterm' in form and form['searchterm']:
            self.searchterm = form['searchterm']
            self.searchtype = form['searchtype']
        elif 'old_searchterm' in form:
            self.searchterm = form['old_searchterm']
            self.searchtype = form['old_searchtype']
        else:
            if 'search' in form:
                self.flash(_('Empty search string'), type="warning")
            return
        if not 'entries' in form:
            self.hitlist = search_customers(query=self.searchterm,
                searchtype=self.searchtype, view=self)
            if not self.hitlist:
                self.flash(_('No customer found.'), type="warning")
            if 'remove' in form:
                self.flash(_('No item selected.'), type="warning")
            return
        entries = form['entries']
        if isinstance(entries, basestring):
            entries = [entries]
        deleted = []
        for entry in entries:
            if 'remove' in form:
                del self.context[entry]
                deleted.append(entry)
        self.hitlist = search_customers(query=self.searchterm,
            searchtype=self.searchtype, view=self)
        if len(deleted):
            self.flash(_('Successfully removed: ${a}',
                mapping={'a': ','.join(deleted)}))
        return


class CustomerAddFormPage(IkobaAddFormPage):
    """Add-form to add a customer.
    """
    grok.context(ICustomersContainer)
    grok.require('waeup.manageCustomer')
    grok.name('addcustomer')
    form_fields = grok.AutoFields(ICustomer).select(
        'firstname', 'middlename', 'lastname', 'reg_number')
    label = _('Add customer')
    pnav = 4

    @action(_('Create customer record'), style='primary')
    def addCustomer(self, **data):
        customer = createObject(u'waeup.Customer')
        self.applyData(customer, **data)
        self.context.addCustomer(customer)
        self.flash(_('Customer created.'))
        self.redirect(self.url(self.context[customer.customer_id], 'index'))
        return


class LoginAsCustomerStep1(IkobaEditFormPage):
    """ View to temporarily set a customer password.
    """
    grok.context(ICustomer)
    grok.name('loginasstep1')
    grok.require('waeup.loginAsCustomer')
    grok.template('loginasstep1')
    pnav = 4

    def label(self):
        return _(u'Set temporary password for ${a}',
            mapping={'a': self.context.display_fullname})

    @action('Set password now', style='primary')
    def setPassword(self, *args, **data):
        ikoba_utils = getUtility(IIkobaUtils)
        password = ikoba_utils.genPassword()
        self.context.setTempPassword(self.request.principal.id, password)
        self.context.writeLogMessage(
            self, 'temp_password generated: %s' % password)
        args = {'password': password}
        self.redirect(self.url(self.context) +
            '/loginasstep2?%s' % urlencode(args))
        return


class LoginAsCustomerStep2(IkobaPage):
    """ View to temporarily login as customer with a temporary password.
    """
    grok.context(ICustomer)
    grok.name('loginasstep2')
    grok.require('waeup.Public')
    grok.template('loginasstep2')
    login_button = _('Login now')
    pnav = 4

    def label(self):
        return _(u'Login as ${a}',
            mapping={'a': self.context.customer_id})

    def update(self, SUBMIT=None, password=None):
        self.password = password
        if SUBMIT is not None:
            self.flash(_('You successfully logged in as customer.'))
            self.redirect(self.url(self.context))
        return


class CustomerBaseDisplayFormPage(IkobaDisplayFormPage):
    """ Page to display customer base data
    """
    grok.context(ICustomer)
    grok.name('index')
    grok.require('waeup.viewCustomer')
    grok.template('basepage')
    pnav = 4

    @property
    def form_fields(self):
        return grok.AutoFields(
            self.context.form_fields_interface).omit(
            'password', 'suspended', 'suspended_comment')

    @property
    def label(self):
        if self.context.suspended:
            return _('${a}: Base Data (account deactivated)',
                mapping={'a': self.context.display_fullname})
        return  _('${a}: Base Data',
            mapping={'a': self.context.display_fullname})

    @property
    def hasPassword(self):
        if self.context.password:
            return _('set')
        return _('unset')

    @property
    def is_requestable(self):
        if self.context.state in (REQUESTED, PROVISIONALLY, APPROVED):
            return False
        return True

    def update(self):
        # Fire transition if customer logs in for the first time
        usertype = getattr(self.request.principal, 'user_type', None)
        if usertype == 'customer' and \
            IWorkflowState(self.context).getState() == CREATED:
            IWorkflowInfo(self.context).fireTransition('start')
        return


class ContactCustomerForm(ContactAdminForm):
    grok.context(ICustomer)
    grok.name('contactcustomer')
    grok.require('waeup.viewCustomer')
    pnav = 4
    form_fields = grok.AutoFields(IContactForm).select('subject', 'body')


    def update(self, subject=u'', body=u''):
        super(ContactCustomerForm, self).update()
        self.form_fields.get('subject').field.default = subject
        self.form_fields.get('body').field.default = body
        return

    def label(self):
        return _(u'Send message to ${a}',
            mapping={'a': self.context.display_fullname})

    @action('Send message now', style='primary')
    def send(self, *args, **data):
        try:
            email = self.request.principal.email
        except AttributeError:
            email = self.config.email_admin
        usertype = getattr(self.request.principal,
                           'user_type', 'system').title()
        ikoba_utils = getUtility(IIkobaUtils)
        success = ikoba_utils.sendContactForm(
                self.request.principal.title, email,
                self.context.display_fullname, self.context.email,
                self.request.principal.id,usertype,
                self.config.name,
                data['body'], data['subject'])
        if success:
            self.flash(_('Your message has been sent.'))
        else:
            self.flash(_('An smtp server error occurred.'), type="danger")
        return


class CustomerBaseManageFormPage(IkobaEditFormPage):
    """ View to manage customer base data
    """
    grok.context(ICustomer)
    grok.name('manage_base')
    grok.require('waeup.manageCustomer')
    grok.template('basemanagepage')
    label = _('Manage base data')
    pnav = 4
    deletion_warning = _('Are you sure?')

    @property
    def form_fields(self):
        return grok.AutoFields(
            self.context.form_fields_interface).omit(
            'customer_id', 'suspended')

    def update(self):
        super(CustomerBaseManageFormPage, self).update()
        self.wf_info = IWorkflowInfo(self.context)
        return

    @action(_('Save'), style='primary')
    def save(self, **data):
        form = self.request.form
        password = form.get('password', None)
        password_ctl = form.get('control_password', None)
        if password:
            validator = getUtility(IPasswordValidator)
            errors = validator.validate_password(password, password_ctl)
            if errors:
                self.flash(' '.join(errors), type="danger")
                return
        changed_fields = self.applyData(self.context, **data)
        # Turn list of lists into single list
        if changed_fields:
            changed_fields = reduce(lambda x,y: x+y, changed_fields.values())
        else:
            changed_fields = []
        if password:
            # Now we know that the form has no errors and can set password
            IUserAccount(self.context).setPassword(password)
            changed_fields.append('password')
        fields_string = ' + '.join(changed_fields)
        self.flash(_('Form has been saved.'))
        if fields_string:
            self.context.writeLogMessage(self, 'saved: % s' % fields_string)
        return


class CustomerTriggerTransitionFormPage(IkobaEditFormPage):
    """ View to trigger customer workflow transitions
    """
    grok.context(ICustomer)
    grok.name('trigtrans')
    grok.require('waeup.triggerTransition')
    grok.template('trigtrans')
    label = _('Trigger registration transition')
    pnav = 4

    def getTransitions(self):
        """Return a list of dicts of allowed transition ids and titles.

        Each list entry provides keys ``name`` and ``title`` for
        internal name and (human readable) title of a single
        transition.
        """
        wf_info = IWorkflowInfo(self.context)
        allowed_transitions = [t for t in wf_info.getManualTransitions()]
        return [dict(name='', title=_('No transition'))] +[
            dict(name=x, title=y) for x, y in allowed_transitions]

    @action(_('Apply'), style='primary')
    def apply(self, **data):
        form = self.request.form
        if 'transition' in form and form['transition']:
            transition_id = form['transition']
            wf_info = IWorkflowInfo(self.context)
            wf_info.fireTransition(transition_id)
            self.flash(_("Transition '%s' executed." % transition_id))
            self.redirect(self.url(self.context))
        return


class CustomerActivatePage(UtilityView, grok.View):
    """ Activate customer account
    """
    grok.context(ICustomer)
    grok.name('activate')
    grok.require('waeup.manageCustomer')

    def update(self):
        self.context.suspended = False
        self.context.writeLogMessage(self, 'account activated')
        history = IObjectHistory(self.context)
        history.addMessage('Customer account activated')
        self.flash(_('Customer account has been activated.'))
        self.redirect(self.url(self.context))
        return

    def render(self):
        return


class CustomerDeactivatePage(UtilityView, grok.View):
    """ Deactivate customer account
    """
    grok.context(ICustomer)
    grok.name('deactivate')
    grok.require('waeup.manageCustomer')

    def update(self):
        self.context.suspended = True
        self.context.writeLogMessage(self, 'account deactivated')
        history = IObjectHistory(self.context)
        history.addMessage('Customer account deactivated')
        self.flash(_('Customer account has been deactivated.'))
        self.redirect(self.url(self.context))
        return

    def render(self):
        return


class CustomerHistoryPage(IkobaPage):
    """ Page to display customer history
    """
    grok.context(ICustomer)
    grok.name('history')
    grok.require('waeup.viewCustomer')
    grok.template('customerhistory')
    pnav = 4

    @property
    def label(self):
        return _('${a}: History', mapping={'a':self.context.display_fullname})


class CustomerRequestPasswordPage(IkobaAddFormPage):
    """Captcha'd password request page for customers.
    """
    grok.name('requestpw')
    grok.require('waeup.Anonymous')
    grok.template('requestpw')
    form_fields = grok.AutoFields(ICustomerRequestPW)
    label = _('Request password for first-time login')

    def update(self):
        # Handle captcha
        self.captcha = getUtility(ICaptchaManager).getCaptcha()
        self.captcha_result = self.captcha.verify(self.request)
        self.captcha_code = self.captcha.display(self.captcha_result.error_code)
        return

    def _redirect(self, email, password, customer_id):
        # Forward only email address to landing page in base package.
        self.redirect(self.url(self.context, 'requestpw_complete',
            data=dict(email=email)))
        return

    def _pw_used(self):
        # XXX: False if password has not been used. We need an extra
        #      attribute which remembers if customer logged in.
        return True

    @action(_('Send login credentials to email address'), style='primary')
    def get_credentials(self, **data):
        if not self.captcha_result.is_valid:
            # Captcha will display error messages automatically.
            # No need to flash something.
            return
        number = data.get('number','')
        firstname = data.get('firstname','')
        cat = getUtility(ICatalog, name='customers_catalog')
        results = list(
            cat.searchResults(reg_number=(number, number)))
        if results:
            customer = results[0]
            if getattr(customer,'firstname',None) is None:
                self.flash(_('An error occurred.'), type="danger")
                return
            elif customer.firstname.lower() != firstname.lower():
                # Don't tell the truth here. Anonymous must not
                # know that a record was found and only the firstname
                # verification failed.
                self.flash(_('No customer found.'), type="warning")
                return
            elif customer.password is not None and self._pw_used:
                self.flash(_('Your password has already been set and used. '
                             'Please proceed to the login page.'),
                           type="warning")
                return
            # Store email address but nothing else.
            customer.email = data['email']
            notify(grok.ObjectModifiedEvent(customer))
        else:
            # No record found, this is the truth.
            self.flash(_('No customer found.'), type="warning")
            return

        ikoba_utils = getUtility(IIkobaUtils)
        password = ikoba_utils.genPassword()
        mandate = PasswordMandate()
        mandate.params['password'] = password
        mandate.params['user'] = customer
        site = grok.getSite()
        site['mandates'].addMandate(mandate)
        # Send email with credentials
        args = {'mandate_id':mandate.mandate_id}
        mandate_url = self.url(site) + '/mandate?%s' % urlencode(args)
        url_info = u'Confirmation link: %s' % mandate_url
        msg = _('You have successfully requested a password for the')
        if ikoba_utils.sendCredentials(IUserAccount(customer),
            password, url_info, msg):
            email_sent = customer.email
        else:
            email_sent = None
        self._redirect(email=email_sent, password=password,
            customer_id=customer.customer_id)
        ob_class = self.__implemented__.__name__.replace('waeup.ikoba.','')
        self.context.logger.info(
            '%s - %s (%s) - %s' % (ob_class, number, customer.customer_id, email_sent))
        return


class CustomerCreateAccountPage(IkobaAddFormPage):
    """Captcha'd account creation page for customers.
    """
    grok.name('createaccount')
    grok.require('waeup.Anonymous')
    grok.template('createaccount')
    form_fields = grok.AutoFields(ICustomerCreate)
    label = _('Create customer account')

    def update(self):
        # Handle captcha
        self.captcha = getUtility(ICaptchaManager).getCaptcha()
        self.captcha_result = self.captcha.verify(self.request)
        self.captcha_code = self.captcha.display(self.captcha_result.error_code)
        return

    def _redirect(self, email, password, customer_id):
        # Forward only email address to landing page in base package.
        self.redirect(self.url(self.context, 'requestpw_complete',
            data=dict(email=email)))
        return

    @action(_('Send login credentials to email address'), style='primary')
    def create_account(self, **data):
        if not self.captcha_result.is_valid:
            # Captcha will display error messages automatically.
            # No need to flash something.
            return
        customer = createObject(u'waeup.Customer')
        customer.firstname = data.get('firstname','')
        customer.middlename = data.get('middlename','')
        customer.lastname = data.get('lastname','')
        customer.email = data.get('email','')
        self.context['customers'].addCustomer(customer)
        ikoba_utils = getUtility(IIkobaUtils)
        password = ikoba_utils.genPassword()
        mandate = PasswordMandate()
        mandate.params['password'] = password
        mandate.params['user'] = customer
        site = grok.getSite()
        site['mandates'].addMandate(mandate)
        # Send email with credentials
        args = {'mandate_id':mandate.mandate_id}
        mandate_url = self.url(site) + '/mandate?%s' % urlencode(args)
        url_info = u'Confirmation link: %s' % mandate_url
        msg = _('You have successfully created a customer account for the')
        if ikoba_utils.sendCredentials(IUserAccount(customer),
            password, url_info, msg):
            email_sent = customer.email
        else:
            email_sent = None
        self._redirect(email=email_sent, password=password,
            customer_id=customer.customer_id)
        ob_class = self.__implemented__.__name__.replace('waeup.ikoba.','')
        self.context.logger.info(
            '%s - %s - %s' % (ob_class, customer.customer_id, email_sent))
        return


class CustomerRequestPasswordEmailSent(IkobaPage):
    """Landing page after successful password request.

    """
    grok.name('requestpw_complete')
    grok.require('waeup.Public')
    grok.template('requestpwmailsent')
    label = _('Your request was successful.')

    def update(self, email=None, customer_id=None, password=None):
        self.email = email
        self.password = password
        self.customer_id = customer_id
        return

# Pages for customers

class CustomerFilesUploadPage(IkobaPage):
    """ View to upload files by customer

    We use this page only to upload a passport picture (portrait).
    """
    grok.context(ICustomer)
    grok.name('upload_files')
    grok.require('waeup.handleCustomer')
    grok.template('filesuploadpage')
    label = _('Upload files')
    pnav = 4
    deletion_warning = _('Are you sure?')

    def update(self, CANCEL=None):
        CUSTMANAGE_STATES = getUtility(
            ICustomersUtils).CUSTMANAGE_CUSTOMER_STATES
        if self.context.state not in CUSTMANAGE_STATES:
            emit_lock_message(self)
            return
        if CANCEL is not None:
            self.redirect(self.url(self.context))
            return
        super(CustomerFilesUploadPage, self).update()
        return


class CustomerBaseEditFormPage(IkobaEditFormPage):
    """ View to edit customer base data
    """
    grok.context(ICustomer)
    grok.name('edit_base')
    grok.require('waeup.handleCustomer')
    pnav = 4

    def is_requestable(self, action=None):
        if self.context.state == STARTED:
            return True
        return False

    @property
    def label(self):
        if self.is_requestable():
            return _('Edit base data and request registration')
        return _('Edit base data')

    @property
    def form_fields(self):
        if not self.is_requestable():
            return grok.AutoFields(
                self.context.form_fields_interface).select('email', 'phone')
        return grok.AutoFields(self.context.form_fields_interface).omit(
            'suspended', 'suspended_comment', 'reg_number', 'customer_id')

    @action(_('Save'), style='primary')
    def save(self, **data):
        msave(self, **data)
        return

    def dataNotComplete(self):
        store = getUtility(IExtFileStore)
        error = ''
        if not store.getFileByContext(self.context, attr=u'passport.jpg'):
            error += _('Passport picture is missing.')
        if error:
            return error
        return

    @action(_('Save and request registration now'),
            warning=WARNING_CUST, condition=is_requestable)
    def finalsubmit(self, **data):
        msave(self, **data)
        if self.dataNotComplete():
            self.flash(self.dataNotComplete(), type="warning")
            self.redirect(self.url(self.context, 'upload_files'))
            return
        IWorkflowInfo(self.context).fireTransition('request')
        self.flash(_('Registration form has been submitted.'))
        self.redirect(self.url(self.context))
        return

    @action(_('Cancel'), validator=NullValidator)
    def cancel(self, **data):
        self.redirect(self.url(self.context))


class CustomerChangePasswordPage(IkobaEditFormPage):
    """ View to edit customer passords
    """
    grok.context(ICustomer)
    grok.name('changepassword')
    grok.require('waeup.handleCustomer')
    grok.template('changepassword')
    label = _('Change password')
    pnav = 4

    @action(_('Save'), style='primary')
    def save(self, **data):
        form = self.request.form
        password = form.get('change_password', None)
        password_ctl = form.get('change_password_repeat', None)
        if password:
            validator = getUtility(IPasswordValidator)
            errors = validator.validate_password(password, password_ctl)
            if not errors:
                IUserAccount(self.context).setPassword(password)
                # Unset temporary password
                self.context.temp_password = None
                self.context.writeLogMessage(self, 'saved: password')
                self.flash(_('Password changed.'))
            else:
                self.flash(' '.join(errors), type="warning")
        return

    @action(_('Cancel'), validator=NullValidator)
    def cancel(self, **data):
        self.redirect(self.url(self.context))


class CustomerBasePDFFormPage(IkobaDisplayFormPage):
    """ Page to display customer base data in pdf files.
    """

    def __init__(self, context, request, omit_fields=()):
        self.omit_fields = omit_fields
        super(CustomerBasePDFFormPage, self).__init__(context, request)

    @property
    def form_fields(self):
        form_fields = grok.AutoFields(self.context.form_fields_interface)
        for field in self.omit_fields:
            form_fields = form_fields.omit(field)
        return form_fields

# Pages for customer documents

class DocumentsBreadcrumb(Breadcrumb):
    """A breadcrumb for the documents container.
    """
    grok.context(ICustomerDocumentsContainer)
    title = _('Documents')


class DocumentBreadcrumb(Breadcrumb):
    """A breadcrumb for the customer container.
    """
    grok.context(ICustomerDocument)

    @property
    def title(self):
        return "%s" % self.context.document_id[:9]


class DocumentsManageFormPage(IkobaEditFormPage):
    """ Page to manage the customer documents

    This manage form page is for both customers and customers officers.
    """
    grok.context(ICustomerDocumentsContainer)
    grok.name('index')
    grok.require('waeup.viewCustomer')
    form_fields = grok.AutoFields(ICustomerDocumentsContainer)
    grok.template('documentsmanagepage')
    pnav = 4

    @property
    def manage_documents_allowed(self):
        return checkPermission('waeup.editCustomerDocuments', self.context)

    def unremovable(self, document):
        usertype = getattr(self.request.principal, 'user_type', None)
        if not usertype:
            return False
        if not self.manage_documents_allowed:
            return True
        return (self.request.principal.user_type == 'customer' and \
            document.state in (VERIFIED, REJECTED, EXPIRED))

    @property
    def label(self):
        return _('${a}: Documents',
            mapping = {'a':self.context.__parent__.display_fullname})

    @action(_('Add document'), validator=NullValidator, style='primary')
    def addDocument(self, **data):
        self.redirect(self.url(self.context, 'adddoc'))
        return

    @jsaction(_('Remove selected documents'))
    def delDocument(self, **data):
        form = self.request.form
        if 'val_id' in form:
            child_id = form['val_id']
        else:
            self.flash(_('No document selected.'), type="warning")
            self.redirect(self.url(self.context))
            return
        if not isinstance(child_id, list):
            child_id = [child_id]
        deleted = []
        for id in child_id:
            # Customers are not allowed to remove used documents
            document = self.context.get(id, None)
            if document is not None and not self.unremovable(document):
                del self.context[id]
                deleted.append(id)
        if len(deleted):
            self.flash(_('Successfully removed: ${a}',
                mapping = {'a': ', '.join(deleted)}))
            self.context.writeLogMessage(
                self,'removed: %s' % ', '.join(deleted))
        self.redirect(self.url(self.context))
        return


class DocumentAddFormPage(IkobaAddFormPage):
    """ Page to add a document

    This add form page is for both customers and customers officers.
    """
    grok.context(ICustomerDocumentsContainer)
    grok.name('adddoc')
    grok.template('documentaddpage')
    grok.require('waeup.editCustomerDocuments')
    label = _('Add document')
    pnav = 4

    form_fields = grok.AutoFields(ICustomerDocument).omit('document_id')

    @property
    def selectable_doctypes(self):
        doctypes = getUtility(ICustomersUtils).SELECTABLE_DOCTYPES_DICT
        return sorted(doctypes.items())

    @property
    def edit_documents_allowed(self):
        right_customer_state = self.context.customer.state in getUtility(
            ICustomersUtils).DOCMANAGE_CUSTOMER_STATES
        if isCustomer(self.request.principal) and not right_customer_state:
            return False
        return True

    def update(self):
        if not self.edit_documents_allowed:
            emit_lock_message(self)
            return
        return super(DocumentAddFormPage, self).update()

    @action(_('Add document'), style='primary')
    def createDocument(self, **data):
        form = self.request.form
        doctype = form.get('doctype', None)
        # Here we can create various instances of CustomerDocument derived 
        # classes depending on the doctype parameter given in form.
        document = createObject('waeup.%s' % doctype)
        self.applyData(document, **data)
        self.context.addDocument(document)
        doctype = getUtility(ICustomersUtils).SELECTABLE_DOCTYPES_DICT[doctype]
        self.flash(_('${a} added.', mapping = {'a': doctype}))
        self.context.writeLogMessage(
            self,'added: %s %s' % (doctype, document.document_id))
        isCustomer = getattr(
            self.request.principal, 'user_type', None) == 'customer'
        if isCustomer:
            self.redirect(self.url(document, 'edit') + '#tab2')
        else:
            self.redirect(self.url(document, 'manage') + '#tab2')
        return

    @action(_('Cancel'), validator=NullValidator)
    def cancel(self, **data):
        self.redirect(self.url(self.context))


class DocumentDisplayFormPage(IkobaDisplayFormPage):
    """ Page to view a document
    """
    grok.context(ICustomerDocument)
    grok.name('index')
    grok.require('waeup.viewCustomer')
    grok.template('documentpage')
    pnav = 4
    label = None  # We render the context title in the documentpage template

    @property
    def form_fields(self):
        return grok.AutoFields(self.context.form_fields_interface)

class DocumentManageFormPage(IkobaEditFormPage):
    """ Page to edit a document
    """
    grok.context(ICustomerDocument)
    grok.name('manage')
    grok.require('waeup.manageCustomer')
    grok.template('documenteditpage')
    pnav = 4
    deletion_warning = _('Are you sure?')

    @property
    def form_fields(self):
        return grok.AutoFields(
            self.context.form_fields_interface).omit('document_id')

    def update(self):
        if not self.context.is_editable_by_manager:
            emit_lock_message(self)
            return
        return super(DocumentManageFormPage, self).update()

    @property
    def label(self):
        return self.context.title

    @action(_('Save'), style='primary')
    def save(self, **data):
        msave(self, **data)
        return


class DocumentEditFormPage(DocumentManageFormPage):
    """ Page to edit a document
    """
    grok.name('edit')
    grok.require('waeup.handleCustomer')

    def update(self):
        if not self.context.is_editable_by_customer:
            emit_lock_message(self)
            return
        return super(DocumentEditFormPage, self).update()

    @action(_('Save'), style='primary')
    def save(self, **data):
        msave(self, **data)
        return

    @action(_('Final Submit'), warning=WARNING_DOC)
    def finalsubmit(self, **data):
        msave(self, **data)
        IWorkflowInfo(self.context).fireTransition('submit')
        self.flash(_('Form has been submitted.'))
        self.redirect(self.url(self.context))
        return


class DocumentTriggerTransitionFormPage(IkobaEditFormPage):
    """ View to trigger customer document transitions
    """
    grok.context(ICustomerDocument)
    grok.name('trigtrans')
    grok.require('waeup.triggerTransition')
    grok.template('trigtrans')
    label = _('Trigger document transition')
    pnav = 4

    def update(self):
        return super(IkobaEditFormPage, self).update()

    def getTransitions(self):
        """Return a list of dicts of allowed transition ids and titles.

        Each list entry provides keys ``name`` and ``title`` for
        internal name and (human readable) title of a single
        transition.
        """
        wf_info = IWorkflowInfo(self.context)
        allowed_transitions = [t for t in wf_info.getManualTransitions()]
        return [dict(name='', title=_('No transition'))] +[
            dict(name=x, title=y) for x, y in allowed_transitions]

    @action(_('Apply'), style='primary')
    def apply(self, **data):
        form = self.request.form
        if 'transition' in form and form['transition']:
            transition_id = form['transition']
            wf_info = IWorkflowInfo(self.context)
            try:
                wf_info.fireTransition(transition_id)
                self.flash(_("Transition '%s' executed." % transition_id))
            except InvalidTransitionError, error:
                self.flash(error, type="warning")
            self.redirect(self.url(self.context))
        return

class PDFDocumentsOverviewPage(UtilityView, grok.View):
    """Deliver an overview slip.
    """
    grok.context(ICustomerDocumentsContainer)
    grok.name('documents_overview_slip.pdf')
    grok.require('waeup.viewCustomer')
    prefix = 'form'

    omit_fields = ('suspended', 'sex',
                   'suspended_comment',)

    form_fields = None

    @property
    def label(self):
        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
        return translate(_('Documents of'),
            'waeup.ikoba', target_language=portal_language) \
            + ' %s' % self.context.customer.display_fullname

    @property
    def tabletitle(self):
        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
        tabletitle = []
        tabletitle.append(translate(_('Customer Documents'), 'waeup.ikoba',
            target_language=portal_language))
        return tabletitle

    def render(self):
        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
        Id = translate(_('Id'), 'waeup.ikoba', target_language=portal_language)
        Title = translate(_('Title'), 'waeup.ikoba', target_language=portal_language)
        Type = translate(_('Type'), 'waeup.ikoba', target_language=portal_language)
        State = translate(_('State'), 'waeup.ikoba', target_language=portal_language)
        LT = translate(_('Last Transition'), 'waeup.ikoba', target_language=portal_language)
        tableheader = []
        tabledata = []
        for i in range(1,3):
            tabledata.append(sorted(
                [value for value in self.context.values()]))
            tableheader.append([(Id, 'document_id', 2),
                             (Title, 'title', 6),
                             (Type, 'translated_class_name', 6),
                             (State, 'translated_state', 2),
                             (LT, 'formatted_transition_date', 3),
                             ])
        customerview = CustomerBasePDFFormPage(self.context.customer,
            self.request, self.omit_fields)
        customers_utils = getUtility(ICustomersUtils)
        return customers_utils.renderPDF(
            self, 'overview_slip.pdf',
            self.context.customer, customerview,
            tableheader=tableheader,
            tabledata=tabledata,
            omit_fields=self.omit_fields)


class PDFDocumentSlipPage(UtilityView, grok.View):
    """Deliver pdf file including metadata.
    """
    grok.context(ICustomerDocument)
    grok.name('document_slip.pdf')
    grok.require('waeup.viewCustomer')
    prefix = 'form'

    omit_fields = ('suspended', 'sex',
                   'suspended_comment',)

    form_fields =()

    @property
    def label(self):
        return '%s of %s\nTitle: %s' % (
            self.context.translated_class_name,
            self.context.customer.display_fullname,
            self.context.title)

    def render(self):
        customerview = CustomerBasePDFFormPage(self.context.customer,
            self.request, self.omit_fields)
        customers_utils = getUtility(ICustomersUtils)
        return customers_utils.renderPDF(
            self, 'document_slip.pdf',
            self.context.customer, customerview,
            omit_fields=self.omit_fields)


class PDFMergeDocumentSlipPage(PDFDocumentSlipPage):
    """Deliver pdf file including metadata.
    """
    grok.context(ICustomerPDFDocument)

    def render(self):
        customerview = CustomerBasePDFFormPage(self.context.customer,
            self.request, self.omit_fields)
        customers_utils = getUtility(ICustomersUtils)
        if self.context.state == VERIFIED:
            watermark_path = os.path.join(
                os.path.dirname(__file__), 'static', 'verified.pdf')
        else:
            watermark_path = os.path.join(
                os.path.dirname(__file__), 'static', 'unverified.pdf')
        watermark = open(watermark_path, 'rb')
        return customers_utils.renderPDF(
            self, 'pdfdocument_slip.pdf',
            self.context.customer, customerview,
            omit_fields=self.omit_fields,
            mergefiles=self.context.connected_files,
            watermark=watermark)

# Pages for customer contracts

class ContractsBreadcrumb(Breadcrumb):
    """A breadcrumb for the contracts container.
    """
    grok.context(IContractsContainer)
    title = _('Contracts')


class ContractBreadcrumb(Breadcrumb):
    """A breadcrumb for the customer container.
    """
    grok.context(IContract)

    @property
    def title(self):
        return "%s" % self.context.contract_id[:9]


class ContractsFormPage(IkobaEditFormPage):
    """ Page to display, edit or manage customer contracts

    This form page is for both customers and officers.
    """
    grok.context(IContractsContainer)
    grok.name('index')
    grok.require('waeup.viewCustomer')
    form_fields = grok.AutoFields(IContractsContainer)
    grok.template('contractsmanagepage')
    pnav = 4

    @property
    def edit_contracts_allowed(self):
        right_customer_state = self.context.customer.state in getUtility(
            ICustomersUtils).CONMANAGE_CUSTOMER_STATES
        if isCustomer(self.request.principal) and not right_customer_state:
            return False
        return checkPermission('waeup.editContracts', self.context)

    def remove_contract_allowed(self, contract):
        if isCustomer(self.request.principal):
            right_customer_state = self.context.customer.state in getUtility(
                ICustomersUtils).CONMANAGE_CUSTOMER_STATES
            if not right_customer_state:
                return False
            if contract.state in (APPROVED, AWAITING, REJECTED, EXPIRED):
                return False
        return checkPermission('waeup.editContracts', self.context)

    @property
    def label(self):
        return _('${a}: Contracts',
            mapping = {'a':self.context.__parent__.display_fullname})

    @action(_('Add contract'), validator=NullValidator, style='primary')
    def addContract(self, **data):
        self.redirect(self.url(self.context, 'addcontract'))
        return

    @jsaction(_('Remove selected contracts'))
    def delContract(self, **data):
        form = self.request.form
        if 'val_id' in form:
            child_id = form['val_id']
        else:
            self.flash(_('No contract selected.'), type="warning")
            self.redirect(self.url(self.context))
            return
        if not isinstance(child_id, list):
            child_id = [child_id]
        deleted = []
        for id in child_id:
            # Customers are not allowed to remove used contracts
            contract = self.context.get(id, None)
            if contract is not None and self.remove_contract_allowed(contract):
                del self.context[id]
                deleted.append(id)
        if len(deleted):
            self.flash(_('Successfully removed: ${a}',
                mapping = {'a': ', '.join(deleted)}))
            self.context.writeLogMessage(
                self,'removed: %s' % ', '.join(deleted))
        self.redirect(self.url(self.context))
        return


class ContractAddFormPage(IkobaAddFormPage):
    """ Page to add an contract

    This page is for both customers and officers.
    """
    grok.context(IContractsContainer)
    grok.name('addcontract')
    grok.template('contractaddpage')
    grok.require('waeup.editContracts')
    label = _('Add contract')
    pnav = 4

    @property
    def edit_contracts_allowed(self):
        right_customer_state = self.context.customer.state in getUtility(
            ICustomersUtils).CONMANAGE_CUSTOMER_STATES
        if right_customer_state:
            return True
        return False

    def update(self):
        if not self.edit_contracts_allowed:
            emit_lock_message(self)
            return
        return super(ContractAddFormPage, self).update()

    @property
    def selectable_contypes(self):
        contypes = getUtility(ICustomersUtils).SELECTABLE_CONTYPES_DICT
        return sorted(contypes.items())

    @action(_('Add contract'), style='primary')
    def createContract(self, **data):
        form = self.request.form
        contype = form.get('contype', None)
        # Here we can create various instances of Contract derived
        # classes depending on the contype parameter given in form.
        contract = createObject('waeup.%s' % contype)
        self.context.addContract(contract)
        contype = getUtility(ICustomersUtils).SELECTABLE_CONTYPES_DICT[contype]
        self.flash(_('${a} added.', mapping = {'a': contype}))
        self.context.writeLogMessage(
            self,'added: %s %s' % (contype, contract.contract_id))
        self.redirect(self.url(contract, 'selectproduct'))
        return

    @action(_('Cancel'), validator=NullValidator)
    def cancel(self, **data):
        self.redirect(self.url(self.context))


class ContractSelectProductPage(IkobaAddFormPage):
    """ Page to select a contract product

    This page is for both customers and officers.
    """
    grok.context(IContract)
    grok.name('selectproduct')
    grok.require('waeup.editContracts')
    label = _('Select product')
    pnav = 4

    form_fields = grok.AutoFields(IContractSelectProduct)

    def update(self):
        if self.context.product_object is not None:
            emit_lock_message(self)
            return
        return super(ContractSelectProductPage, self).update()

    @action(_('Save and proceed'), style='primary')
    def save(self, **data):
        msave(self, **data)
        self.context.title = self.context.product_object.contract_autotitle
        self.context.tc_dict = self.context.product_object.tc_dict
        self.context.valid_from = self.context.product_object.valid_from
        self.context.valid_to = self.context.product_object.valid_to
        isCustomer = getattr(
            self.request.principal, 'user_type', None) == 'customer'
        if isCustomer:
            self.redirect(self.url(self.context, 'edit'))
        else:
            self.redirect(self.url(self.context, 'manage'))
        return


class ContractDisplayFormPage(IkobaDisplayFormPage):
    """ Page to view a contract
    """
    grok.context(IContract)
    grok.name('index')
    grok.require('waeup.viewCustomer')
    grok.template('contractpage')
    pnav = 4
    label = None  # We render the context title in the contractpage template

    @property
    def form_fields(self):
        form_fields = grok.AutoFields(self.context.form_fields_interface)
        for field in form_fields:
            if field.__name__.endswith('_object'):
                form_fields[field.__name__].custom_widget = HREFDisplayWidget
        return form_fields

    @property
    def terms_and_conditions(self):
        lang = self.request.cookies.get('ikoba.language')
        html = self.context.tc_dict.get(lang,'')
        if html =='':
            portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
            html = self.context.tc_dict.get(portal_language,'')
        return html


class ContractManageFormPage(IkobaEditFormPage):
    """ Page to manage a contract
    """
    grok.context(IContract)
    grok.name('manage')
    grok.require('waeup.manageCustomer')
    grok.template('contracteditpage')
    pnav = 4
    deletion_warning = _('Are you sure?')

    def update(self):
        if not self.context.is_editable:
            emit_lock_message(self)
            return
        return super(ContractManageFormPage, self).update()

    @property
    def terms_and_conditions(self):
        lang = self.request.cookies.get('ikoba.language')
        html = self.context.tc_dict.get(lang,'')
        if html =='':
            portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
            html = self.context.tc_dict.get(portal_language,'')
        return html

    @property
    def form_fields(self):
        return grok.AutoFields(self.context.form_fields_interface).omit(
            'contract_id', 'product_object')

    @property
    def label(self):
        return self.context.title

    @action(_('Save'), style='primary')
    def save(self, **data):
        msave(self, **data)
        return


class ContractOfficialUsePage(IkobaEditFormPage):
    """ Page to manage a official use data of contract
    """
    grok.context(IContract)
    grok.name('official_use')
    grok.require('waeup.manageCustomer')
    grok.template('contracteditpage')
    pnav = 4
    deletion_warning = _('Are you sure?')
    terms_and_conditions = None

    @property
    def form_fields(self):
        return grok.AutoFields(self.context.ou_form_fields_interface)

    @property
    def label(self):
        return self.context.title

    @action(_('Save'), style='primary')
    def save(self, **data):
        msave(self, **data)
        return


class ContractEditFormPage(ContractManageFormPage):
    """ Page to edit a contract by customer only
    """
    grok.name('edit')
    grok.require('waeup.handleCustomer')

    def submission_allowed(self, action=None):
        if self.context.state == CREATED and not self.context.fee_based:
            return True
        return False

    def payment_expected(self, action=None):
        if self.context.state == CREATED and self.context.fee_based:
            return True
        return False

    @property
    def form_fields(self):
        return grok.AutoFields(self.context.edit_form_fields_interface).omit(
            'contract_id', 'product_object')

    @action(_('Save'), style='primary')
    def save(self, **data):
        msave(self, **data)
        return

    @action(_('Apply now (final submission)'), warning=WARNING_CON,
            condition=submission_allowed, style='primary')
    def submit(self, **data):
        if self.terms_and_conditions and not self.request.form.get(
            'confirm_tc', False):
            self.flash(_('Please read the terms and conditions and '
                     'confirm your acceptance of these by ticking '
                     'the confirmation box.'), type="danger")
            return
        msave(self, **data)
        IWorkflowInfo(self.context).fireTransition('submit')
        self.flash(_('Application form has been submitted.'))
        self.redirect(self.url(self.context))
        return

    @action(_('Proceed to checkout'),
            condition=payment_expected, style='primary')
    def proceed_checkout(self, **data):
        if self.terms_and_conditions and not self.request.form.get(
            'confirm_tc', False):
            self.flash(_('Please read the terms and conditions and '
                     'confirm your acceptance of these by ticking '
                     'the confirmation box.'), type="danger")
            return
        msave(self, **data)
        self.redirect(self.url(self.context, 'select_payment_method'))
        return


class ContractSelectPaymentMethodPage(IkobaEditFormPage):
    """ Page to to select payment method
    """
    grok.context(IContract)
    grok.name('select_payment_method')
    grok.require('waeup.handleCustomer')
    grok.template('selectpaymentmethodpage')
    pnav = 4
    label = _('Select payment method')

    @property
    def payment_gateways(self):
        """Get an iter over registered and enabled gateway service providers.

        We provide tuples ``(value, description)`` for each supported
        payment gateway.
        """
        lister = getUtility(IPaymentGatewayServicesLister)
        for name, service in lister().items():
            yield {'name': name, 'title': service.title}

    def update(self, CANCEL=None, gw=None):
        if self.context.state != CREATED or not self.context.fee_based:
            emit_lock_message(self)
            return
        self.gw = gw
        super(ContractSelectPaymentMethodPage, self).update()
        return

    @action(_('Select payment method (final submission)'),
            style='primary')
    def confirm(self, **data):
        if self.gw is None:
            self.flash(_('Please pick a payment method.'),
                       type='warning')
        else:
            service = queryUtility(IPaymentGatewayService, name=self.gw)
            if service is None:
                self.flash(_('Invalid payment gateway.'), type='danger')
                return
            payer = IPayer(self.context)
            payable = IPayable(self.context)
            payment = service.create_payment(payer, payable)
            service.store(payment)
            payment, view_name = service.next_step(payment.payment_id)
            url = self.url(payment, view_name)
            self.redirect(url)
            return
        return

    @action(_('Cancel'))
    def cancel(self, **data):
        self.redirect(self.url(self.context, 'edit'))
        return


class ContractTriggerTransitionFormPage(IkobaEditFormPage):
    """ View to trigger customer contract transitions
    """
    grok.context(IContract)
    grok.name('trigtrans')
    grok.require('waeup.triggerTransition')
    grok.template('trigtrans')
    label = _('Trigger contract transition')
    pnav = 4

    def update(self):
        return super(IkobaEditFormPage, self).update()

    def getTransitions(self):
        """Return a list of dicts of allowed transition ids and titles.

        Each list entry provides keys ``name`` and ``title`` for
        internal name and (human readable) title of a single
        transition.
        """
        wf_info = IWorkflowInfo(self.context)
        allowed_transitions = [t for t in wf_info.getManualTransitions()]
        # Skip payment transitions if total amount is zero
        if not self.context.fee_based:
            allowed_transitions = [t for t in allowed_transitions
                                   if not t[0] in PAYMENT_TRANSITIONS]
        return [dict(name='', title=_('No transition'))] +[
            dict(name=x, title=y) for x, y in allowed_transitions]

    @action(_('Apply'), style='primary')
    def apply(self, **data):
        form = self.request.form
        if 'transition' in form and form['transition']:
            transition_id = form['transition']
            wf_info = IWorkflowInfo(self.context)
            try:
                wf_info.fireTransition(transition_id)
                self.flash(_("Transition '%s' executed." % transition_id))
            except InvalidTransitionError, error:
                self.flash(error, type="warning")
            self.redirect(self.url(self.context))
        return

class PDFContractsOverviewPage(UtilityView, grok.View):
    """Deliver an overview slip.
    """
    grok.context(IContractsContainer)
    grok.name('contracts_overview_slip.pdf')
    grok.require('waeup.viewCustomer')
    prefix = 'form'

    omit_fields = ('suspended', 'sex',
                   'suspended_comment',)

    form_fields = None

    @property
    def label(self):
        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
        return translate(_('Contracts of'),
            'waeup.ikoba', target_language=portal_language) \
            + ' %s' % self.context.customer.display_fullname

    @property
    def tabletitle(self):
        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
        tabletitle = []
        tabletitle.append(translate(_('Customer Contracts'), 'waeup.ikoba',
            target_language=portal_language))
        return tabletitle

    def render(self):
        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
        #Id = translate(_('Id'), 'waeup.ikoba', target_language=portal_language)
        Title = translate(_('Title'), 'waeup.ikoba', target_language=portal_language)
        Type = translate(_('Type'), 'waeup.ikoba', target_language=portal_language)
        State = translate(_('State'), 'waeup.ikoba', target_language=portal_language)
        LT = translate(_('Last Transition'), 'waeup.ikoba', target_language=portal_language)
        tableheader = []
        tabledata = []
        for i in range(1,3):
            tabledata.append(sorted(
                [value for value in self.context.values()]))
            tableheader.append([
                             #(Id, 'contract_id', 2),
                             (Title, 'title', 6),
                             (Type, 'translated_class_name', 6),
                             (State, 'translated_state', 2),
                             (LT, 'formatted_transition_date', 3),
                             ])
        customerview = CustomerBasePDFFormPage(self.context.customer,
            self.request, self.omit_fields)
        customers_utils = getUtility(ICustomersUtils)
        return customers_utils.renderPDF(
            self, 'overview_slip.pdf',
            self.context.customer, customerview,
            tableheader=tableheader,
            tabledata=tabledata,
            omit_fields=self.omit_fields)


class PDFContractSlipPage(UtilityView, grok.View):
    """Deliver pdf file including metadata.
    """
    grok.context(IContract)
    grok.name('contract_slip.pdf')
    grok.require('waeup.viewCustomer')
    prefix = 'form'

    omit_fields = ('suspended', 'sex',
                   'suspended_comment',)

    @property
    def form_fields(self):
        return grok.AutoFields(self.context.form_fields_interface)

    @property
    def terms_and_conditions(self):
        lang = self.request.cookies.get('ikoba.language')
        html = self.context.tc_dict.get(lang,'')
        if html =='':
            portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
            html = self.context.tc_dict.get(portal_language,'')
        return html

    @property
    def validity_period(self):
        if self.context.valid_from or self.context.valid_to:
            return "%s - %s" % (format_date(self.context.valid_from, self.request),
                                format_date(self.context.valid_to, self.request))
        return

    @property
    def label(self):
        return self.context.title

    # XXX: not used in waeup.ikoba and thus not tested
    def _signatures(self):
        return ([_('Customer Signature')],
                [_('Company Officer Signature')]
                )

    def _sigsInFooter(self):
        return (_('Date, Customer Signature'),
                _('Date, Company Officer Signature'),
                )

    def render(self):
        customerview = CustomerBasePDFFormPage(self.context.customer,
            self.request, self.omit_fields)
        customers_utils = getUtility(ICustomersUtils)
        return customers_utils.renderPDF(
            self, 'contract_slip.pdf',
            self.context.customer, customerview,
            signatures=self._signatures(),
            sigs_in_footer=self._sigsInFooter(),
            omit_fields=self.omit_fields)


class PaymentsPage(IkobaPage):
    """ Page to display all payments made by customer.

    """
    grok.context(ICustomer)
    grok.name('payments')
    grok.require('waeup.manageCustomer')
    grok.template('paymentspage')
    pnav = 4

    @property
    def label(self):
        return _('${a}: Payments', mapping={'a':self.context.display_fullname})

    @property
    def payment_states(self):
        return payment_states

    @property
    def gateway_services(self):
        return get_payment_providers()

    def payments(self):
        return search_payments(
            query=self.context.customer_id, searchtype='payer_id')


class PDFContractReceiptPage(UtilityView, grok.View):
    """Deliver pdf file including metadata.
    """
    grok.context(IContract)
    grok.name('contract_payment_receipt.pdf')
    grok.require('waeup.viewCustomer')
    prefix = 'form'

    omit_fields = ('suspended', 'sex',
                   'suspended_comment',)

    @property
    def form_fields(self):
        return grok.AutoFields(self.context.form_fields_interface)

    @property
    def label(self):
        return 'Payment Receipt: %s' % (
            self.context.title)

    @property
    def payment_tuples(self):
        payment_tuples = []
        for payment in get_payments_from_payable_id(self.context.contract_id):
            form_fields = grok.AutoFields(payment.form_fields_interface).omit(
                'payment_items')
            form_fields[
                'creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
            form_fields[
                'payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
            payment_tuples.append((payment, form_fields))
        return payment_tuples

    def render(self):
        customerview = CustomerBasePDFFormPage(self.context.customer,
            self.request, self.omit_fields)
        customers_utils = getUtility(ICustomersUtils)
        return customers_utils.renderPDF(
            self, 'contract_payment_receipt.pdf',
            self.context.customer, customerview,
            omit_fields=self.omit_fields,
            show_history=False)
