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

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

Add Cancel button.

  • Property svn:keywords set to Id
File size: 50.6 KB
Line 
1## $Id: browser.py 12348 2014-12-31 08:31:44Z 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.handleCustomer')
655    grok.template('filesuploadpage')
656    label = _('Change portrait')
657    pnav = 4
658
659    def update(self, CANCEL=None):
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        if CANCEL is not None:
666            self.redirect(self.url(self.context))
667            return
668        super(CustomerFilesUploadPage, self).update()
669        return
670
671# Pages for customers
672
673
674class CustomerBaseEditFormPage(IkobaEditFormPage):
675    """ View to edit customer base data
676    """
677    grok.context(ICustomer)
678    grok.name('edit_base')
679    grok.require('waeup.handleCustomer')
680    form_fields = grok.AutoFields(ICustomer).select(
681        'email', 'phone')
682    label = _('Edit base data')
683    pnav = 4
684
685    @action(_('Save'), style='primary')
686    def save(self, **data):
687        msave(self, **data)
688        return
689
690    @action(_('Cancel'), validator=NullValidator)
691    def cancel(self, **data):
692        self.redirect(self.url(self.context))
693
694
695class CustomerChangePasswordPage(IkobaEditFormPage):
696    """ View to edit customer passords
697    """
698    grok.context(ICustomer)
699    grok.name('changepassword')
700    grok.require('waeup.handleCustomer')
701    grok.template('changepassword')
702    label = _('Change password')
703    pnav = 4
704
705    @action(_('Save'), style='primary')
706    def save(self, **data):
707        form = self.request.form
708        password = form.get('change_password', None)
709        password_ctl = form.get('change_password_repeat', None)
710        if password:
711            validator = getUtility(IPasswordValidator)
712            errors = validator.validate_password(password, password_ctl)
713            if not errors:
714                IUserAccount(self.context).setPassword(password)
715                self.context.writeLogMessage(self, 'saved: password')
716                self.flash(_('Password changed.'))
717            else:
718                self.flash(' '.join(errors), type="warning")
719        return
720
721    @action(_('Cancel'), validator=NullValidator)
722    def cancel(self, **data):
723        self.redirect(self.url(self.context))
724
725
726class CustomerBasePDFFormPage(IkobaDisplayFormPage):
727    """ Page to display customer base data in pdf files.
728    """
729
730    def __init__(self, context, request, omit_fields=()):
731        self.omit_fields = omit_fields
732        super(CustomerBasePDFFormPage, self).__init__(context, request)
733
734    @property
735    def form_fields(self):
736        form_fields = grok.AutoFields(ICustomer)
737        for field in self.omit_fields:
738            form_fields = form_fields.omit(field)
739        return form_fields
740
741# Pages for customer documents
742
743class DocumentsBreadcrumb(Breadcrumb):
744    """A breadcrumb for the documents container.
745    """
746    grok.context(ICustomerDocumentsContainer)
747    title = _('Documents')
748
749
750class DocumentBreadcrumb(Breadcrumb):
751    """A breadcrumb for the customer container.
752    """
753    grok.context(ICustomerDocument)
754
755    @property
756    def title(self):
757        return self.context.document_id
758
759
760class DocumentsManageFormPage(IkobaEditFormPage):
761    """ Page to manage the customer documents
762
763    This manage form page is for both customers and customers officers.
764    """
765    grok.context(ICustomerDocumentsContainer)
766    grok.name('index')
767    grok.require('waeup.viewCustomer')
768    form_fields = grok.AutoFields(ICustomerDocumentsContainer)
769    grok.template('documentsmanagepage')
770    pnav = 4
771
772    @property
773    def manage_documents_allowed(self):
774        return checkPermission('waeup.editCustomerDocuments', self.context)
775
776    def unremovable(self, document):
777        usertype = getattr(self.request.principal, 'user_type', None)
778        if not usertype:
779            return False
780        if not self.manage_documents_allowed:
781            return True
782        return (self.request.principal.user_type == 'customer' and \
783            document.state in (VERIFIED, REJECTED, EXPIRED))
784
785    @property
786    def label(self):
787        return _('${a}: Documents',
788            mapping = {'a':self.context.__parent__.display_fullname})
789
790    @action(_('Add document'), validator=NullValidator, style='primary')
791    def addDocument(self, **data):
792        self.redirect(self.url(self.context, 'adddoc'))
793        return
794
795    @jsaction(_('Remove selected documents'))
796    def delDocument(self, **data):
797        form = self.request.form
798        if 'val_id' in form:
799            child_id = form['val_id']
800        else:
801            self.flash(_('No document selected.'), type="warning")
802            self.redirect(self.url(self.context))
803            return
804        if not isinstance(child_id, list):
805            child_id = [child_id]
806        deleted = []
807        for id in child_id:
808            # Customers are not allowed to remove used documents
809            document = self.context.get(id, None)
810            if document is not None and not self.unremovable(document):
811                del self.context[id]
812                deleted.append(id)
813        if len(deleted):
814            self.flash(_('Successfully removed: ${a}',
815                mapping = {'a': ', '.join(deleted)}))
816            self.context.writeLogMessage(
817                self,'removed: %s' % ', '.join(deleted))
818        self.redirect(self.url(self.context))
819        return
820
821
822class DocumentAddFormPage(IkobaAddFormPage):
823    """ Page to add a document
824
825    This add form page is for both customers and customers officers.
826    """
827    grok.context(ICustomerDocumentsContainer)
828    grok.name('adddoc')
829    grok.template('documentaddform')
830    grok.require('waeup.editCustomerDocuments')
831    label = _('Add document')
832    pnav = 4
833
834    form_fields = grok.AutoFields(ICustomerDocument).omit('document_id')
835
836    @property
837    def selectable_doctypes(self):
838        doctypes = getUtility(ICustomersUtils).SELECTABLE_DOCTYPES_DICT
839        return sorted(doctypes.items())
840
841    @action(_('Add document'), style='primary')
842    def createDocument(self, **data):
843        form = self.request.form
844        customer = self.context.__parent__
845        doctype = form.get('doctype', None)
846        # Here we can create various instances of CustomerDocument derived
847        # classes depending on the doctype parameter given in form.
848        document = createObject('waeup.%s' % doctype)
849        self.applyData(document, **data)
850        self.context.addDocument(document)
851        doctype = getUtility(ICustomersUtils).SELECTABLE_DOCTYPES_DICT[doctype]
852        self.flash(_('${a} added.', mapping = {'a': doctype}))
853        self.context.writeLogMessage(
854            self,'added: %s %s' % (doctype, document.document_id))
855        self.redirect(self.url(self.context))
856        return
857
858    @action(_('Cancel'), validator=NullValidator)
859    def cancel(self, **data):
860        self.redirect(self.url(self.context))
861
862
863class DocumentDisplayFormPage(IkobaDisplayFormPage):
864    """ Page to view a document
865    """
866    grok.context(ICustomerDocument)
867    grok.name('index')
868    grok.require('waeup.viewCustomer')
869    grok.template('documentpage')
870    pnav = 4
871    label = None  # We render the context title in the documentpage template
872
873    @property
874    def form_fields(self):
875        return grok.AutoFields(self.context.form_fields_interface)
876
877class DocumentManageFormPage(IkobaEditFormPage):
878    """ Page to edit a document
879    """
880    grok.context(ICustomerDocument)
881    grok.name('manage')
882    grok.require('waeup.manageCustomer')
883    grok.template('documenteditpage')
884    pnav = 4
885    deletion_warning = _('Are you sure?')
886
887    @property
888    def form_fields(self):
889        return grok.AutoFields(
890            self.context.form_fields_interface).omit('document_id')
891
892    def update(self):
893        if not self.context.is_editable_by_manager:
894            emit_lock_message(self)
895            return
896        return super(DocumentManageFormPage, self).update()
897
898    @property
899    def label(self):
900        return self.context.title
901
902    @action(_('Save'), style='primary')
903    def save(self, **data):
904        msave(self, **data)
905        return
906
907
908class DocumentEditFormPage(DocumentManageFormPage):
909    """ Page to edit a document
910    """
911    grok.name('edit')
912    grok.require('waeup.handleCustomer')
913
914    def update(self):
915        if not self.context.is_editable_by_customer:
916            emit_lock_message(self)
917            return
918        return super(DocumentEditFormPage, self).update()
919
920    @action(_('Save'), style='primary')
921    def save(self, **data):
922        msave(self, **data)
923        return
924
925    @action(_('Final Submit'), warning=WARNING)
926    def finalsubmit(self, **data):
927        msave(self, **data)
928        IWorkflowInfo(self.context).fireTransition('submit')
929        self.flash(_('Form has been submitted.'))
930        self.redirect(self.url(self.context))
931        return
932
933
934class DocumentTriggerTransitionFormPage(IkobaEditFormPage):
935    """ View to trigger customer document transitions
936    """
937    grok.context(ICustomerDocument)
938    grok.name('trigtrans')
939    grok.require('waeup.triggerTransition')
940    grok.template('trigtrans')
941    label = _('Trigger document transition')
942    pnav = 4
943
944    def update(self):
945        return super(IkobaEditFormPage, self).update()
946
947    def getTransitions(self):
948        """Return a list of dicts of allowed transition ids and titles.
949
950        Each list entry provides keys ``name`` and ``title`` for
951        internal name and (human readable) title of a single
952        transition.
953        """
954        wf_info = IWorkflowInfo(self.context)
955        allowed_transitions = [t for t in wf_info.getManualTransitions()]
956        return [dict(name='', title=_('No transition'))] +[
957            dict(name=x, title=y) for x, y in allowed_transitions]
958
959    @action(_('Apply now'), style='primary')
960    def apply(self, **data):
961        form = self.request.form
962        if 'transition' in form and form['transition']:
963            transition_id = form['transition']
964            wf_info = IWorkflowInfo(self.context)
965            try:
966                wf_info.fireTransition(transition_id)
967                self.flash(_("Transition '%s' executed." % transition_id))
968            except InvalidTransitionError, error:
969                self.flash(error, type="warning")
970            self.redirect(self.url(self.context))
971        return
972
973class PDFDocumentsOverviewPage(UtilityView, grok.View):
974    """Deliver an overview slip.
975    """
976    grok.context(ICustomerDocumentsContainer)
977    grok.name('documents_overview_slip.pdf')
978    grok.require('waeup.viewCustomer')
979    prefix = 'form'
980
981    omit_fields = ('suspended', 'sex',
982                   'suspended_comment',)
983
984    form_fields = None
985
986    @property
987    def label(self):
988        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
989        return translate(_('Documents of'),
990            'waeup.ikoba', target_language=portal_language) \
991            + ' %s' % self.context.customer.display_fullname
992
993    @property
994    def tabletitle(self):
995        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
996        tabletitle = []
997        tabletitle.append(translate(_('Customer Documents'), 'waeup.ikoba',
998            target_language=portal_language))
999        return tabletitle
1000
1001    def render(self):
1002        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
1003        Id = translate(_('Id'), 'waeup.ikoba', target_language=portal_language)
1004        Title = translate(_('Title'), 'waeup.ikoba', target_language=portal_language)
1005        Type = translate(_('Type'), 'waeup.ikoba', target_language=portal_language)
1006        State = translate(_('State'), 'waeup.ikoba', target_language=portal_language)
1007        LT = translate(_('Last Transition'), 'waeup.ikoba', target_language=portal_language)
1008        tableheader = []
1009        tabledata = []
1010        contenttitle = []
1011        for i in range(1,3):
1012            tabledata.append(sorted(
1013                [value for value in self.context.values()]))
1014            tableheader.append([(Id, 'document_id', 2),
1015                             (Title, 'title', 6),
1016                             (Type, 'translated_class_name', 6),
1017                             (State, 'translated_state', 2),
1018                             (LT, 'formatted_transition_date', 3),
1019                             ])
1020        customerview = CustomerBasePDFFormPage(self.context.customer,
1021            self.request, self.omit_fields)
1022        customers_utils = getUtility(ICustomersUtils)
1023        return customers_utils.renderPDF(
1024            self, 'overview_slip.pdf',
1025            self.context.customer, customerview,
1026            tableheader=tableheader,
1027            tabledata=tabledata,
1028            omit_fields=self.omit_fields)
1029
1030
1031class PDFDocumentSlipPage(UtilityView, grok.View):
1032    """Deliver pdf file including metadata.
1033    """
1034    grok.context(ICustomerDocument)
1035    grok.name('document_slip.pdf')
1036    grok.require('waeup.viewCustomer')
1037    prefix = 'form'
1038
1039    omit_fields = ('suspended', 'sex',
1040                   'suspended_comment',)
1041
1042    form_fields =()
1043
1044    @property
1045    def label(self):
1046        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
1047        return '%s of %s\nTitle: %s' % (
1048            self.context.translated_class_name,
1049            self.context.customer.display_fullname,
1050            self.context.title)
1051
1052    def render(self):
1053        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
1054        customerview = CustomerBasePDFFormPage(self.context.customer,
1055            self.request, self.omit_fields)
1056        customers_utils = getUtility(ICustomersUtils)
1057        return customers_utils.renderPDF(
1058            self, 'document_slip.pdf',
1059            self.context.customer, customerview,
1060            omit_fields=self.omit_fields)
1061
1062
1063class PDFMergeDocumentSlipPage(PDFDocumentSlipPage):
1064    """Deliver pdf file including metadata.
1065    """
1066    grok.context(ICustomerPDFDocument)
1067
1068    def render(self):
1069        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
1070        customerview = CustomerBasePDFFormPage(self.context.customer,
1071            self.request, self.omit_fields)
1072        customers_utils = getUtility(ICustomersUtils)
1073        if self.context.state == VERIFIED:
1074            watermark_path = os.path.join(
1075                os.path.dirname(__file__), 'static', 'verified.pdf')
1076        else:
1077            watermark_path = os.path.join(
1078                os.path.dirname(__file__), 'static', 'unverified.pdf')
1079        watermark = open(watermark_path, 'rb')
1080        return customers_utils.renderPDF(
1081            self, 'pdfdocument_slip.pdf',
1082            self.context.customer, customerview,
1083            omit_fields=self.omit_fields,
1084            mergefiles=self.context.connected_files,
1085            watermark=watermark)
1086
1087# Pages for customer contracts
1088
1089class ContractsBreadcrumb(Breadcrumb):
1090    """A breadcrumb for the contracts container.
1091    """
1092    grok.context(IContractsContainer)
1093    title = _('Contracts')
1094
1095
1096class ContractBreadcrumb(Breadcrumb):
1097    """A breadcrumb for the customer container.
1098    """
1099    grok.context(IContract)
1100
1101    @property
1102    def title(self):
1103        return self.context.contract_id
1104
1105
1106class ContractsFormPage(IkobaEditFormPage):
1107    """ Page to display, edit or manage customer contracts
1108
1109    This form page is for both customers and officers.
1110    """
1111    grok.context(IContractsContainer)
1112    grok.name('index')
1113    grok.require('waeup.viewCustomer')
1114    form_fields = grok.AutoFields(IContractsContainer)
1115    grok.template('contractsmanagepage')
1116    pnav = 4
1117
1118    @property
1119    def edit_contracts_allowed(self):
1120        right_customer_state = self.context.customer.state in getUtility(
1121            ICustomersUtils).CONMANAGE_CUSTOMER_STATES
1122        if isCustomer(self.request.principal) and not right_customer_state:
1123            return False
1124        return checkPermission('waeup.editContracts', self.context)
1125
1126    def remove_contract_allowed(self, contract):
1127        if isCustomer(self.request.principal):
1128            right_customer_state = self.context.customer.state in getUtility(
1129                ICustomersUtils).CONMANAGE_CUSTOMER_STATES
1130            if not right_customer_state:
1131                return False
1132            if contract.state in (APPROVED, REJECTED, EXPIRED):
1133                return False
1134        return checkPermission('waeup.editContracts', self.context)
1135
1136    @property
1137    def label(self):
1138        return _('${a}: Contracts',
1139            mapping = {'a':self.context.__parent__.display_fullname})
1140
1141    @action(_('Add contract'), validator=NullValidator, style='primary')
1142    def addContract(self, **data):
1143        self.redirect(self.url(self.context, 'addcontract'))
1144        return
1145
1146    @jsaction(_('Remove selected contracts'))
1147    def delContract(self, **data):
1148        form = self.request.form
1149        if 'val_id' in form:
1150            child_id = form['val_id']
1151        else:
1152            self.flash(_('No contract selected.'), type="warning")
1153            self.redirect(self.url(self.context))
1154            return
1155        if not isinstance(child_id, list):
1156            child_id = [child_id]
1157        deleted = []
1158        for id in child_id:
1159            # Customers are not allowed to remove used contracts
1160            contract = self.context.get(id, None)
1161            if contract is not None and self.remove_contract_allowed(contract):
1162                del self.context[id]
1163                deleted.append(id)
1164        if len(deleted):
1165            self.flash(_('Successfully removed: ${a}',
1166                mapping = {'a': ', '.join(deleted)}))
1167            self.context.writeLogMessage(
1168                self,'removed: %s' % ', '.join(deleted))
1169        self.redirect(self.url(self.context))
1170        return
1171
1172
1173class ContractAddPage(IkobaAddFormPage):
1174    """ Page to add an contract
1175
1176    This page is for both customers and officers.
1177    """
1178    grok.context(IContractsContainer)
1179    grok.name('addcontract')
1180    grok.template('contractaddpage')
1181    grok.require('waeup.editContracts')
1182    label = _('Add contract')
1183    pnav = 4
1184
1185    form_fields = grok.AutoFields(IContract).omit(
1186        'product_object', 'contract_id', 'product_options')
1187
1188    @property
1189    def edit_contracts_allowed(self):
1190        right_customer_state = self.context.customer.state in getUtility(
1191            ICustomersUtils).CONMANAGE_CUSTOMER_STATES
1192        if isCustomer(self.request.principal) and not right_customer_state:
1193            return False
1194        return True
1195
1196    def update(self):
1197        if not self.edit_contracts_allowed:
1198            emit_lock_message(self)
1199            return
1200        return super(ContractAddPage, self).update()
1201
1202    @property
1203    def selectable_contypes(self):
1204        contypes = getUtility(ICustomersUtils).SELECTABLE_CONTYPES_DICT
1205        return sorted(contypes.items())
1206
1207    @action(_('Add contract'), style='primary')
1208    def createContract(self, **data):
1209        form = self.request.form
1210        customer = self.context.__parent__
1211        contype = form.get('contype', None)
1212        # Here we can create various instances of Contract derived
1213        # classes depending on the contype parameter given in form.
1214        contract = createObject('waeup.%s' % contype)
1215        self.applyData(contract, **data)
1216        self.context.addContract(contract)
1217        contype = getUtility(ICustomersUtils).SELECTABLE_CONTYPES_DICT[contype]
1218        self.flash(_('${a} added.', mapping = {'a': contype}))
1219        self.context.writeLogMessage(
1220            self,'added: %s %s' % (contype, contract.contract_id))
1221        self.redirect(self.url(contract, 'selectproduct'))
1222        return
1223
1224    @action(_('Cancel'), validator=NullValidator)
1225    def cancel(self, **data):
1226        self.redirect(self.url(self.context))
1227
1228
1229class ContractSelectProductPage(IkobaAddFormPage):
1230    """ Page to select a contract product
1231
1232    This page is for both customers and officers.
1233    """
1234    grok.context(IContract)
1235    grok.name('selectproduct')
1236    grok.require('waeup.editContracts')
1237    label = _('Select product')
1238    pnav = 4
1239
1240    form_fields = grok.AutoFields(IContractEdit).select('product_object')
1241
1242    def update(self):
1243        if not self.context.is_editable and self.context.product_options == []:
1244            emit_lock_message(self)
1245            return
1246        return super(ContractSelectProductPage, self).update()
1247
1248    @action(_('Save and proceed'), style='primary')
1249    def save(self, **data):
1250        msave(self, **data)
1251        isCustomer = getattr(
1252            self.request.principal, 'user_type', None) == 'customer'
1253        if isCustomer:
1254            self.redirect(self.url(self.context, 'edit'))
1255        else:
1256            self.redirect(self.url(self.context, 'manage'))
1257        return
1258
1259
1260class ContractDisplayFormPage(IkobaDisplayFormPage):
1261    """ Page to view a contract
1262    """
1263    grok.context(IContract)
1264    grok.name('index')
1265    grok.require('waeup.viewCustomer')
1266    grok.template('contractpage')
1267    pnav = 4
1268    label = None  # We render the context title in the contractpage template
1269
1270    @property
1271    def form_fields(self):
1272        form_fields = grok.AutoFields(self.context.form_fields_interface)
1273        for field in form_fields:
1274            if field.__name__.endswith('_object'):
1275                form_fields[field.__name__].custom_widget = HREFDisplayWidget
1276        return form_fields
1277
1278
1279class ContractManageFormPage(IkobaEditFormPage):
1280    """ Page to manage a contract
1281    """
1282    grok.context(IContract)
1283    grok.name('manage')
1284    grok.require('waeup.manageCustomer')
1285    grok.template('contracteditpage')
1286    pnav = 4
1287    deletion_warning = _('Are you sure?')
1288
1289    def update(self):
1290        if not self.context.is_editable:
1291            emit_lock_message(self)
1292            return
1293        return super(ContractManageFormPage, self).update()
1294
1295    @property
1296    def form_fields(self):
1297        return grok.AutoFields(self.context.form_fields_interface).omit(
1298            'contract_id')
1299
1300    @property
1301    def label(self):
1302        return self.context.title
1303
1304    @action(_('Save'), style='primary')
1305    def save(self, **data):
1306        msave(self, **data)
1307        return
1308
1309    @action(_('Final Submit'), warning=WARNING)
1310    def finalsubmit(self, **data):
1311        msave(self, **data)
1312        IWorkflowInfo(self.context).fireTransition('submit')
1313        self.flash(_('Form has been submitted.'))
1314        self.redirect(self.url(self.context))
1315        return
1316
1317
1318class ContractEditFormPage(ContractManageFormPage):
1319    """ Page to edit a contract by customer only
1320    """
1321    grok.name('edit')
1322    grok.require('waeup.handleCustomer')
1323
1324    @property
1325    def form_fields(self):
1326        return grok.AutoFields(self.context.edit_form_fields_interface).omit(
1327            'contract_id', 'product_object')
1328
1329    @action(_('Save'), style='primary')
1330    def save(self, **data):
1331        msave(self, **data)
1332        return
1333
1334    @action(_('Apply now (final submit)'), warning=WARNING)
1335    def finalsubmit(self, **data):
1336        msave(self, **data)
1337        IWorkflowInfo(self.context).fireTransition('submit')
1338        self.flash(_('Form has been submitted.'))
1339        self.redirect(self.url(self.context))
1340        return
1341
1342
1343class ContractTriggerTransitionFormPage(IkobaEditFormPage):
1344    """ View to trigger customer contract transitions
1345    """
1346    grok.context(IContract)
1347    grok.name('trigtrans')
1348    grok.require('waeup.triggerTransition')
1349    grok.template('trigtrans')
1350    label = _('Trigger contract transition')
1351    pnav = 4
1352
1353    def update(self):
1354        return super(IkobaEditFormPage, self).update()
1355
1356    def getTransitions(self):
1357        """Return a list of dicts of allowed transition ids and titles.
1358
1359        Each list entry provides keys ``name`` and ``title`` for
1360        internal name and (human readable) title of a single
1361        transition.
1362        """
1363        wf_info = IWorkflowInfo(self.context)
1364        allowed_transitions = [t for t in wf_info.getManualTransitions()]
1365        return [dict(name='', title=_('No transition'))] +[
1366            dict(name=x, title=y) for x, y in allowed_transitions]
1367
1368    @action(_('Apply now'), style='primary')
1369    def apply(self, **data):
1370        form = self.request.form
1371        if 'transition' in form and form['transition']:
1372            transition_id = form['transition']
1373            wf_info = IWorkflowInfo(self.context)
1374            try:
1375                wf_info.fireTransition(transition_id)
1376                self.flash(_("Transition '%s' executed." % transition_id))
1377            except InvalidTransitionError, error:
1378                self.flash(error, type="warning")
1379            self.redirect(self.url(self.context))
1380        return
1381
1382class PDFContractsOverviewPage(UtilityView, grok.View):
1383    """Deliver an overview slip.
1384    """
1385    grok.context(IContractsContainer)
1386    grok.name('contracts_overview_slip.pdf')
1387    grok.require('waeup.viewCustomer')
1388    prefix = 'form'
1389
1390    omit_fields = ('suspended', 'sex',
1391                   'suspended_comment',)
1392
1393    form_fields = None
1394
1395    @property
1396    def label(self):
1397        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
1398        return translate(_('Contracts of'),
1399            'waeup.ikoba', target_language=portal_language) \
1400            + ' %s' % self.context.customer.display_fullname
1401
1402    @property
1403    def tabletitle(self):
1404        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
1405        tabletitle = []
1406        tabletitle.append(translate(_('Customer Contracts'), 'waeup.ikoba',
1407            target_language=portal_language))
1408        return tabletitle
1409
1410    def render(self):
1411        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
1412        #Id = translate(_('Id'), 'waeup.ikoba', target_language=portal_language)
1413        Title = translate(_('Title'), 'waeup.ikoba', target_language=portal_language)
1414        Type = translate(_('Type'), 'waeup.ikoba', target_language=portal_language)
1415        State = translate(_('State'), 'waeup.ikoba', target_language=portal_language)
1416        LT = translate(_('Last Transition'), 'waeup.ikoba', target_language=portal_language)
1417        tableheader = []
1418        tabledata = []
1419        contenttitle = []
1420        for i in range(1,3):
1421            tabledata.append(sorted(
1422                [value for value in self.context.values()]))
1423            tableheader.append([
1424                             #(Id, 'contract_id', 2),
1425                             (Title, 'title', 6),
1426                             (Type, 'translated_class_name', 6),
1427                             (State, 'translated_state', 2),
1428                             (LT, 'formatted_transition_date', 3),
1429                             ])
1430        customerview = CustomerBasePDFFormPage(self.context.customer,
1431            self.request, self.omit_fields)
1432        customers_utils = getUtility(ICustomersUtils)
1433        return customers_utils.renderPDF(
1434            self, 'overview_slip.pdf',
1435            self.context.customer, customerview,
1436            tableheader=tableheader,
1437            tabledata=tabledata,
1438            omit_fields=self.omit_fields)
1439
1440
1441class PDFContractSlipPage(UtilityView, grok.View):
1442    """Deliver pdf file including metadata.
1443    """
1444    grok.context(ISampleContract)
1445    grok.name('contract_slip.pdf')
1446    grok.require('waeup.viewCustomer')
1447    prefix = 'form'
1448
1449    omit_fields = ('suspended', 'sex',
1450                   'suspended_comment',)
1451
1452    form_fields = grok.AutoFields(ISampleContract)
1453
1454    @property
1455    def label(self):
1456        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
1457        return '%s of %s\nTitle: %s' % (
1458            self.context.translated_class_name,
1459            self.context.customer.display_fullname,
1460            self.context.title)
1461
1462    def render(self):
1463        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
1464        customerview = CustomerBasePDFFormPage(self.context.customer,
1465            self.request, self.omit_fields)
1466        customers_utils = getUtility(ICustomersUtils)
1467        return customers_utils.renderPDF(
1468            self, 'contract_slip.pdf',
1469            self.context.customer, customerview,
1470            omit_fields=self.omit_fields)
Note: See TracBrowser for help on using the repository browser.