source: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser.py @ 12353

Last change on this file since 12353 was 12353, checked in by Henrik Bettermann, 10 years ago

Change button text.

Extend test.

  • Property svn:keywords set to Id
File size: 51.6 KB
Line 
1## $Id: browser.py 12353 2014-12-31 16:09:01Z henrik $
2##
3## Copyright (C) 2014 Uli Fouquet & Henrik Bettermann
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
8##
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13##
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18"""UI components for customers and related components.
19"""
20
21import sys
22import grok
23import pytz
24import os
25from urllib import urlencode
26from datetime import datetime
27from zope.event import notify
28from zope.i18n import translate
29from zope.catalog.interfaces import ICatalog
30from zope.component import queryUtility, getUtility, createObject
31from zope.schema.interfaces import ConstraintNotSatisfied, RequiredMissing
32from zope.formlib.textwidgets import BytesDisplayWidget
33from zope.security import checkPermission
34from hurry.workflow.interfaces import (
35    IWorkflowInfo, IWorkflowState, InvalidTransitionError)
36from waeup.ikoba.interfaces import MessageFactory as _
37from waeup.ikoba.interfaces import (
38    IContactForm, IObjectHistory, IIkobaObject, IIkobaUtils,
39    IPasswordValidator, IUserAccount,
40    VERIFIED, REJECTED, EXPIRED, REQUESTED, APPROVED)
41from waeup.ikoba.browser.layout import (
42    IkobaPage, IkobaEditFormPage, IkobaAddFormPage, IkobaDisplayFormPage,
43    IkobaForm, NullValidator, jsaction, action, UtilityView)
44from waeup.ikoba.widgets.datewidget import (
45    FriendlyDateWidget, FriendlyDateDisplayWidget,
46    FriendlyDatetimeDisplayWidget)
47from waeup.ikoba.browser.pages import ContactAdminForm
48from waeup.ikoba.browser.breadcrumbs import Breadcrumb
49from waeup.ikoba.browser.interfaces import ICaptchaManager
50from waeup.ikoba.mandates.mandate import PasswordMandate
51from waeup.ikoba.widgets.hrefwidget import HREFDisplayWidget
52from waeup.ikoba.utils.helpers import get_current_principal, to_timezone, now
53from waeup.ikoba.customers.interfaces import (
54    ICustomer, ICustomersContainer, ICustomerRequestPW, ICustomersUtils,
55    ICustomerDocument, ICustomerDocumentsContainer, ICustomerCreate,
56    ICustomerPDFDocument, IContractsContainer, IContract, IContractEdit,
57    ISampleContract,
58    )
59from waeup.ikoba.customers.catalog import search
60
61grok.context(IIkobaObject)
62
63WARNING_CUST = _('You can not edit some data after final submission.'
64            ' You really want to submit?')
65
66WARNING_DOC = _('You can not edit your document after final submission.'
67            ' You really want to submit?')
68
69WARNING_CON = _('You can not edit your contract after final submission.'
70            ' You really want to submit?')
71
72
73# Save function used for save methods in pages
74def msave(view, **data):
75    changed_fields = view.applyData(view.context, **data)
76    # Turn list of lists into single list
77    if changed_fields:
78        changed_fields = reduce(lambda x,y: x+y, changed_fields.values())
79    if 'product_object' in changed_fields and data['product_object'] is not None:
80        view.context.last_product_id = data['product_object'].product_id
81        notify(grok.ObjectModifiedEvent(view.context))
82    fields_string = ' + '.join(changed_fields)
83    view.flash(_('Form has been saved.'))
84    if fields_string:
85        view.context.writeLogMessage(
86            view, '%s - saved: %s' % (view.context.__name__, fields_string))
87    return
88
89
90def emit_lock_message(view):
91    """Flash a lock message.
92    """
93    view.flash(_('The requested form is locked (read-only).'), type="warning")
94    view.redirect(view.url(view.context))
95    return
96
97def isCustomer(principal):
98    return getattr(principal, 'user_type', None) == 'customer'
99
100
101class CustomersBreadcrumb(Breadcrumb):
102    """A breadcrumb for the customers container.
103    """
104    grok.context(ICustomersContainer)
105    title = _('Customers')
106
107    @property
108    def target(self):
109        user = get_current_principal()
110        if getattr(user, 'user_type', None) == 'customer':
111            return None
112        return self.viewname
113
114
115class CustomerBreadcrumb(Breadcrumb):
116    """A breadcrumb for the customer container.
117    """
118    grok.context(ICustomer)
119
120    def title(self):
121        return self.context.display_fullname
122
123
124class CustomersContainerPage(IkobaPage):
125    """The standard view for customer containers.
126    """
127    grok.context(ICustomersContainer)
128    grok.name('index')
129    grok.require('waeup.viewCustomersContainer')
130    grok.template('containerpage')
131    label = _('Find customers')
132    search_button = _('Find customer(s)')
133    pnav = 4
134
135    def update(self, *args, **kw):
136        form = self.request.form
137        self.hitlist = []
138        if form.get('searchtype', None) == 'suspended':
139            self.searchtype = form['searchtype']
140            self.searchterm = None
141        elif 'searchterm' in form and form['searchterm']:
142            self.searchterm = form['searchterm']
143            self.searchtype = form['searchtype']
144        elif 'old_searchterm' in form:
145            self.searchterm = form['old_searchterm']
146            self.searchtype = form['old_searchtype']
147        else:
148            if 'search' in form:
149                self.flash(_('Empty search string'), type="warning")
150            return
151        if self.searchtype == 'current_session':
152            try:
153                self.searchterm = int(self.searchterm)
154            except ValueError:
155                self.flash(_('Only year dates allowed (e.g. 2011).'),
156                           type="danger")
157                return
158        self.hitlist = search(query=self.searchterm,
159            searchtype=self.searchtype, view=self)
160        if not self.hitlist:
161            self.flash(_('No customer found.'), type="warning")
162        return
163
164
165class CustomersContainerManagePage(IkobaPage):
166    """The manage page for customer containers.
167    """
168    grok.context(ICustomersContainer)
169    grok.name('manage')
170    grok.require('waeup.manageCustomer')
171    grok.template('containermanagepage')
172    pnav = 4
173    label = _('Manage customer section')
174    search_button = _('Find customer(s)')
175    remove_button = _('Remove selected')
176
177    def update(self, *args, **kw):
178        form = self.request.form
179        self.hitlist = []
180        if form.get('searchtype', None) == 'suspended':
181            self.searchtype = form['searchtype']
182            self.searchterm = None
183        elif 'searchterm' in form and form['searchterm']:
184            self.searchterm = form['searchterm']
185            self.searchtype = form['searchtype']
186        elif 'old_searchterm' in form:
187            self.searchterm = form['old_searchterm']
188            self.searchtype = form['old_searchtype']
189        else:
190            if 'search' in form:
191                self.flash(_('Empty search string'), type="warning")
192            return
193        if self.searchtype == 'current_session':
194            try:
195                self.searchterm = int(self.searchterm)
196            except ValueError:
197                self.flash(_('Only year dates allowed (e.g. 2011).'),
198                           type="danger")
199                return
200        if not 'entries' in form:
201            self.hitlist = search(query=self.searchterm,
202                searchtype=self.searchtype, view=self)
203            if not self.hitlist:
204                self.flash(_('No customer found.'), type="warning")
205            if 'remove' in form:
206                self.flash(_('No item selected.'), type="warning")
207            return
208        entries = form['entries']
209        if isinstance(entries, basestring):
210            entries = [entries]
211        deleted = []
212        for entry in entries:
213            if 'remove' in form:
214                del self.context[entry]
215                deleted.append(entry)
216        self.hitlist = search(query=self.searchterm,
217            searchtype=self.searchtype, view=self)
218        if len(deleted):
219            self.flash(_('Successfully removed: ${a}',
220                mapping={'a': ','.join(deleted)}))
221        return
222
223
224class CustomerAddFormPage(IkobaAddFormPage):
225    """Add-form to add a customer.
226    """
227    grok.context(ICustomersContainer)
228    grok.require('waeup.manageCustomer')
229    grok.name('addcustomer')
230    form_fields = grok.AutoFields(ICustomer).select(
231        'firstname', 'middlename', 'lastname', 'reg_number')
232    label = _('Add customer')
233    pnav = 4
234
235    @action(_('Create customer record'), style='primary')
236    def addCustomer(self, **data):
237        customer = createObject(u'waeup.Customer')
238        self.applyData(customer, **data)
239        self.context.addCustomer(customer)
240        self.flash(_('Customer created.'))
241        self.redirect(self.url(self.context[customer.customer_id], 'index'))
242        return
243
244
245class LoginAsCustomerStep1(IkobaEditFormPage):
246    """ View to temporarily set a customer password.
247    """
248    grok.context(ICustomer)
249    grok.name('loginasstep1')
250    grok.require('waeup.loginAsCustomer')
251    grok.template('loginasstep1')
252    pnav = 4
253
254    def label(self):
255        return _(u'Set temporary password for ${a}',
256            mapping={'a': self.context.display_fullname})
257
258    @action('Set password now', style='primary')
259    def setPassword(self, *args, **data):
260        ikoba_utils = getUtility(IIkobaUtils)
261        password = ikoba_utils.genPassword()
262        self.context.setTempPassword(self.request.principal.id, password)
263        self.context.writeLogMessage(
264            self, 'temp_password generated: %s' % password)
265        args = {'password': password}
266        self.redirect(self.url(self.context) +
267            '/loginasstep2?%s' % urlencode(args))
268        return
269
270
271class LoginAsCustomerStep2(IkobaPage):
272    """ View to temporarily login as customer with a temporary password.
273    """
274    grok.context(ICustomer)
275    grok.name('loginasstep2')
276    grok.require('waeup.Public')
277    grok.template('loginasstep2')
278    login_button = _('Login now')
279    pnav = 4
280
281    def label(self):
282        return _(u'Login as ${a}',
283            mapping={'a': self.context.customer_id})
284
285    def update(self, SUBMIT=None, password=None):
286        self.password = password
287        if SUBMIT is not None:
288            self.flash(_('You successfully logged in as customer.'))
289            self.redirect(self.url(self.context))
290        return
291
292
293class CustomerBaseDisplayFormPage(IkobaDisplayFormPage):
294    """ Page to display customer base data
295    """
296    grok.context(ICustomer)
297    grok.name('index')
298    grok.require('waeup.viewCustomer')
299    grok.template('basepage')
300    form_fields = grok.AutoFields(ICustomer).omit(
301        'password', 'suspended', 'suspended_comment')
302    pnav = 4
303
304    @property
305    def label(self):
306        if self.context.suspended:
307            return _('${a}: Base Data (account deactivated)',
308                mapping={'a': self.context.display_fullname})
309        return  _('${a}: Base Data',
310            mapping={'a': self.context.display_fullname})
311
312    @property
313    def hasPassword(self):
314        if self.context.password:
315            return _('set')
316        return _('unset')
317
318    @property
319    def is_requestable(self):
320        if self.context.state in (REQUESTED, APPROVED):
321            return False
322        return True
323
324
325class ContactCustomerForm(ContactAdminForm):
326    grok.context(ICustomer)
327    grok.name('contactcustomer')
328    grok.require('waeup.viewCustomer')
329    pnav = 4
330    form_fields = grok.AutoFields(IContactForm).select('subject', 'body')
331
332    def update(self, subject=u'', body=u''):
333        super(ContactCustomerForm, self).update()
334        self.form_fields.get('subject').field.default = subject
335        self.form_fields.get('body').field.default = body
336        return
337
338    def label(self):
339        return _(u'Send message to ${a}',
340            mapping={'a': self.context.display_fullname})
341
342    @action('Send message now', style='primary')
343    def send(self, *args, **data):
344        try:
345            email = self.request.principal.email
346        except AttributeError:
347            email = self.config.email_admin
348        usertype = getattr(self.request.principal,
349                           'user_type', 'system').title()
350        ikoba_utils = getUtility(IIkobaUtils)
351        success = ikoba_utils.sendContactForm(
352                self.request.principal.title, email,
353                self.context.display_fullname, self.context.email,
354                self.request.principal.id,usertype,
355                self.config.name,
356                data['body'], data['subject'])
357        if success:
358            self.flash(_('Your message has been sent.'))
359        else:
360            self.flash(_('An smtp server error occurred.'), type="danger")
361        return
362
363
364class CustomerBaseManageFormPage(IkobaEditFormPage):
365    """ View to manage customer base data
366    """
367    grok.context(ICustomer)
368    grok.name('manage_base')
369    grok.require('waeup.manageCustomer')
370    form_fields = grok.AutoFields(ICustomer).omit(
371        'customer_id', 'adm_code', 'suspended')
372    grok.template('basemanagepage')
373    label = _('Manage base data')
374    pnav = 4
375
376    def update(self):
377        super(CustomerBaseManageFormPage, self).update()
378        self.wf_info = IWorkflowInfo(self.context)
379        return
380
381    @action(_('Save'), style='primary')
382    def save(self, **data):
383        form = self.request.form
384        password = form.get('password', None)
385        password_ctl = form.get('control_password', None)
386        if password:
387            validator = getUtility(IPasswordValidator)
388            errors = validator.validate_password(password, password_ctl)
389            if errors:
390                self.flash(' '.join(errors), type="danger")
391                return
392        changed_fields = self.applyData(self.context, **data)
393        # Turn list of lists into single list
394        if changed_fields:
395            changed_fields = reduce(lambda x,y: x+y, changed_fields.values())
396        else:
397            changed_fields = []
398        if password:
399            # Now we know that the form has no errors and can set password
400            IUserAccount(self.context).setPassword(password)
401            changed_fields.append('password')
402        fields_string = ' + '.join(changed_fields)
403        self.flash(_('Form has been saved.'))
404        if fields_string:
405            self.context.writeLogMessage(self, 'saved: % s' % fields_string)
406        return
407
408
409class CustomerTriggerTransitionFormPage(IkobaEditFormPage):
410    """ View to trigger customer workflow transitions
411    """
412    grok.context(ICustomer)
413    grok.name('trigtrans')
414    grok.require('waeup.triggerTransition')
415    grok.template('trigtrans')
416    label = _('Trigger registration transition')
417    pnav = 4
418
419    def getTransitions(self):
420        """Return a list of dicts of allowed transition ids and titles.
421
422        Each list entry provides keys ``name`` and ``title`` for
423        internal name and (human readable) title of a single
424        transition.
425        """
426        wf_info = IWorkflowInfo(self.context)
427        allowed_transitions = [t for t in wf_info.getManualTransitions()]
428        return [dict(name='', title=_('No transition'))] +[
429            dict(name=x, title=y) for x, y in allowed_transitions]
430
431    @action(_('Apply'), style='primary')
432    def apply(self, **data):
433        form = self.request.form
434        if 'transition' in form and form['transition']:
435            transition_id = form['transition']
436            wf_info = IWorkflowInfo(self.context)
437            wf_info.fireTransition(transition_id)
438            self.flash(_("Transition '%s' executed." % transition_id))
439            self.redirect(self.url(self.context))
440        return
441
442
443class CustomerActivatePage(UtilityView, grok.View):
444    """ Activate customer account
445    """
446    grok.context(ICustomer)
447    grok.name('activate')
448    grok.require('waeup.manageCustomer')
449
450    def update(self):
451        self.context.suspended = False
452        self.context.writeLogMessage(self, 'account activated')
453        history = IObjectHistory(self.context)
454        history.addMessage('Customer account activated')
455        self.flash(_('Customer account has been activated.'))
456        self.redirect(self.url(self.context))
457        return
458
459    def render(self):
460        return
461
462
463class CustomerDeactivatePage(UtilityView, grok.View):
464    """ Deactivate customer account
465    """
466    grok.context(ICustomer)
467    grok.name('deactivate')
468    grok.require('waeup.manageCustomer')
469
470    def update(self):
471        self.context.suspended = True
472        self.context.writeLogMessage(self, 'account deactivated')
473        history = IObjectHistory(self.context)
474        history.addMessage('Customer account deactivated')
475        self.flash(_('Customer account has been deactivated.'))
476        self.redirect(self.url(self.context))
477        return
478
479    def render(self):
480        return
481
482
483class CustomerHistoryPage(IkobaPage):
484    """ Page to display customer history
485    """
486    grok.context(ICustomer)
487    grok.name('history')
488    grok.require('waeup.viewCustomer')
489    grok.template('customerhistory')
490    pnav = 4
491
492    @property
493    def label(self):
494        return _('${a}: History', mapping={'a':self.context.display_fullname})
495
496
497class CustomerRequestPasswordPage(IkobaAddFormPage):
498    """Captcha'd password request page for customers.
499    """
500    grok.name('requestpw')
501    grok.require('waeup.Anonymous')
502    grok.template('requestpw')
503    form_fields = grok.AutoFields(ICustomerRequestPW)
504    label = _('Request password for first-time login')
505
506    def update(self):
507        # Handle captcha
508        self.captcha = getUtility(ICaptchaManager).getCaptcha()
509        self.captcha_result = self.captcha.verify(self.request)
510        self.captcha_code = self.captcha.display(self.captcha_result.error_code)
511        return
512
513    def _redirect(self, email, password, customer_id):
514        # Forward only email address to landing page in base package.
515        self.redirect(self.url(self.context, 'requestpw_complete',
516            data=dict(email=email)))
517        return
518
519    def _pw_used(self):
520        # XXX: False if password has not been used. We need an extra
521        #      attribute which remembers if customer logged in.
522        return True
523
524    @action(_('Send login credentials to email address'), style='primary')
525    def get_credentials(self, **data):
526        if not self.captcha_result.is_valid:
527            # Captcha will display error messages automatically.
528            # No need to flash something.
529            return
530        number = data.get('number','')
531        firstname = data.get('firstname','')
532        cat = getUtility(ICatalog, name='customers_catalog')
533        results = list(
534            cat.searchResults(reg_number=(number, number)))
535        if results:
536            customer = results[0]
537            if getattr(customer,'firstname',None) is None:
538                self.flash(_('An error occurred.'), type="danger")
539                return
540            elif customer.firstname.lower() != firstname.lower():
541                # Don't tell the truth here. Anonymous must not
542                # know that a record was found and only the firstname
543                # verification failed.
544                self.flash(_('No customer found.'), type="warning")
545                return
546            elif customer.password is not None and self._pw_used:
547                self.flash(_('Your password has already been set and used. '
548                             'Please proceed to the login page.'),
549                           type="warning")
550                return
551            # Store email address but nothing else.
552            customer.email = data['email']
553            notify(grok.ObjectModifiedEvent(customer))
554        else:
555            # No record found, this is the truth.
556            self.flash(_('No customer found.'), type="warning")
557            return
558
559        ikoba_utils = getUtility(IIkobaUtils)
560        password = ikoba_utils.genPassword()
561        mandate = PasswordMandate()
562        mandate.params['password'] = password
563        mandate.params['user'] = customer
564        site = grok.getSite()
565        site['mandates'].addMandate(mandate)
566        # Send email with credentials
567        args = {'mandate_id':mandate.mandate_id}
568        mandate_url = self.url(site) + '/mandate?%s' % urlencode(args)
569        url_info = u'Confirmation link: %s' % mandate_url
570        msg = _('You have successfully requested a password for the')
571        if ikoba_utils.sendCredentials(IUserAccount(customer),
572            password, url_info, msg):
573            email_sent = customer.email
574        else:
575            email_sent = None
576        self._redirect(email=email_sent, password=password,
577            customer_id=customer.customer_id)
578        ob_class = self.__implemented__.__name__.replace('waeup.ikoba.','')
579        self.context.logger.info(
580            '%s - %s (%s) - %s' % (ob_class, number, customer.customer_id, email_sent))
581        return
582
583
584class CustomerCreateAccountPage(IkobaAddFormPage):
585    """Captcha'd account creation page for customers.
586    """
587    grok.name('createaccount')
588    grok.require('waeup.Anonymous')
589    grok.template('createaccount')
590    form_fields = grok.AutoFields(ICustomerCreate)
591    label = _('Create customer account')
592
593    def update(self):
594        # Handle captcha
595        self.captcha = getUtility(ICaptchaManager).getCaptcha()
596        self.captcha_result = self.captcha.verify(self.request)
597        self.captcha_code = self.captcha.display(self.captcha_result.error_code)
598        return
599
600    def _redirect(self, email, password, customer_id):
601        # Forward only email address to landing page in base package.
602        self.redirect(self.url(self.context, 'requestpw_complete',
603            data=dict(email=email)))
604        return
605
606    @action(_('Send login credentials to email address'), style='primary')
607    def create_account(self, **data):
608        if not self.captcha_result.is_valid:
609            # Captcha will display error messages automatically.
610            # No need to flash something.
611            return
612        customer = createObject(u'waeup.Customer')
613        customer.firstname = data.get('firstname','')
614        customer.middlename = data.get('middlename','')
615        customer.lastname = data.get('lastname','')
616        customer.email = data.get('email','')
617        self.context['customers'].addCustomer(customer)
618        ikoba_utils = getUtility(IIkobaUtils)
619        password = ikoba_utils.genPassword()
620        mandate = PasswordMandate()
621        mandate.params['password'] = password
622        mandate.params['user'] = customer
623        site = grok.getSite()
624        site['mandates'].addMandate(mandate)
625        # Send email with credentials
626        args = {'mandate_id':mandate.mandate_id}
627        mandate_url = self.url(site) + '/mandate?%s' % urlencode(args)
628        url_info = u'Confirmation link: %s' % mandate_url
629        msg = _('You have successfully created a customer account for the')
630        if ikoba_utils.sendCredentials(IUserAccount(customer),
631            password, url_info, msg):
632            email_sent = customer.email
633        else:
634            email_sent = None
635        self._redirect(email=email_sent, password=password,
636            customer_id=customer.customer_id)
637        ob_class = self.__implemented__.__name__.replace('waeup.ikoba.','')
638        self.context.logger.info(
639            '%s - %s - %s' % (ob_class, customer.customer_id, email_sent))
640        return
641
642
643class CustomerRequestPasswordEmailSent(IkobaPage):
644    """Landing page after successful password request.
645
646    """
647    grok.name('requestpw_complete')
648    grok.require('waeup.Public')
649    grok.template('requestpwmailsent')
650    label = _('Your request was successful.')
651
652    def update(self, email=None, customer_id=None, password=None):
653        self.email = email
654        self.password = password
655        self.customer_id = customer_id
656        return
657
658
659class CustomerFilesUploadPage(IkobaPage):
660    """ View to upload files by customer
661
662    We use this page only to upload a passport picture (portrait).
663    """
664    grok.context(ICustomer)
665    grok.name('change_portrait')
666    grok.require('waeup.handleCustomer')
667    grok.template('filesuploadpage')
668    label = _('Change portrait')
669    pnav = 4
670
671    def update(self, CANCEL=None):
672        CUSTMANAGE_STATES = getUtility(
673            ICustomersUtils).CUSTMANAGE_CUSTOMER_STATES
674        if self.context.customer.state not in CUSTMANAGE_STATES:
675            emit_lock_message(self)
676            return
677        if CANCEL is not None:
678            self.redirect(self.url(self.context))
679            return
680        super(CustomerFilesUploadPage, self).update()
681        return
682
683# Pages for customers
684
685
686class CustomerBaseEditFormPage(IkobaEditFormPage):
687    """ View to edit customer base data
688    """
689    grok.context(ICustomer)
690    grok.name('edit_base')
691    grok.require('waeup.handleCustomer')
692    pnav = 4
693
694    @property
695    def label(self):
696        if self.is_requestable():
697            return _('Edit base data and request registration')
698        return _('Edit base data')
699
700    @property
701    def form_fields(self):
702        if self.context.state in (REQUESTED, APPROVED):
703            return grok.AutoFields(ICustomer).select('email', 'phone')
704        return grok.AutoFields(ICustomer).omit(
705            'suspended', 'suspended_comment', 'reg_number', 'customer_id')
706
707    @action(_('Save'), style='primary')
708    def save(self, **data):
709        msave(self, **data)
710        return
711
712    def is_requestable(self, action=None):
713        if self.context.state in (REQUESTED, APPROVED):
714            return False
715        return True
716
717    @action(_('Request registration now'),
718            warning=WARNING_CUST, condition=is_requestable)
719    def finalsubmit(self, **data):
720        msave(self, **data)
721        IWorkflowInfo(self.context).fireTransition('request')
722        self.flash(_('Registration form has been submitted.'))
723        self.redirect(self.url(self.context))
724        return
725
726    @action(_('Cancel'), validator=NullValidator)
727    def cancel(self, **data):
728        self.redirect(self.url(self.context))
729
730
731class CustomerChangePasswordPage(IkobaEditFormPage):
732    """ View to edit customer passords
733    """
734    grok.context(ICustomer)
735    grok.name('changepassword')
736    grok.require('waeup.handleCustomer')
737    grok.template('changepassword')
738    label = _('Change password')
739    pnav = 4
740
741    @action(_('Save'), style='primary')
742    def save(self, **data):
743        form = self.request.form
744        password = form.get('change_password', None)
745        password_ctl = form.get('change_password_repeat', None)
746        if password:
747            validator = getUtility(IPasswordValidator)
748            errors = validator.validate_password(password, password_ctl)
749            if not errors:
750                IUserAccount(self.context).setPassword(password)
751                self.context.writeLogMessage(self, 'saved: password')
752                self.flash(_('Password changed.'))
753            else:
754                self.flash(' '.join(errors), type="warning")
755        return
756
757    @action(_('Cancel'), validator=NullValidator)
758    def cancel(self, **data):
759        self.redirect(self.url(self.context))
760
761
762class CustomerBasePDFFormPage(IkobaDisplayFormPage):
763    """ Page to display customer base data in pdf files.
764    """
765
766    def __init__(self, context, request, omit_fields=()):
767        self.omit_fields = omit_fields
768        super(CustomerBasePDFFormPage, self).__init__(context, request)
769
770    @property
771    def form_fields(self):
772        form_fields = grok.AutoFields(ICustomer)
773        for field in self.omit_fields:
774            form_fields = form_fields.omit(field)
775        return form_fields
776
777# Pages for customer documents
778
779class DocumentsBreadcrumb(Breadcrumb):
780    """A breadcrumb for the documents container.
781    """
782    grok.context(ICustomerDocumentsContainer)
783    title = _('Documents')
784
785
786class DocumentBreadcrumb(Breadcrumb):
787    """A breadcrumb for the customer container.
788    """
789    grok.context(ICustomerDocument)
790
791    @property
792    def title(self):
793        return "%s..." % self.context.document_id[:6]
794
795
796class DocumentsManageFormPage(IkobaEditFormPage):
797    """ Page to manage the customer documents
798
799    This manage form page is for both customers and customers officers.
800    """
801    grok.context(ICustomerDocumentsContainer)
802    grok.name('index')
803    grok.require('waeup.viewCustomer')
804    form_fields = grok.AutoFields(ICustomerDocumentsContainer)
805    grok.template('documentsmanagepage')
806    pnav = 4
807
808    @property
809    def manage_documents_allowed(self):
810        return checkPermission('waeup.editCustomerDocuments', self.context)
811
812    def unremovable(self, document):
813        usertype = getattr(self.request.principal, 'user_type', None)
814        if not usertype:
815            return False
816        if not self.manage_documents_allowed:
817            return True
818        return (self.request.principal.user_type == 'customer' and \
819            document.state in (VERIFIED, REJECTED, EXPIRED))
820
821    @property
822    def label(self):
823        return _('${a}: Documents',
824            mapping = {'a':self.context.__parent__.display_fullname})
825
826    @action(_('Add document'), validator=NullValidator, style='primary')
827    def addDocument(self, **data):
828        self.redirect(self.url(self.context, 'adddoc'))
829        return
830
831    @jsaction(_('Remove selected documents'))
832    def delDocument(self, **data):
833        form = self.request.form
834        if 'val_id' in form:
835            child_id = form['val_id']
836        else:
837            self.flash(_('No document selected.'), type="warning")
838            self.redirect(self.url(self.context))
839            return
840        if not isinstance(child_id, list):
841            child_id = [child_id]
842        deleted = []
843        for id in child_id:
844            # Customers are not allowed to remove used documents
845            document = self.context.get(id, None)
846            if document is not None and not self.unremovable(document):
847                del self.context[id]
848                deleted.append(id)
849        if len(deleted):
850            self.flash(_('Successfully removed: ${a}',
851                mapping = {'a': ', '.join(deleted)}))
852            self.context.writeLogMessage(
853                self,'removed: %s' % ', '.join(deleted))
854        self.redirect(self.url(self.context))
855        return
856
857
858class DocumentAddFormPage(IkobaAddFormPage):
859    """ Page to add a document
860
861    This add form page is for both customers and customers officers.
862    """
863    grok.context(ICustomerDocumentsContainer)
864    grok.name('adddoc')
865    grok.template('documentaddform')
866    grok.require('waeup.editCustomerDocuments')
867    label = _('Add document')
868    pnav = 4
869
870    form_fields = grok.AutoFields(ICustomerDocument).omit('document_id')
871
872    @property
873    def selectable_doctypes(self):
874        doctypes = getUtility(ICustomersUtils).SELECTABLE_DOCTYPES_DICT
875        return sorted(doctypes.items())
876
877    @action(_('Add document'), style='primary')
878    def createDocument(self, **data):
879        form = self.request.form
880        customer = self.context.__parent__
881        doctype = form.get('doctype', None)
882        # Here we can create various instances of CustomerDocument derived
883        # classes depending on the doctype parameter given in form.
884        document = createObject('waeup.%s' % doctype)
885        self.applyData(document, **data)
886        self.context.addDocument(document)
887        doctype = getUtility(ICustomersUtils).SELECTABLE_DOCTYPES_DICT[doctype]
888        self.flash(_('${a} added.', mapping = {'a': doctype}))
889        self.context.writeLogMessage(
890            self,'added: %s %s' % (doctype, document.document_id))
891        self.redirect(self.url(self.context))
892        return
893
894    @action(_('Cancel'), validator=NullValidator)
895    def cancel(self, **data):
896        self.redirect(self.url(self.context))
897
898
899class DocumentDisplayFormPage(IkobaDisplayFormPage):
900    """ Page to view a document
901    """
902    grok.context(ICustomerDocument)
903    grok.name('index')
904    grok.require('waeup.viewCustomer')
905    grok.template('documentpage')
906    pnav = 4
907    label = None  # We render the context title in the documentpage template
908
909    @property
910    def form_fields(self):
911        return grok.AutoFields(self.context.form_fields_interface)
912
913class DocumentManageFormPage(IkobaEditFormPage):
914    """ Page to edit a document
915    """
916    grok.context(ICustomerDocument)
917    grok.name('manage')
918    grok.require('waeup.manageCustomer')
919    grok.template('documenteditpage')
920    pnav = 4
921    deletion_warning = _('Are you sure?')
922
923    @property
924    def form_fields(self):
925        return grok.AutoFields(
926            self.context.form_fields_interface).omit('document_id')
927
928    def update(self):
929        if not self.context.is_editable_by_manager:
930            emit_lock_message(self)
931            return
932        return super(DocumentManageFormPage, self).update()
933
934    @property
935    def label(self):
936        return self.context.title
937
938    @action(_('Save'), style='primary')
939    def save(self, **data):
940        msave(self, **data)
941        return
942
943
944class DocumentEditFormPage(DocumentManageFormPage):
945    """ Page to edit a document
946    """
947    grok.name('edit')
948    grok.require('waeup.handleCustomer')
949
950    def update(self):
951        if not self.context.is_editable_by_customer:
952            emit_lock_message(self)
953            return
954        return super(DocumentEditFormPage, self).update()
955
956    @action(_('Save'), style='primary')
957    def save(self, **data):
958        msave(self, **data)
959        return
960
961    @action(_('Final Submit'), warning=WARNING_DOC)
962    def finalsubmit(self, **data):
963        msave(self, **data)
964        IWorkflowInfo(self.context).fireTransition('submit')
965        self.flash(_('Form has been submitted.'))
966        self.redirect(self.url(self.context))
967        return
968
969
970class DocumentTriggerTransitionFormPage(IkobaEditFormPage):
971    """ View to trigger customer document transitions
972    """
973    grok.context(ICustomerDocument)
974    grok.name('trigtrans')
975    grok.require('waeup.triggerTransition')
976    grok.template('trigtrans')
977    label = _('Trigger document transition')
978    pnav = 4
979
980    def update(self):
981        return super(IkobaEditFormPage, self).update()
982
983    def getTransitions(self):
984        """Return a list of dicts of allowed transition ids and titles.
985
986        Each list entry provides keys ``name`` and ``title`` for
987        internal name and (human readable) title of a single
988        transition.
989        """
990        wf_info = IWorkflowInfo(self.context)
991        allowed_transitions = [t for t in wf_info.getManualTransitions()]
992        return [dict(name='', title=_('No transition'))] +[
993            dict(name=x, title=y) for x, y in allowed_transitions]
994
995    @action(_('Apply'), style='primary')
996    def apply(self, **data):
997        form = self.request.form
998        if 'transition' in form and form['transition']:
999            transition_id = form['transition']
1000            wf_info = IWorkflowInfo(self.context)
1001            try:
1002                wf_info.fireTransition(transition_id)
1003                self.flash(_("Transition '%s' executed." % transition_id))
1004            except InvalidTransitionError, error:
1005                self.flash(error, type="warning")
1006            self.redirect(self.url(self.context))
1007        return
1008
1009class PDFDocumentsOverviewPage(UtilityView, grok.View):
1010    """Deliver an overview slip.
1011    """
1012    grok.context(ICustomerDocumentsContainer)
1013    grok.name('documents_overview_slip.pdf')
1014    grok.require('waeup.viewCustomer')
1015    prefix = 'form'
1016
1017    omit_fields = ('suspended', 'sex',
1018                   'suspended_comment',)
1019
1020    form_fields = None
1021
1022    @property
1023    def label(self):
1024        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
1025        return translate(_('Documents of'),
1026            'waeup.ikoba', target_language=portal_language) \
1027            + ' %s' % self.context.customer.display_fullname
1028
1029    @property
1030    def tabletitle(self):
1031        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
1032        tabletitle = []
1033        tabletitle.append(translate(_('Customer Documents'), 'waeup.ikoba',
1034            target_language=portal_language))
1035        return tabletitle
1036
1037    def render(self):
1038        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
1039        Id = translate(_('Id'), 'waeup.ikoba', target_language=portal_language)
1040        Title = translate(_('Title'), 'waeup.ikoba', target_language=portal_language)
1041        Type = translate(_('Type'), 'waeup.ikoba', target_language=portal_language)
1042        State = translate(_('State'), 'waeup.ikoba', target_language=portal_language)
1043        LT = translate(_('Last Transition'), 'waeup.ikoba', target_language=portal_language)
1044        tableheader = []
1045        tabledata = []
1046        contenttitle = []
1047        for i in range(1,3):
1048            tabledata.append(sorted(
1049                [value for value in self.context.values()]))
1050            tableheader.append([(Id, 'document_id', 2),
1051                             (Title, 'title', 6),
1052                             (Type, 'translated_class_name', 6),
1053                             (State, 'translated_state', 2),
1054                             (LT, 'formatted_transition_date', 3),
1055                             ])
1056        customerview = CustomerBasePDFFormPage(self.context.customer,
1057            self.request, self.omit_fields)
1058        customers_utils = getUtility(ICustomersUtils)
1059        return customers_utils.renderPDF(
1060            self, 'overview_slip.pdf',
1061            self.context.customer, customerview,
1062            tableheader=tableheader,
1063            tabledata=tabledata,
1064            omit_fields=self.omit_fields)
1065
1066
1067class PDFDocumentSlipPage(UtilityView, grok.View):
1068    """Deliver pdf file including metadata.
1069    """
1070    grok.context(ICustomerDocument)
1071    grok.name('document_slip.pdf')
1072    grok.require('waeup.viewCustomer')
1073    prefix = 'form'
1074
1075    omit_fields = ('suspended', 'sex',
1076                   'suspended_comment',)
1077
1078    form_fields =()
1079
1080    @property
1081    def label(self):
1082        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
1083        return '%s of %s\nTitle: %s' % (
1084            self.context.translated_class_name,
1085            self.context.customer.display_fullname,
1086            self.context.title)
1087
1088    def render(self):
1089        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
1090        customerview = CustomerBasePDFFormPage(self.context.customer,
1091            self.request, self.omit_fields)
1092        customers_utils = getUtility(ICustomersUtils)
1093        return customers_utils.renderPDF(
1094            self, 'document_slip.pdf',
1095            self.context.customer, customerview,
1096            omit_fields=self.omit_fields)
1097
1098
1099class PDFMergeDocumentSlipPage(PDFDocumentSlipPage):
1100    """Deliver pdf file including metadata.
1101    """
1102    grok.context(ICustomerPDFDocument)
1103
1104    def render(self):
1105        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
1106        customerview = CustomerBasePDFFormPage(self.context.customer,
1107            self.request, self.omit_fields)
1108        customers_utils = getUtility(ICustomersUtils)
1109        if self.context.state == VERIFIED:
1110            watermark_path = os.path.join(
1111                os.path.dirname(__file__), 'static', 'verified.pdf')
1112        else:
1113            watermark_path = os.path.join(
1114                os.path.dirname(__file__), 'static', 'unverified.pdf')
1115        watermark = open(watermark_path, 'rb')
1116        return customers_utils.renderPDF(
1117            self, 'pdfdocument_slip.pdf',
1118            self.context.customer, customerview,
1119            omit_fields=self.omit_fields,
1120            mergefiles=self.context.connected_files,
1121            watermark=watermark)
1122
1123# Pages for customer contracts
1124
1125class ContractsBreadcrumb(Breadcrumb):
1126    """A breadcrumb for the contracts container.
1127    """
1128    grok.context(IContractsContainer)
1129    title = _('Contracts')
1130
1131
1132class ContractBreadcrumb(Breadcrumb):
1133    """A breadcrumb for the customer container.
1134    """
1135    grok.context(IContract)
1136
1137    @property
1138    def title(self):
1139        return "%s..." % self.context.contract_id[:6]
1140
1141
1142class ContractsFormPage(IkobaEditFormPage):
1143    """ Page to display, edit or manage customer contracts
1144
1145    This form page is for both customers and officers.
1146    """
1147    grok.context(IContractsContainer)
1148    grok.name('index')
1149    grok.require('waeup.viewCustomer')
1150    form_fields = grok.AutoFields(IContractsContainer)
1151    grok.template('contractsmanagepage')
1152    pnav = 4
1153
1154    @property
1155    def edit_contracts_allowed(self):
1156        right_customer_state = self.context.customer.state in getUtility(
1157            ICustomersUtils).CONMANAGE_CUSTOMER_STATES
1158        if isCustomer(self.request.principal) and not right_customer_state:
1159            return False
1160        return checkPermission('waeup.editContracts', self.context)
1161
1162    def remove_contract_allowed(self, contract):
1163        if isCustomer(self.request.principal):
1164            right_customer_state = self.context.customer.state in getUtility(
1165                ICustomersUtils).CONMANAGE_CUSTOMER_STATES
1166            if not right_customer_state:
1167                return False
1168            if contract.state in (APPROVED, REJECTED, EXPIRED):
1169                return False
1170        return checkPermission('waeup.editContracts', self.context)
1171
1172    @property
1173    def label(self):
1174        return _('${a}: Contracts',
1175            mapping = {'a':self.context.__parent__.display_fullname})
1176
1177    @action(_('Add contract'), validator=NullValidator, style='primary')
1178    def addContract(self, **data):
1179        self.redirect(self.url(self.context, 'addcontract'))
1180        return
1181
1182    @jsaction(_('Remove selected contracts'))
1183    def delContract(self, **data):
1184        form = self.request.form
1185        if 'val_id' in form:
1186            child_id = form['val_id']
1187        else:
1188            self.flash(_('No contract selected.'), type="warning")
1189            self.redirect(self.url(self.context))
1190            return
1191        if not isinstance(child_id, list):
1192            child_id = [child_id]
1193        deleted = []
1194        for id in child_id:
1195            # Customers are not allowed to remove used contracts
1196            contract = self.context.get(id, None)
1197            if contract is not None and self.remove_contract_allowed(contract):
1198                del self.context[id]
1199                deleted.append(id)
1200        if len(deleted):
1201            self.flash(_('Successfully removed: ${a}',
1202                mapping = {'a': ', '.join(deleted)}))
1203            self.context.writeLogMessage(
1204                self,'removed: %s' % ', '.join(deleted))
1205        self.redirect(self.url(self.context))
1206        return
1207
1208
1209class ContractAddPage(IkobaAddFormPage):
1210    """ Page to add an contract
1211
1212    This page is for both customers and officers.
1213    """
1214    grok.context(IContractsContainer)
1215    grok.name('addcontract')
1216    grok.template('contractaddpage')
1217    grok.require('waeup.editContracts')
1218    label = _('Add contract')
1219    pnav = 4
1220
1221    form_fields = grok.AutoFields(IContract).omit(
1222        'product_object', 'contract_id', 'product_options')
1223
1224    @property
1225    def edit_contracts_allowed(self):
1226        right_customer_state = self.context.customer.state in getUtility(
1227            ICustomersUtils).CONMANAGE_CUSTOMER_STATES
1228        if isCustomer(self.request.principal) and not right_customer_state:
1229            return False
1230        return True
1231
1232    def update(self):
1233        if not self.edit_contracts_allowed:
1234            emit_lock_message(self)
1235            return
1236        return super(ContractAddPage, self).update()
1237
1238    @property
1239    def selectable_contypes(self):
1240        contypes = getUtility(ICustomersUtils).SELECTABLE_CONTYPES_DICT
1241        return sorted(contypes.items())
1242
1243    @action(_('Add contract'), style='primary')
1244    def createContract(self, **data):
1245        form = self.request.form
1246        customer = self.context.__parent__
1247        contype = form.get('contype', None)
1248        # Here we can create various instances of Contract derived
1249        # classes depending on the contype parameter given in form.
1250        contract = createObject('waeup.%s' % contype)
1251        self.applyData(contract, **data)
1252        self.context.addContract(contract)
1253        contype = getUtility(ICustomersUtils).SELECTABLE_CONTYPES_DICT[contype]
1254        self.flash(_('${a} added.', mapping = {'a': contype}))
1255        self.context.writeLogMessage(
1256            self,'added: %s %s' % (contype, contract.contract_id))
1257        self.redirect(self.url(contract, 'selectproduct'))
1258        return
1259
1260    @action(_('Cancel'), validator=NullValidator)
1261    def cancel(self, **data):
1262        self.redirect(self.url(self.context))
1263
1264
1265class ContractSelectProductPage(IkobaAddFormPage):
1266    """ Page to select a contract product
1267
1268    This page is for both customers and officers.
1269    """
1270    grok.context(IContract)
1271    grok.name('selectproduct')
1272    grok.require('waeup.editContracts')
1273    label = _('Select product')
1274    pnav = 4
1275
1276    form_fields = grok.AutoFields(IContractEdit).select('product_object')
1277
1278    def update(self):
1279        if not self.context.is_editable and self.context.product_options == []:
1280            emit_lock_message(self)
1281            return
1282        return super(ContractSelectProductPage, self).update()
1283
1284    @action(_('Save and proceed'), style='primary')
1285    def save(self, **data):
1286        msave(self, **data)
1287        isCustomer = getattr(
1288            self.request.principal, 'user_type', None) == 'customer'
1289        if isCustomer:
1290            self.redirect(self.url(self.context, 'edit'))
1291        else:
1292            self.redirect(self.url(self.context, 'manage'))
1293        return
1294
1295
1296class ContractDisplayFormPage(IkobaDisplayFormPage):
1297    """ Page to view a contract
1298    """
1299    grok.context(IContract)
1300    grok.name('index')
1301    grok.require('waeup.viewCustomer')
1302    grok.template('contractpage')
1303    pnav = 4
1304    label = None  # We render the context title in the contractpage template
1305
1306    @property
1307    def form_fields(self):
1308        form_fields = grok.AutoFields(self.context.form_fields_interface)
1309        for field in form_fields:
1310            if field.__name__.endswith('_object'):
1311                form_fields[field.__name__].custom_widget = HREFDisplayWidget
1312        return form_fields
1313
1314
1315class ContractManageFormPage(IkobaEditFormPage):
1316    """ Page to manage a contract
1317    """
1318    grok.context(IContract)
1319    grok.name('manage')
1320    grok.require('waeup.manageCustomer')
1321    grok.template('contracteditpage')
1322    pnav = 4
1323    deletion_warning = _('Are you sure?')
1324
1325    def update(self):
1326        if not self.context.is_editable:
1327            emit_lock_message(self)
1328            return
1329        return super(ContractManageFormPage, self).update()
1330
1331    @property
1332    def form_fields(self):
1333        return grok.AutoFields(self.context.form_fields_interface).omit(
1334            'contract_id')
1335
1336    @property
1337    def label(self):
1338        return self.context.title
1339
1340    @action(_('Save'), style='primary')
1341    def save(self, **data):
1342        msave(self, **data)
1343        return
1344
1345
1346class ContractEditFormPage(ContractManageFormPage):
1347    """ Page to edit a contract by customer only
1348    """
1349    grok.name('edit')
1350    grok.require('waeup.handleCustomer')
1351
1352    @property
1353    def form_fields(self):
1354        return grok.AutoFields(self.context.edit_form_fields_interface).omit(
1355            'contract_id', 'product_object')
1356
1357    @action(_('Save'), style='primary')
1358    def save(self, **data):
1359        msave(self, **data)
1360        return
1361
1362    @action(_('Apply now (final submit)'), warning=WARNING_CON)
1363    def finalsubmit(self, **data):
1364        msave(self, **data)
1365        IWorkflowInfo(self.context).fireTransition('submit')
1366        self.flash(_('Form has been submitted.'))
1367        self.redirect(self.url(self.context))
1368        return
1369
1370
1371class ContractTriggerTransitionFormPage(IkobaEditFormPage):
1372    """ View to trigger customer contract transitions
1373    """
1374    grok.context(IContract)
1375    grok.name('trigtrans')
1376    grok.require('waeup.triggerTransition')
1377    grok.template('trigtrans')
1378    label = _('Trigger contract transition')
1379    pnav = 4
1380
1381    def update(self):
1382        return super(IkobaEditFormPage, self).update()
1383
1384    def getTransitions(self):
1385        """Return a list of dicts of allowed transition ids and titles.
1386
1387        Each list entry provides keys ``name`` and ``title`` for
1388        internal name and (human readable) title of a single
1389        transition.
1390        """
1391        wf_info = IWorkflowInfo(self.context)
1392        allowed_transitions = [t for t in wf_info.getManualTransitions()]
1393        return [dict(name='', title=_('No transition'))] +[
1394            dict(name=x, title=y) for x, y in allowed_transitions]
1395
1396    @action(_('Apply'), style='primary')
1397    def apply(self, **data):
1398        form = self.request.form
1399        if 'transition' in form and form['transition']:
1400            transition_id = form['transition']
1401            wf_info = IWorkflowInfo(self.context)
1402            try:
1403                wf_info.fireTransition(transition_id)
1404                self.flash(_("Transition '%s' executed." % transition_id))
1405            except InvalidTransitionError, error:
1406                self.flash(error, type="warning")
1407            self.redirect(self.url(self.context))
1408        return
1409
1410class PDFContractsOverviewPage(UtilityView, grok.View):
1411    """Deliver an overview slip.
1412    """
1413    grok.context(IContractsContainer)
1414    grok.name('contracts_overview_slip.pdf')
1415    grok.require('waeup.viewCustomer')
1416    prefix = 'form'
1417
1418    omit_fields = ('suspended', 'sex',
1419                   'suspended_comment',)
1420
1421    form_fields = None
1422
1423    @property
1424    def label(self):
1425        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
1426        return translate(_('Contracts of'),
1427            'waeup.ikoba', target_language=portal_language) \
1428            + ' %s' % self.context.customer.display_fullname
1429
1430    @property
1431    def tabletitle(self):
1432        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
1433        tabletitle = []
1434        tabletitle.append(translate(_('Customer Contracts'), 'waeup.ikoba',
1435            target_language=portal_language))
1436        return tabletitle
1437
1438    def render(self):
1439        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
1440        #Id = translate(_('Id'), 'waeup.ikoba', target_language=portal_language)
1441        Title = translate(_('Title'), 'waeup.ikoba', target_language=portal_language)
1442        Type = translate(_('Type'), 'waeup.ikoba', target_language=portal_language)
1443        State = translate(_('State'), 'waeup.ikoba', target_language=portal_language)
1444        LT = translate(_('Last Transition'), 'waeup.ikoba', target_language=portal_language)
1445        tableheader = []
1446        tabledata = []
1447        contenttitle = []
1448        for i in range(1,3):
1449            tabledata.append(sorted(
1450                [value for value in self.context.values()]))
1451            tableheader.append([
1452                             #(Id, 'contract_id', 2),
1453                             (Title, 'title', 6),
1454                             (Type, 'translated_class_name', 6),
1455                             (State, 'translated_state', 2),
1456                             (LT, 'formatted_transition_date', 3),
1457                             ])
1458        customerview = CustomerBasePDFFormPage(self.context.customer,
1459            self.request, self.omit_fields)
1460        customers_utils = getUtility(ICustomersUtils)
1461        return customers_utils.renderPDF(
1462            self, 'overview_slip.pdf',
1463            self.context.customer, customerview,
1464            tableheader=tableheader,
1465            tabledata=tabledata,
1466            omit_fields=self.omit_fields)
1467
1468
1469class PDFContractSlipPage(UtilityView, grok.View):
1470    """Deliver pdf file including metadata.
1471    """
1472    grok.context(ISampleContract)
1473    grok.name('contract_slip.pdf')
1474    grok.require('waeup.viewCustomer')
1475    prefix = 'form'
1476
1477    omit_fields = ('suspended', 'sex',
1478                   'suspended_comment',)
1479
1480    form_fields = grok.AutoFields(ISampleContract)
1481
1482    @property
1483    def label(self):
1484        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
1485        return '%s of %s\nTitle: %s' % (
1486            self.context.translated_class_name,
1487            self.context.customer.display_fullname,
1488            self.context.title)
1489
1490    def render(self):
1491        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
1492        customerview = CustomerBasePDFFormPage(self.context.customer,
1493            self.request, self.omit_fields)
1494        customers_utils = getUtility(ICustomersUtils)
1495        return customers_utils.renderPDF(
1496            self, 'contract_slip.pdf',
1497            self.context.customer, customerview,
1498            omit_fields=self.omit_fields)
Note: See TracBrowser for help on using the repository browser.