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

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

Some modifications to enable customization.

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