Ignore:
Timestamp:
29 Nov 2014, 07:57:51 (10 years ago)
Author:
Henrik Bettermann
Message:

Add application browser components.

Location:
main/waeup.ikoba/trunk/src/waeup/ikoba/customers
Files:
4 added
11 edited

Legend:

Unmodified
Added
Removed
  • main/waeup.ikoba/trunk/src/waeup/ikoba/customers/applications.py

    r12089 r12090  
    129129    def translated_class_name(self):
    130130        try:
    131             DOCTYPES_DICT = getUtility(ICustomersUtils).DOCTYPES_DICT
    132             return DOCTYPES_DICT[self.class_name]
     131            APPTYPES_DICT = getUtility(ICustomersUtils).APPTYPES_DICT
     132            return APPTYPES_DICT[self.class_name]
    133133        except KeyError:
    134134            return
     
    157157        return implementedBy(SampleApplication)
    158158
     159@grok.subscribe(IApplication, grok.IObjectAddedEvent)
     160def handle_document_added(application, event):
     161    """If an application is added the transition create is fired.
     162    The latter produces a logging message.
     163    """
     164    if IWorkflowState(application).getState() is None:
     165        IWorkflowInfo(application).fireTransition('create')
     166    return
  • main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser.py

    r12089 r12090  
    5151    ICustomer, ICustomersContainer, ICustomerRequestPW, ICustomersUtils,
    5252    ICustomerDocument, ICustomerDocumentsContainer, ICustomerCreate,
    53     ICustomerPDFDocument
     53    ICustomerPDFDocument, IApplicationsContainer, IApplication
    5454    )
    5555from waeup.ikoba.customers.catalog import search
     
    10241024            self.context.customer, customerview,
    10251025            omit_fields=self.omit_fields)
     1026
     1027# Pages for customer applications
     1028
     1029class ApplicationsBreadcrumb(Breadcrumb):
     1030    """A breadcrumb for the applications container.
     1031    """
     1032    grok.context(IApplicationsContainer)
     1033    title = _('Applications')
     1034
     1035
     1036class ApplicationBreadcrumb(Breadcrumb):
     1037    """A breadcrumb for the customer container.
     1038    """
     1039    grok.context(IApplication)
     1040
     1041    @property
     1042    def title(self):
     1043        return self.context.application_id
     1044
     1045
     1046class ApplicationsManageFormPage(IkobaEditFormPage):
     1047    """ Page to manage the customer applications
     1048
     1049    This manage form page is for both customers and customers officers.
     1050    """
     1051    grok.context(IApplicationsContainer)
     1052    grok.name('index')
     1053    grok.require('waeup.viewCustomer')
     1054    form_fields = grok.AutoFields(IApplicationsContainer)
     1055    grok.template('applicationsmanagepage')
     1056    pnav = 4
     1057
     1058    @property
     1059    def manage_applications_allowed(self):
     1060        return checkPermission('waeup.editApplications', self.context)
     1061
     1062    def unremovable(self, application):
     1063        usertype = getattr(self.request.principal, 'user_type', None)
     1064        if not usertype:
     1065            return False
     1066        if not self.manage_applications_allowed:
     1067            return True
     1068        return (self.request.principal.user_type == 'customer' and \
     1069            application.state in (APPROVED, REJECTED, EXPIRED))
     1070
     1071    @property
     1072    def label(self):
     1073        return _('${a}: Applications',
     1074            mapping = {'a':self.context.__parent__.display_fullname})
     1075
     1076    @jsaction(_('Remove selected applications'))
     1077    def delApplication(self, **data):
     1078        form = self.request.form
     1079        if 'val_id' in form:
     1080            child_id = form['val_id']
     1081        else:
     1082            self.flash(_('No application selected.'), type="warning")
     1083            self.redirect(self.url(self.context))
     1084            return
     1085        if not isinstance(child_id, list):
     1086            child_id = [child_id]
     1087        deleted = []
     1088        for id in child_id:
     1089            # Customers are not allowed to remove used applications
     1090            application = self.context.get(id, None)
     1091            if application is not None and not self.unremovable(application):
     1092                del self.context[id]
     1093                deleted.append(id)
     1094        if len(deleted):
     1095            self.flash(_('Successfully removed: ${a}',
     1096                mapping = {'a': ', '.join(deleted)}))
     1097            self.context.writeLogMessage(
     1098                self,'removed: %s' % ', '.join(deleted))
     1099        self.redirect(self.url(self.context))
     1100        return
     1101
     1102
     1103class ApplicationAddFormPage(IkobaAddFormPage):
     1104    """ Page to add an application
     1105    """
     1106    grok.context(IApplicationsContainer)
     1107    grok.name('addapp')
     1108    grok.template('applicationaddform')
     1109    grok.require('waeup.editApplications')
     1110    form_fields = grok.AutoFields(IApplication)
     1111    label = _('Add application')
     1112    pnav = 4
     1113
     1114    @property
     1115    def selectable_apptypes(self):
     1116        apptypes = getUtility(ICustomersUtils).SELECTABLE_APPTYPES_DICT
     1117        return sorted(apptypes.items())
     1118
     1119    @action(_('Create application'), style='primary')
     1120    def createApplication(self, **data):
     1121        form = self.request.form
     1122        customer = self.context.__parent__
     1123        apptype = form.get('apptype', None)
     1124        # Here we can create various instances of Application derived
     1125        # classes depending on the apptype parameter given in form.
     1126        application = createObject('waeup.%s' % apptype)
     1127        self.context.addApplication(application)
     1128        apptype = getUtility(ICustomersUtils).SELECTABLE_APPTYPES_DICT[apptype]
     1129        self.flash(_('${a} created.',
     1130            mapping = {'a': apptype}))
     1131        self.context.writeLogMessage(
     1132            self,'added: %s %s' % (apptype, application.application_id))
     1133        self.redirect(self.url(self.context))
     1134        return
     1135
     1136    @action(_('Cancel'), validator=NullValidator)
     1137    def cancel(self, **data):
     1138        self.redirect(self.url(self.context))
     1139
     1140
     1141class ApplicationDisplayFormPage(IkobaDisplayFormPage):
     1142    """ Page to view a application
     1143    """
     1144    grok.context(IApplication)
     1145    grok.name('index')
     1146    grok.require('waeup.viewCustomer')
     1147    grok.template('applicationpage')
     1148    form_fields = grok.AutoFields(IApplication).omit('last_transition_date')
     1149    pnav = 4
     1150
     1151    #@property
     1152    #def label(self):
     1153    #    return _('${a}: Application ${b}', mapping = {
     1154    #        'a':self.context.customer.display_fullname,
     1155    #        'b':self.context.application_id})
     1156
     1157    @property
     1158    def label(self):
     1159        return _('${a}', mapping = {'a':self.context.title})
     1160
     1161
     1162class ApplicationManageFormPage(IkobaEditFormPage):
     1163    """ Page to edit a application
     1164    """
     1165    grok.context(IApplication)
     1166    grok.name('manage')
     1167    grok.require('waeup.manageCustomer')
     1168    grok.template('applicationeditpage')
     1169    form_fields = grok.AutoFields(IApplication).omit('last_transition_date')
     1170    pnav = 4
     1171    deletion_warning = _('Are you sure?')
     1172
     1173    #@property
     1174    #def label(self):
     1175    #    return _('${a}: Application ${b}', mapping = {
     1176    #        'a':self.context.customer.display_fullname,
     1177    #        'b':self.context.application_id})
     1178
     1179    @property
     1180    def label(self):
     1181        return _('${a}', mapping = {'a':self.context.title})
     1182
     1183    @action(_('Save'), style='primary')
     1184    def save(self, **data):
     1185        msave(self, **data)
     1186        return
     1187
     1188
     1189class ApplicationEditFormPage(ApplicationManageFormPage):
     1190    """ Page to edit a application
     1191    """
     1192    grok.name('edit')
     1193    grok.require('waeup.handleCustomer')
     1194
     1195    def update(self):
     1196        if not self.context.is_editable:
     1197            emit_lock_message(self)
     1198            return
     1199        return super(ApplicationEditFormPage, self).update()
     1200
     1201    @action(_('Save'), style='primary')
     1202    def save(self, **data):
     1203        msave(self, **data)
     1204        return
     1205
     1206    @action(_('Final Submit'), warning=WARNING)
     1207    def finalsubmit(self, **data):
     1208        msave(self, **data)
     1209        IWorkflowInfo(self.context).fireTransition('submit')
     1210        self.flash(_('Form has been submitted.'))
     1211        self.redirect(self.url(self.context))
     1212        return
     1213
     1214
     1215class ApplicationTriggerTransitionFormPage(IkobaEditFormPage):
     1216    """ View to trigger customer application transitions
     1217    """
     1218    grok.context(IApplication)
     1219    grok.name('trigtrans')
     1220    grok.require('waeup.triggerTransition')
     1221    grok.template('trigtrans')
     1222    label = _('Trigger application transition')
     1223    pnav = 4
     1224
     1225    def update(self):
     1226        return super(IkobaEditFormPage, self).update()
     1227
     1228    def getTransitions(self):
     1229        """Return a list of dicts of allowed transition ids and titles.
     1230
     1231        Each list entry provides keys ``name`` and ``title`` for
     1232        internal name and (human readable) title of a single
     1233        transition.
     1234        """
     1235        wf_info = IWorkflowInfo(self.context)
     1236        allowed_transitions = [t for t in wf_info.getManualTransitions()]
     1237        return [dict(name='', title=_('No transition'))] +[
     1238            dict(name=x, title=y) for x, y in allowed_transitions]
     1239
     1240    @action(_('Save'), style='primary')
     1241    def save(self, **data):
     1242        form = self.request.form
     1243        if 'transition' in form and form['transition']:
     1244            transition_id = form['transition']
     1245            wf_info = IWorkflowInfo(self.context)
     1246            wf_info.fireTransition(transition_id)
     1247        return
     1248
     1249class PDFApplicationsOverviewPage(UtilityView, grok.View):
     1250    """Deliver an overview slip.
     1251    """
     1252    grok.context(IApplicationsContainer)
     1253    grok.name('overview_slip.pdf')
     1254    grok.require('waeup.viewCustomer')
     1255    prefix = 'form'
     1256
     1257    omit_fields = ('suspended', 'sex',
     1258                   'suspended_comment',)
     1259
     1260    form_fields = None
     1261
     1262    @property
     1263    def label(self):
     1264        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
     1265        return translate(_('Applications of'),
     1266            'waeup.ikoba', target_language=portal_language) \
     1267            + ' %s' % self.context.customer.display_fullname
     1268
     1269    @property
     1270    def tabletitle(self):
     1271        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
     1272        tabletitle = []
     1273        tabletitle.append(translate(_('Customer Applications'), 'waeup.ikoba',
     1274            target_language=portal_language))
     1275        return tabletitle
     1276
     1277    def render(self):
     1278        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
     1279        Id = translate(_('Id'), 'waeup.ikoba', target_language=portal_language)
     1280        Title = translate(_('Title'), 'waeup.ikoba', target_language=portal_language)
     1281        Type = translate(_('Type'), 'waeup.ikoba', target_language=portal_language)
     1282        State = translate(_('State'), 'waeup.ikoba', target_language=portal_language)
     1283        LT = translate(_('Last Transition'), 'waeup.ikoba', target_language=portal_language)
     1284        tableheader = []
     1285        tabledata = []
     1286        contenttitle = []
     1287        for i in range(1,3):
     1288            tabledata.append(sorted(
     1289                [value for value in self.context.values()]))
     1290            tableheader.append([(Id, 'application_id', 2),
     1291                             (Title, 'title', 6),
     1292                             (Type, 'translated_class_name', 6),
     1293                             (State, 'translated_state', 2),
     1294                             (LT, 'formatted_transition_date', 3),
     1295                             ])
     1296        customerview = CustomerBasePDFFormPage(self.context.customer,
     1297            self.request, self.omit_fields)
     1298        customers_utils = getUtility(ICustomersUtils)
     1299        return customers_utils.renderPDF(
     1300            self, 'overview_slip.pdf',
     1301            self.context.customer, customerview,
     1302            tableheader=tableheader,
     1303            tabledata=tabledata,
     1304            omit_fields=self.omit_fields)
     1305
     1306
     1307class PDFApplicationSlipPage(UtilityView, grok.View):
     1308    """Deliver pdf file including metadata.
     1309    """
     1310    grok.context(IApplication)
     1311    grok.name('application_slip.pdf')
     1312    grok.require('waeup.viewCustomer')
     1313    prefix = 'form'
     1314
     1315    omit_fields = ('suspended', 'sex',
     1316                   'suspended_comment',)
     1317
     1318    #form_fields = grok.AutoFields(ICustomerPDFApplication).omit(
     1319    #    'last_transition_date')
     1320    form_fields =()
     1321
     1322    @property
     1323    def label(self):
     1324        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
     1325        return '%s of %s\nTitle: %s' % (
     1326            self.context.translated_class_name,
     1327            self.context.customer.display_fullname,
     1328            self.context.title)
     1329
     1330    def render(self):
     1331        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
     1332        customerview = CustomerBasePDFFormPage(self.context.customer,
     1333            self.request, self.omit_fields)
     1334        customers_utils = getUtility(ICustomersUtils)
     1335        return customers_utils.renderPDF(
     1336            self, 'application_slip.pdf',
     1337            self.context.customer, customerview,
     1338            omit_fields=self.omit_fields)
  • main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser_templates/documentaddform.pt

    r12015 r12090  
    11<form action="." tal:attributes="action request/URL" method="post"
    2       enctype="multipart/form-data">
     2      enctype="multipart/form-data" i18n:domain="waeup.ikoba">
    33  <table class="form-table">
    44    <tbody>
    55      <tr>
    66        <td class="fieldname">
    7           <span>Document Type</span>:
     7          <span i18n:translate="">Document Type</span>:
    88        </td>
    99        <td>
  • main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser_templates/documentpage.pt

    r12056 r12090  
    1 <div class="wfstatus-sub"  i18n:domain="waeup.kofa">
     1<div class="wfstatus-sub"  i18n:domain="waeup.ikoba">
    22  Document State: <span tal:replace="context/translated_state">
    33    VERIFICATIONSTATE</span>
  • main/waeup.ikoba/trunk/src/waeup/ikoba/customers/container.py

    r11997 r12090  
    1 ## $Id: container.py 8737 2012-06-17 07:32:08Z henrik $
     1## $Id$
    22##
    33## Copyright (C) 2014 Uli Fouquet & Henrik Bettermann
     
    6666        return new_id
    6767
    68     def archive(self, id=None):
    69         raise NotImplementedError()
    70 
    71     def clear(self, id=None, archive=True):
    72         raise NotImplementedError()
    73 
    7468    def addCustomer(self, customer):
    7569        """Add a customer with subcontainers.
  • main/waeup.ikoba/trunk/src/waeup/ikoba/customers/customer.py

    r12087 r12090  
    4343from waeup.ikoba.customers.utils import generate_customer_id, path_from_custid
    4444from waeup.ikoba.customers.documents import CustomerDocumentsContainer
     45from waeup.ikoba.customers.applications import ApplicationsContainer
    4546from waeup.ikoba.utils.helpers import attrs_to_fields, now, copy_filesystem_tree
    4647
     
    183184    documents = CustomerDocumentsContainer()
    184185    customer['documents'] = documents
     186    applications = ApplicationsContainer()
     187    customer['applications'] = applications
    185188    return
    186189
  • main/waeup.ikoba/trunk/src/waeup/ikoba/customers/interfaces.py

    r12089 r12090  
    4747        """
    4848
    49     def archive(id=None):
    50         """Create on-dist archive of customers.
    51 
    52         If id is `None`, all customers are archived.
    53 
    54         If id contains a single id string, only the respective
    55         customers are archived.
    56 
    57         If id contains a list of id strings all of the respective
    58         customers types are saved to disk.
    59         """
    60 
    61     def clear(id=None, archive=True):
    62         """Remove customers of type given by 'id'.
    63 
    64         Optionally archive the customers.
    65 
    66         If id is `None`, all customers are archived.
    67 
    68         If id contains a single id string, only the respective
    69         customers are archived.
    70 
    71         If id contains a list of id strings all of the respective
    72         customer types are saved to disk.
    73 
    74         If `archive` is ``False`` none of the archive-handling is done
    75         and respective customers are simply removed from the
    76         database.
    77         """
    78 
    7949    unique_customer_id = Attribute("""A unique customer id.""")
    8050
  • main/waeup.ikoba/trunk/src/waeup/ikoba/customers/permissions.py

    r12065 r12090  
    4848
    4949
     50class EditApplications(grok.Permission):
     51    grok.name('waeup.editApplications')
     52
     53
    5054class UploadCustomerFile(grok.Permission):
    5155    grok.name('waeup.uploadCustomerFile')
     
    9397                     'waeup.manageCustomer', 'waeup.viewCustomersContainer',
    9498                     'waeup.editCustomerDocuments', 'waeup.uploadCustomerFile',
    95                      'waeup.viewCustomersTab', 'waeup.triggerTransition')
     99                     'waeup.viewCustomersTab', 'waeup.triggerTransition',
     100                     'waeup.editApplications')
    96101
    97102
  • main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_container.py

    r11997 r12090  
    1 ## $Id: test_container.py 7811 2012-03-08 19:00:51Z uli $
     1## $Id$
    22##
    33## Copyright (C) 2014 Uli Fouquet & Henrik Bettermann
     
    6868        return
    6969
    70     def test_base(self):
    71         # We cannot call the fundamental methods of a base in that case
    72         container = CustomersContainer()
    73         self.assertRaises(
    74             NotImplementedError, container.archive)
    75         self.assertRaises(
    76             NotImplementedError, container.clear)
    77         # We cannot add arbitrary objects
    78         #department = Department()
    79         #self.assertRaises(
    80         #    TypeError, container.addCustomer, department)
    81 
    82 
    8370    def test_logger(self):
    8471        # We can get a logger from root
  • main/waeup.ikoba/trunk/src/waeup/ikoba/customers/utils.py

    r12089 r12090  
    3333from waeup.ikoba.interfaces import MessageFactory as _
    3434from waeup.ikoba.interfaces import (
    35     CREATED, STARTED, REQUESTED, APPROVED, IIkobaUtils, IExtFileStore)
     35    STARTED, CREATED, REQUESTED, APPROVED, SUBMITTED, VERIFIED, REJECTED,
     36    EXPIRED,
     37    IIkobaUtils, IExtFileStore)
    3638from waeup.ikoba.customers.interfaces import ICustomersUtils
    3739from waeup.ikoba.browser.interfaces import IPDFCreator
     
    300302
    301303    TRANSLATED_APPLICATION_STATES = {
     304        CREATED: _('created'),
     305        SUBMITTED: _('submitted for approval'),
     306        APPROVED: _('approved'),
     307        REJECTED: _('rejected'),
     308        EXPIRED:_('expired')
    302309        }
    303310
     
    307314        }
    308315
     316    APPTYPES_DICT = {
     317        'SampleApplication': 'Sample Application',
     318        }
     319
    309320    SELECTABLE_DOCTYPES_DICT = DOCTYPES_DICT
     321
     322    SELECTABLE_APPTYPES_DICT = APPTYPES_DICT
    310323
    311324    def getPDFCreator(self, context=None):
  • main/waeup.ikoba/trunk/src/waeup/ikoba/customers/viewlets.py

    r12062 r12090  
    2727from waeup.ikoba.customers.interfaces import (
    2828    ICustomer, ICustomersContainer,
    29     ICustomerDocumentsContainer, ICustomerDocument)
     29    ICustomerDocumentsContainer, ICustomerDocument,
     30    IApplicationsContainer, IApplication)
    3031from waeup.ikoba.customers.browser import (
    3132    CustomersContainerPage, CustomersContainerManagePage,
    3233    CustomerBaseDisplayFormPage,
    33     DocumentsManageFormPage, DocumentDisplayFormPage, DocumentManageFormPage)
     34    DocumentsManageFormPage, DocumentDisplayFormPage, DocumentManageFormPage,
     35    ApplicationsManageFormPage, ApplicationDisplayFormPage,
     36    ApplicationManageFormPage)
    3437
    3538grok.context(IIkobaObject)  # Make IIkobaObject the default context
     
    107110        targets += [
    108111            {'url':customer_url, 'title':'Base Data'},
    109             {'url':customer_url + '/documents', 'title':_('Documents')},
     112            {'url':customer_url + '/applications', 'title':_('My Applications')},
     113            {'url':customer_url + '/documents', 'title':_('My Documents')},
    110114            {'url':customer_url + '/history', 'title':_('History')},
    111115            ]
     
    148152    text = _(u'Base Data')
    149153
     154
     155class CustomerManageApplicationsLink(CustomerManageLink):
     156    grok.order(3)
     157    link = 'applications'
     158    text = _(u'Applications')
     159
     160
    150161class CustomerManageDocumentsLink(CustomerManageLink):
    151     grok.order(3)
     162    grok.order(4)
    152163    link = 'documents'
    153164    text = _(u'Documents')
     
    298309
    299310
    300 class PDFOverviewActionButton(ManageActionButton):
     311class PDFDocumentOverviewActionButton(ManageActionButton):
    301312    grok.order(2)
    302313    grok.context(ICustomerDocumentsContainer)
     
    351362    icon = 'actionicon_view.png'
    352363
    353 class PDFSlipActionButton(ManageActionButton):
     364
     365class PDFDocumentSlipActionButton(ManageActionButton):
    354366    grok.order(3)
    355367    grok.context(ICustomerDocument)
     
    359371    text = _('Download document slip')
    360372    target = 'document_slip.pdf'
     373
     374# Viewlets for customer applications
     375
     376class AddApplicationActionButton(AddActionButton):
     377    grok.order(1)
     378    grok.context(IApplicationsContainer)
     379    grok.view(ApplicationsManageFormPage)
     380    grok.require('waeup.editApplications')
     381    text = _('Add application')
     382    target = 'addapp'
     383
     384
     385class PDFApplicationOverviewActionButton(ManageActionButton):
     386    grok.order(2)
     387    grok.context(IApplicationsContainer)
     388    grok.view(ApplicationsManageFormPage)
     389    grok.require('waeup.viewCustomer')
     390    icon = 'actionicon_pdf.png'
     391    text = _('Download applications overview')
     392    target = 'overview_slip.pdf'
     393
     394
     395class ApplicationManageActionButton(ManageActionButton):
     396    grok.order(1)
     397    grok.context(IApplication)
     398    grok.view(ApplicationDisplayFormPage)
     399    grok.require('waeup.manageCustomer')
     400    text = _('Manage')
     401    target = 'manage'
     402
     403
     404class ApplicationEditActionButton(ManageActionButton):
     405    grok.order(1)
     406    grok.context(IApplication)
     407    grok.view(ApplicationDisplayFormPage)
     408    grok.require('waeup.handleCustomer')
     409    text = _('Edit')
     410    target = 'edit'
     411
     412    @property
     413    def target_url(self):
     414        if not self.context.is_editable:
     415            return ''
     416        return self.view.url(self.view.context, self.target)
     417
     418
     419class ApplicationTrigTransActionButton(ManageActionButton):
     420    grok.order(2)
     421    grok.context(IApplication)
     422    grok.view(ApplicationDisplayFormPage)
     423    grok.require('waeup.triggerTransition')
     424    icon = 'actionicon_trigtrans.png'
     425    text = _(u'Transition')
     426    target = 'trigtrans'
     427
     428
     429class ApplicationViewActionButton(ManageActionButton):
     430    grok.order(1)
     431    grok.context(IApplication)
     432    grok.view(ApplicationManageFormPage)
     433    grok.require('waeup.handleCustomer')
     434    text = _('View')
     435    target = 'index'
     436    icon = 'actionicon_view.png'
     437
     438
     439class PDFApplicationSlipActionButton(ManageActionButton):
     440    grok.order(3)
     441    grok.context(IApplication)
     442    grok.view(ApplicationDisplayFormPage)
     443    grok.require('waeup.viewCustomer')
     444    icon = 'actionicon_pdf.png'
     445    text = _('Download application slip')
     446    target = 'application_slip.pdf'
     447
Note: See TracChangeset for help on using the changeset viewer.