source: main/waeup.ikoba/branches/uli-fake-gw-provider/src/waeup/ikoba/customers/browser.py @ 12643

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

Show validity period on contract pages and slips.

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