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

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

Fire transition when customer logs in for the first time.

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