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

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

Improve contract management. Tests will follow which show that customers can only do what they are allowed to do.

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