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

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

Title must be stored with the contract. Otherwise no title will be displayed if product is removed.

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