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

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

First batch of UI improvements.

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