Ignore:
Timestamp:
12 Mar 2015, 05:29:43 (10 years ago)
Author:
uli
Message:

Merge changes from uli-payments back into trunk.

Location:
main/waeup.ikoba/trunk/src/waeup/ikoba/customers
Files:
9 edited

Legend:

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

    r12663 r12741  
    1818"""UI components for customers and related components.
    1919"""
    20 
    21 import sys
    2220import grok
    23 import pytz
    2421import os
    2522from urllib import urlencode
    26 from datetime import datetime
    2723from zope.event import notify
    2824from zope.i18n import translate
    2925from zope.catalog.interfaces import ICatalog
    3026from zope.component import queryUtility, getUtility, createObject
    31 from zope.schema.interfaces import ConstraintNotSatisfied, RequiredMissing
    32 from zope.formlib.textwidgets import BytesDisplayWidget
    3327from zope.security import checkPermission
    3428from hurry.workflow.interfaces import (
     
    4236from waeup.ikoba.browser.layout import (
    4337    IkobaPage, IkobaEditFormPage, IkobaAddFormPage, IkobaDisplayFormPage,
    44     IkobaForm, NullValidator, jsaction, action, UtilityView)
    45 from waeup.ikoba.widgets.datewidget import (
    46     FriendlyDateWidget, FriendlyDateDisplayWidget,
    47     FriendlyDatetimeDisplayWidget)
     38    NullValidator, jsaction, action, UtilityView)
    4839from waeup.ikoba.browser.pages import ContactAdminForm
    4940from waeup.ikoba.browser.breadcrumbs import Breadcrumb
    5041from waeup.ikoba.browser.interfaces import ICaptchaManager
    5142from waeup.ikoba.mandates.mandate import PasswordMandate
     43from waeup.ikoba.payments.payment import format_payment_item_values
     44from waeup.ikoba.payments.interfaces import (
     45    IPaymentGatewayServicesLister, IPaymentGatewayService, IPayer, IPayable
     46    )
    5247from waeup.ikoba.widgets.hrefwidget import HREFDisplayWidget
    5348from waeup.ikoba.utils.helpers import (
    54     get_current_principal, to_timezone, now, format_date)
     49    get_current_principal, format_date)
    5550from waeup.ikoba.customers.interfaces import (
    5651    ICustomer, ICustomersContainer, ICustomerRequestPW, ICustomersUtils,
    5752    ICustomerDocument, ICustomerDocumentsContainer, ICustomerCreate,
    5853    ICustomerPDFDocument, IContractsContainer, IContract,
    59     IContractSelectProduct, ISampleContract,
     54    IContractSelectProduct,
    6055    )
    6156from waeup.ikoba.customers.catalog import search
    6257from waeup.ikoba.customers.workflow import PAYMENT_TRANSITIONS
     58
    6359
    6460grok.context(IIkobaObject)
     
    927923    def createDocument(self, **data):
    928924        form = self.request.form
    929         customer = self.context.__parent__
    930925        doctype = form.get('doctype', None)
    931926        # Here we can create various instances of CustomerDocument derived
     
    10981093        tableheader = []
    10991094        tabledata = []
    1100         contenttitle = []
    11011095        for i in range(1,3):
    11021096            tabledata.append(sorted(
     
    11341128    @property
    11351129    def label(self):
    1136         portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
    11371130        return '%s of %s\nTitle: %s' % (
    11381131            self.context.translated_class_name,
     
    11411134
    11421135    def render(self):
    1143         portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
    11441136        customerview = CustomerBasePDFFormPage(self.context.customer,
    11451137            self.request, self.omit_fields)
     
    11571149
    11581150    def render(self):
    1159         portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
    11601151        customerview = CustomerBasePDFFormPage(self.context.customer,
    11611152            self.request, self.omit_fields)
     
    12951286    def createContract(self, **data):
    12961287        form = self.request.form
    1297         customer = self.context.__parent__
    12981288        contype = form.get('contype', None)
    12991289        # Here we can create various instances of Contract derived
     
    15061496    label = _('Select payment method')
    15071497
    1508     def update(self, CANCEL=None):
     1498    @property
     1499    def payment_gateways(self):
     1500        """Get an iter over registered and enabled gateway service providers.
     1501
     1502        We provide tuples ``(value, description)`` for each supported
     1503        payment gateway.
     1504        """
     1505        lister = getUtility(IPaymentGatewayServicesLister)
     1506        for name, service in lister().items():
     1507            yield {'name': name, 'title': service.title}
     1508
     1509    def update(self, CANCEL=None, gw=None):
    15091510        if self.context.state != CREATED or not self.context.fee_based:
    15101511            emit_lock_message(self)
    15111512            return
     1513        self.gw = gw
    15121514        super(SelectPaymentMethodPage, self).update()
    15131515        return
    15141516
    1515     @action(_('Select payment method and proceed to payment gateway (final submission)'),
    1516             style='primary', warning=WARNING_CON,)
     1517    @action(_('Select payment method (final submission)'),
     1518            style='primary')
    15171519    def confirm(self, **data):
    1518         IWorkflowInfo(self.context).fireTransition('await')
    1519         self.flash(_('Payment has been initiated.'))
    1520         return
     1520        if self.gw is None:
     1521            self.flash(_('Please pick a payment method.'),
     1522                       type='warning')
     1523        else:
     1524            service = queryUtility(IPaymentGatewayService, name=self.gw)
     1525            if service is None:
     1526                self.flash(_('Invalid payment gateway.'), type='danger')
     1527                return
     1528            payer = IPayer(self.context)
     1529            payable = IPayable(self.context)
     1530            payment = service.create_payment(payer, payable)
     1531            service.store(payment)
     1532            payment, view_name = service.next_step(payment.payment_id)
     1533            url = self.url(payment, view_name)
     1534            self.redirect(url)
     1535            return
     1536        return
     1537
     1538    @action(_('Cancel'))
     1539    def cancel(self, **data):
     1540        self.redirect(self.url(self.context, 'edit'))
     1541        return
     1542
    15211543
    15221544class ContractTriggerTransitionFormPage(IkobaEditFormPage):
     
    16001622        tableheader = []
    16011623        tabledata = []
    1602         contenttitle = []
    16031624        for i in range(1,3):
    16041625            tabledata.append(sorted(
     
    16551676    @property
    16561677    def label(self):
    1657         portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
    16581678        return self.context.title
    16591679
     
    16701690
    16711691    def render(self):
    1672         portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
    16731692        customerview = CustomerBasePDFFormPage(self.context.customer,
    16741693            self.request, self.omit_fields)
  • main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser_templates/selectpaymentmethodpage.pt

    r12663 r12741  
     1
    12<form action="." tal:attributes="action request/URL" method="post"
    23      i18n:domain="waeup.ikoba" enctype="multipart/form-data">
    34
    4   <table class="form-table">
    5     <tbody>
    6       <tr>
    7         <td class="fieldname" i18n:translate=""></td>
    8         <td>
    9         </td>
    10       </tr>
    11       <tr>
    12         <td class="fieldname" i18n:translate=""></td>
    13         <td>
     5  <div class="form-group">
    146
    15         </td>
    16       </tr>
    17     </tbody>
    18   </table>
     7    <div class="radio" tal:repeat="service view/payment_gateways">
     8      <label>
     9        <input type="radio" name="gw" tal:attributes="value service/name" />
     10        <b><span tal:replace="service/title" /></b>
     11      </label>
     12    </div>
     13
     14  </div>
     15
    1916  <br />
    2017
  • main/waeup.ikoba/trunk/src/waeup/ikoba/customers/contracts.py

    r12663 r12741  
    1919Customer contract components.
    2020"""
    21 import os
    2221import grok
    23 from zope.component import queryUtility, getUtility
     22from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
     23from zope.catalog.interfaces import ICatalog
     24from zope.component import getUtility, queryUtility
    2425from zope.component.interfaces import IFactory
    2526from zope.interface import implementedBy
    2627from zope.schema import getFields
    27 from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
    2828from waeup.ikoba.interfaces import MessageFactory as _
    2929from waeup.ikoba.interfaces import (
    30     IIkobaUtils, IObjectHistory,
    31     VERIFIED, APPROVED, PROVISIONALLY,
    32     IIDSource)
     30    IObjectHistory, VERIFIED, APPROVED, PROVISIONALLY, IIDSource)
    3331from waeup.ikoba.customers.interfaces import (
    34     IContractsContainer, ICustomerNavigation,
    35     IContract, IContractSelectProduct, ICustomersUtils,
    36     ISampleContract, ISampleContractProcess, ISampleContractEdit,
    37     ISampleContractOfficialUse)
    38 from waeup.ikoba.customers.utils import generate_contract_id
     32    IContractsContainer, ICustomerNavigation, IContract,
     33    IContractSelectProduct, ICustomersUtils, ISampleContract,
     34    ISampleContractProcess, ISampleContractEdit, ISampleContractOfficialUse)
     35from waeup.ikoba.payments.interfaces import (
     36    IPayer, IPayableFinder, IPayable, IPaymentWaitingForGatewayEvent,
     37    STATE_PAID, STATE_FAILED, IPaymentFinishedEvent
     38    )
     39from waeup.ikoba.payments.payment import (
     40    PaymentItem, find_payable_from_payable_id,
     41    )
    3942from waeup.ikoba.utils.helpers import attrs_to_fields
     43
    4044
    4145class ContractsContainer(grok.Container):
     
    6165ContractsContainer = attrs_to_fields(ContractsContainer)
    6266
     67
     68class ContractPayer(grok.Adapter):
     69    """Adapter to turn contracts into IPayers.
     70    """
     71    grok.implements(IPayer)
     72    grok.context(IContract)
     73
     74    def __init__(self, context):
     75        self.context = context
     76
     77    @property
     78    def _customer(self):
     79        return self.context.customer
     80
     81    @property
     82    def first_name(self):
     83        return getattr(self._customer, 'firstname', None)
     84
     85    @property
     86    def last_name(self):
     87        return getattr(self._customer, 'lastname', None)
     88
     89    @property
     90    def payer_id(self):
     91        return getattr(self._customer, 'customer_id', None)
     92
     93
    6394class ContractBase(grok.Container):
    6495    """This is a customer contract baseclass.
    6596    """
    66     grok.implements(IContractSelectProduct)  # Necesary for the selectproduct page
    67 
     97    grok.implements(IContractSelectProduct)  # Neccessary for the
     98                                             # selectproduct page (why?)
    6899    grok.baseclass()
    69100
     
    150181                state = getattr(obj, 'state', None)
    151182                if state and state != VERIFIED:
    152                     return False, _("Attached documents must be verified first.")
     183                    return False, _(
     184                        "Attached documents must be verified first.")
    153185        return True, None
    154186
     
    177209
    178210    grok.implements(
    179         ISampleContractProcess, # must come before ISampleContract
     211        ISampleContractProcess,  # must come before ISampleContract
    180212        ISampleContract,
    181213        ISampleContractEdit,
     
    210242        return implementedBy(SampleContract)
    211243
     244
    212245@grok.subscribe(IContract, grok.IObjectAddedEvent)
    213246def handle_contract_added(contract, event):
     
    218251        IWorkflowInfo(contract).fireTransition('create')
    219252    return
     253
     254
     255@grok.subscribe(IPaymentWaitingForGatewayEvent)
     256def handle_payment_waiting_for_gw(event):
     257    maybe_contract = find_payable_from_payable_id(
     258        event.object.payable_id)
     259    if IContract.providedBy(maybe_contract):
     260        IWorkflowInfo(maybe_contract).fireTransition('await')
     261
     262
     263@grok.subscribe(IPaymentFinishedEvent)
     264def handle_payment_finished(event):
     265    payment = event.object
     266    maybe_contract = find_payable_from_payable_id(payment.payable_id)
     267    if not IContract.providedBy(maybe_contract):
     268        return
     269    if payment.state == STATE_PAID:
     270        IWorkflowInfo(maybe_contract).fireTransition('confirm')
     271    else:
     272        IWorkflowInfo(maybe_contract).fireTransition('discard')
     273
     274
     275class ContractFinder(grok.GlobalUtility):
     276    grok.name('contracts_finder')
     277    grok.implements(IPayableFinder)
     278
     279    def get_payable_by_id(self, contract_id):
     280        catalog = queryUtility(ICatalog, 'contracts_catalog')
     281        if catalog is None:
     282            return None
     283        result = catalog.searchResults(
     284            contract_id=(contract_id, contract_id))
     285        result = [x for x in result]
     286        if not result:
     287            return None
     288        # there should not be more than one result really.
     289        return result[0]
     290
     291
     292class PayableContract(grok.Adapter):
     293    """Adapter to adapt IContracts to IPayable.
     294    """
     295
     296    grok.context(IContract)
     297    grok.implements(IPayable)
     298
     299    def __init__(self, context):
     300        self.context = context
     301        currencies = set([x.currency for x in context.product_options])
     302        if len(currencies) > 1:
     303            raise ValueError(
     304                "Only contracts with same currency for all options allowed.")
     305        return
     306
     307    @property
     308    def payable_id(self):
     309        return self.context.contract_id
     310
     311    @property
     312    def title(self):
     313        return self.context.title or u''
     314
     315    @property
     316    def currency(self):
     317        if not len(self.context.product_options):
     318            return None
     319        return self.context.product_options[0].currency
     320
     321    @property
     322    def payment_items(self):
     323        result = []
     324        for num, option in enumerate(self.context.product_options):
     325            item = PaymentItem()
     326            item.item_id = u'%s' % num
     327            item.title = option.title
     328            item.amount = option.fee
     329            result.append(item)
     330        return tuple(result)
  • main/waeup.ikoba/trunk/src/waeup/ikoba/customers/customer.py

    r12553 r12741  
    2525from hurry.workflow.interfaces import IWorkflowState, IWorkflowInfo
    2626from zope.password.interfaces import IPasswordManager
    27 from zope.component import getUtility, createObject
     27from zope.catalog.interfaces import ICatalog
     28from zope.component import getUtility, queryUtility
    2829from zope.component.interfaces import IFactory
    2930from zope.interface import implementedBy
    3031from zope.securitypolicy.interfaces import IPrincipalRoleManager
    31 from zope.schema.interfaces import ConstraintNotSatisfied
    3232
    3333from waeup.ikoba.image import IkobaImageFile
     
    3535from waeup.ikoba.interfaces import (
    3636    IObjectHistory, IUserAccount, IFileStoreNameChooser, IFileStoreHandler,
    37     IIkobaUtils, IExtFileStore,
    38     CREATED, REQUESTED, APPROVED)
     37    IIkobaUtils, IExtFileStore, )
    3938from waeup.ikoba.customers.interfaces import (
    4039    ICustomer, ICustomerNavigation, ICSVCustomerExporter,
     
    4342from waeup.ikoba.customers.documents import CustomerDocumentsContainer
    4443from waeup.ikoba.customers.contracts import ContractsContainer
    45 from waeup.ikoba.utils.helpers import attrs_to_fields, now, copy_filesystem_tree
     44from waeup.ikoba.payments.interfaces import IPayer, IPayerFinder
     45from waeup.ikoba.utils.helpers import (
     46    attrs_to_fields, now, copy_filesystem_tree)
     47
    4648
    4749class Customer(grok.Container):
     
    7779            'password'] = passwordmanager.encodePassword(password)
    7880        self.temp_password['user'] = user
    79         self.temp_password['timestamp'] = datetime.utcnow()  # offset-naive datetime
     81        self.temp_password[
     82            'timestamp'] = datetime.utcnow()  # offset-naive datetime
    8083
    8184    def getTempPassword(self):
     
    8992        if temp_password_dict is not None:
    9093            delta = timedelta(minutes=self.temp_password_minutes)
    91             now = datetime.utcnow()
    92             if now < temp_password_dict.get('timestamp') + delta:
     94            dt_now = datetime.utcnow()
     95            if dt_now < temp_password_dict.get('timestamp') + delta:
    9396                return temp_password_dict.get('password')
    9497            else:
     
    98101
    99102    def writeLogMessage(self, view, message):
    100         ob_class = view.__implemented__.__name__.replace('waeup.ikoba.','')
     103        ob_class = view.__implemented__.__name__.replace(
     104            'waeup.ikoba.', '')
    101105        self.__parent__.logger.info(
    102106            '%s - %s - %s' % (ob_class, self.__name__, message))
     
    196200    return
    197201
     202
    198203def move_customer_files(customer, del_dir):
    199204    """Move files belonging to `customer` to `del_dir`.
     
    246251        timestamp = str(now().replace(microsecond=0))  # store UTC timestamp
    247252        for num, row in enumerate(csv_data[1:-1]):
    248             csv_data[num+1] = csv_data[num+1] + ',' + timestamp
     253            csv_data[num + 1] = csv_data[num + 1] + ',' + timestamp
    249254        csv_path = os.path.join(del_dir, '%s.csv' % name)
    250255
     
    399404        return file, path, IkobaImageFile(
    400405            file_obj.filename, file_obj.data)
     406
     407
     408class CustomerPayer(grok.Adapter):
     409    """Adapter to turn customers into IPayers.
     410    """
     411    grok.implements(IPayer)
     412    grok.context(ICustomer)
     413
     414    @property
     415    def first_name(self):
     416        return getattr(self.context, 'firstname', None)
     417
     418    @property
     419    def last_name(self):
     420        return getattr(self.context, 'lastname', None)
     421
     422    @property
     423    def payer_id(self):
     424        return getattr(self.context, 'customer_id', None)
     425
     426
     427class CustomerFinder(grok.GlobalUtility):
     428    """Find customers.
     429    """
     430    grok.name('customer_finder')
     431    grok.implements(IPayerFinder)
     432
     433    def get_payer_by_id(self, customer_id):
     434        catalog = queryUtility(ICatalog, 'customers_catalog')
     435        if catalog is None:
     436            return None
     437        result = catalog.searchResults(
     438            customer_id=(customer_id, customer_id))
     439        result = [x for x in result]
     440        if not result:
     441            return None
     442        # there should not be more than one result really.
     443        return result[0]
  • main/waeup.ikoba/trunk/src/waeup/ikoba/customers/export.py

    r12500 r12741  
    215215                      'translated_class_name',
    216216                      'is_editable',
    217                       'is_approvable'])))
     217                      'is_approvable',
     218                      'customer'])))
    218219
    219220    def filter_func(self, x, **kw):
  • main/waeup.ikoba/trunk/src/waeup/ikoba/customers/interfaces.py

    r12663 r12741  
    298298    is_approvable = Attribute('Contract approvable by officer')
    299299    translated_class_name = Attribute('Translatable class name')
     300    customer = Attribute('Customer object of context.')
    300301    user_id = Attribute('Id of a user, actually the id of the customer')
    301302    title = Attribute('Title generated by the associated product')
  • main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_browser.py

    r12663 r12741  
    11## $Id$
    2 ## 
     2##
    33## Copyright (C) 2014 Uli Fouquet & Henrik Bettermann
    44## This program is free software; you can redistribute it and/or modify
     
    66## the Free Software Foundation; either version 2 of the License, or
    77## (at your option) any later version.
    8 ## 
     8##
    99## This program is distributed in the hope that it will be useful,
    1010## but WITHOUT ANY WARRANTY; without even the implied warranty of
    1111## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    1212## GNU General Public License for more details.
    13 ## 
     13##
    1414## You should have received a copy of the GNU General Public License
    1515## along with this program; if not, write to the Free Software
     
    2222import tempfile
    2323import logging
    24 import pytz
    2524import base64
    2625from decimal import Decimal
     
    4241from waeup.ikoba.testing import FunctionalLayer, FunctionalTestCase
    4342from waeup.ikoba.app import Company
    44 from waeup.ikoba.customers.interfaces import ICustomersUtils
    45 from waeup.ikoba.customers.customer import Customer
    4643from waeup.ikoba.interfaces import (
    4744    IUserAccount, IJobManager, APPROVED, SUBMITTED,
    48     IFileStoreNameChooser, IExtFileStore, IFileStoreHandler, NotIdValue)
    49 from waeup.ikoba.imagestorage import (
    50     FileStoreNameChooser, ExtFileStore, DefaultFileStoreHandler,
    51     DefaultStorage)
    52 from waeup.ikoba.authentication import LocalRoleSetEvent
     45    IFileStoreNameChooser, NotIdValue)
     46from waeup.ikoba.imagestorage import ExtFileStore
    5347from waeup.ikoba.tests.test_async import FunctionalAsyncTestCase
    5448from waeup.ikoba.interfaces import VERIFIED
     
    6155SAMPLE_IMAGE_BMP = os.path.join(os.path.dirname(__file__), 'test_image.bmp')
    6256SAMPLE_PDF = os.path.join(os.path.dirname(__file__), 'test_pdf.pdf')
     57
    6358
    6459def lookup_submit_value(name, value, browser):
     
    7267            break
    7368    return None
     69
    7470
    7571class CustomersFullSetup(FunctionalTestCase):
     
    119115        prodoption.fee = Decimal('99.9')
    120116        prodoption.currency = 'USD'
    121         self.product.options = [prodoption,]
     117        self.product.options = [prodoption, ]
    122118        self.app['products'].addProduct(self.product)
    123119
     
    138134        self.document.document_id = u'DOC1'
    139135        self.assertRaises(
    140             NotIdValue, setattr, self.document, 'document_id', u'id with spaces')
     136            NotIdValue, setattr, self.document, 'document_id',
     137            u'id with spaces')
    141138        self.customer['documents'].addDocument(self.document)
    142139        self.contract = createObject(self._contract_factory)
    143140        self.contract.contract_id = u'CON1'
    144141        self.assertRaises(
    145             NotIdValue, setattr, self.contract, 'contract_id', u'id with spaces')
     142            NotIdValue, setattr, self.contract, 'contract_id',
     143            u'id with spaces')
    146144        self.customer['contracts'].addContract(self.contract)
    147145
     
    299297        return
    300298
     299
    301300class OfficerUITests(CustomersFullSetup):
    302301    # Tests for Customer class views and pages
    303 
    304302
    305303    def setup_logging(self):
     
    331329        self.browser.getLink("Logout").click()
    332330        self.assertTrue('You have been logged out' in self.browser.contents)
    333         # But we are still logged in since we've used basic authentication here.
    334         # Wikipedia says: Existing browsers retain authentication information
    335         # until the tab or browser is closed or the user clears the history.
    336         # HTTP does not provide a method for a server to direct clients to
    337         # discard these cached credentials. This means that there is no
    338         # effective way for a server to "log out" the user without closing
    339         # the browser. This is a significant defect that requires browser
    340         # manufacturers to support a "logout" user interface element ...
     331        # But we are still logged in since we've used basic
     332        # authentication here.  Wikipedia says: Existing browsers
     333        # retain authentication information until the tab or browser
     334        # is closed or the user clears the history.  HTTP does not
     335        # provide a method for a server to direct clients to discard
     336        # these cached credentials. This means that there is no
     337        # effective way for a server to "log out" the user without
     338        # closing the browser. This is a significant defect that
     339        # requires browser manufacturers to support a "logout" user
     340        # interface element ...
    341341        self.assertTrue('Manager' in self.browser.contents)
    342342
     
    378378        self.browser.open(self.customer_path)
    379379        self.browser.getLink("Send email").click()
    380         self.browser.getControl(name="form.subject").value = 'Important subject'
     380        self.browser.getControl(
     381            name="form.subject").value = 'Important subject'
    381382        self.browser.getControl(name="form.body").value = 'Hello!'
    382383        self.browser.getControl("Send message now").click()
     
    410411        self.assertTrue('passport.jpg deleted' in self.browser.contents)
    411412
    412 
    413413    def test_manage_workflow_send_transition_information(self):
    414414        # Managers can pass through the whole workflow
     
    417417        self.setup_logging()
    418418        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
    419         customer = self.app['customers'][self.customer_id]
     419        self.customer = self.app['customers'][self.customer_id]
    420420        self.browser.open(self.trigtrans_path)
    421421        self.browser.getControl(name="transition").value = ['start']
     
    459459        self.browser.getControl("Apply").click()
    460460        self.browser.open(self.trigtrans_path)
    461         self.browser.getControl(name="transition").value = ['approve_provisionally']
     461        self.browser.getControl(name="transition").value = [
     462            'approve_provisionally']
    462463        self.browser.getControl("Apply").click()
    463464        self.browser.open(self.trigtrans_path)
     
    499500        self.browser.getControl('Perform import').click()
    500501        self.assertTrue('Processing of 1 rows failed' in self.browser.contents)
    501         self.assertTrue('Successfully processed 2 rows' in self.browser.contents)
     502        self.assertTrue(
     503            'Successfully processed 2 rows' in self.browser.contents)
    502504        self.assertTrue('Batch processing finished' in self.browser.contents)
    503505
     
    535537        self.browser.getLink("History").click()
    536538        self.assertTrue(
    537             'Customer account deactivated by Manager<br />' in self.browser.contents)
    538         self.assertTrue(
    539             'Customer account activated by Manager<br />' in self.browser.contents)
     539            'Customer account deactivated by Manager<br />'
     540            in self.browser.contents)
     541        self.assertTrue(
     542            'Customer account activated by Manager<br />'
     543            in self.browser.contents)
    540544        # ... and actions have been logged.
    541545        logfile = os.path.join(
    542546            self.app['datacenter'].storage, 'logs', 'customers.log')
    543547        logcontent = open(logfile).read()
    544         self.assertTrue('zope.mgr - customers.browser.CustomerDeactivatePage - '
    545                         'K1000000 - account deactivated' in logcontent)
    546         self.assertTrue('zope.mgr - customers.browser.CustomerActivatePage - '
    547                         'K1000000 - account activated' in logcontent)
    548 
     548        self.assertTrue(
     549            'zope.mgr - customers.browser.CustomerDeactivatePage - '
     550            'K1000000 - account deactivated' in logcontent)
     551        self.assertTrue(
     552            'zope.mgr - customers.browser.CustomerActivatePage - '
     553            'K1000000 - account activated' in logcontent)
    549554
    550555    def test_login_as_customer(self):
     
    554559        self.app['users']['mrofficer'].title = 'Harry Actor'
    555560        prmglobal = IPrincipalRoleManager(self.app)
    556         prmglobal.assignRoleToPrincipal('waeup.CustomerImpersonator', 'mrofficer')
     561        prmglobal.assignRoleToPrincipal(
     562            'waeup.CustomerImpersonator', 'mrofficer')
    557563        prmglobal.assignRoleToPrincipal('waeup.CustomersManager', 'mrofficer')
    558564        self.assertEqual(self.customer.state, 'created')
     
    574580        # We are logged in as customer and can see the 'My Data' tab
    575581        self.assertMatches(
    576             '...<a href="#" class="dropdown-toggle" data-toggle="dropdown">...',
     582            '...<a href="#" class="dropdown-toggle"'
     583            ' data-toggle="dropdown">...',
    577584            self.browser.contents)
    578585        self.assertMatches(
     
    724731        self.assertMatches(
    725732            '...<div class="alert alert-warning">'
    726             'Your account has been deactivated.</div>...', self.browser.contents)
     733            'Your account has been deactivated.</div>...',
     734            self.browser.contents)
    727735        # If suspended_comment is set this message will be flashed instead
    728736        self.customer.suspended_comment = u'Aetsch baetsch!'
     
    778786        self.assertTrue('An email with' in self.browser.contents)
    779787
     788
    780789class CustomerRegistrationTests(CustomersFullSetup):
    781790    # Tests for customer registration
     
    823832            cat.searchResults(
    824833            email=('new@yy.zz', 'new@yy.zz')))
    825         self.assertEqual(self.customer,results[0])
     834        self.assertEqual(self.customer, results[0])
    826835        logfile = os.path.join(
    827836            self.app['datacenter'].storage, 'logs', 'main.log')
    828837        logcontent = open(logfile).read()
    829         self.assertTrue('zope.anybody - customers.browser.CustomerRequestPasswordPage - '
    830                         '123 (K1000000) - new@yy.zz' in logcontent)
     838        self.assertTrue(
     839            'zope.anybody - customers.browser.CustomerRequestPasswordPage - '
     840            '123 (K1000000) - new@yy.zz' in logcontent)
    831841        return
    832842
     
    841851        self.browser.getControl(name="form.email").value = 'newcustomer@xx.zz'
    842852        self.browser.getControl("Send login credentials").click()
    843         self.assertTrue('Your request was successful.' in self.browser.contents)
     853        self.assertTrue(
     854            'Your request was successful.' in self.browser.contents)
    844855        # Customer can be found in the catalog via the email address
    845856        cat = queryUtility(ICatalog, name='customers_catalog')
     
    849860        self.assertEqual(self.app['customers']['K1000001'], results[0])
    850861        self.assertEqual(self.app['customers']['K1000001'].firstname, 'Ruben')
    851         self.assertEqual(self.app['customers']['K1000001'].lastname, 'Gonzales')
     862        self.assertEqual(
     863            self.app['customers']['K1000001'].lastname, 'Gonzales')
    852864        logfile = os.path.join(
    853865            self.app['datacenter'].storage, 'logs', 'main.log')
    854866        logcontent = open(logfile).read()
    855         self.assertTrue('zope.anybody - customers.browser.CustomerCreateAccountPage - '
    856                         'K1000001 - newcustomer@xx.zz' in logcontent)
     867        self.assertTrue(
     868            'zope.anybody - customers.browser.CustomerCreateAccountPage - '
     869            'K1000001 - newcustomer@xx.zz' in logcontent)
    857870        return
     871
    858872
    859873class CustomerDataExportTests(CustomersFullSetup, FunctionalAsyncTestCase):
     
    929943        self.browser.getControl("Add document").click()
    930944        self.assertTrue('PDF Document added.' in self.browser.contents)
    931         docid = [i for i in self.customer['documents'].keys() if len(i) > 10][0]
     945        docid = [i for i in self.customer['documents'].keys()
     946                 if len(i) > 10][0]
    932947        document = self.customer['documents'][docid]
    933948
     
    950965        self.browser.getControl(name="transition").value = ['verify']
    951966        self.browser.getControl("Apply").click()
    952         self.assertTrue('Customer has not yet been approved' in self.browser.contents)
     967        self.assertTrue(
     968            'Customer has not yet been approved' in self.browser.contents)
    953969        IWorkflowState(self.customer).setState(APPROVED)
    954970        # Document can only be verified if files have been uploaded before
     
    958974        self.assertTrue('No file uploaded' in self.browser.contents)
    959975        self.assertEqual(document.state, 'submitted')
    960         # We set state here manually (verification is tested in test_verify_document)
     976        # We set state here manually (verification is tested in
     977        # test_verify_document)
    961978        IWorkflowState(document).setState(VERIFIED)
    962979
     
    964981        self.browser.open(self.documents_path + '/' + docid + '/index')
    965982        self.assertFalse(
    966             'href="http://localhost/app/customers/K1000000/documents/%s/manage"'
     983            'href="http://localhost/app/customers/K1000000/'
     984            'documents/%s/manage"'
    967985            % docid in self.browser.contents)
    968986        self.browser.open(self.documents_path + '/' + docid + '/manage')
     
    10121030        self.browser.getLink("Documents").click()
    10131031        self.browser.getControl("Add document").click()
    1014         self.assertTrue('The requested form is locked' in self.browser.contents)
     1032        self.assertTrue(
     1033            'The requested form is locked' in self.browser.contents)
    10151034        # Customer is in wrong state
    10161035        IWorkflowState(self.customer).setState(APPROVED)
    10171036        self.browser.getControl("Add document").click()
    1018         self.browser.getControl(name="doctype").value = ['CustomerSampleDocument']
     1037        self.browser.getControl(name="doctype").value = [
     1038            'CustomerSampleDocument']
    10191039        self.browser.getControl(name="form.title").value = 'My Sample Document'
    10201040        self.browser.getControl("Add document").click()
    10211041        self.assertTrue('Sample Document added.' in self.browser.contents)
    1022         docid = [i for i in self.customer['documents'].keys() if len(i) > 10][0]
     1042        docid = [i for i in self.customer['documents'].keys()
     1043                 if len(i) > 10][0]
    10231044        document = self.customer['documents'][docid]
    10241045        self.browser.getControl(name="form.title").value = 'My second doc'
     
    10381059            name='upload_samplescaneditupload').click()
    10391060        self.assertTrue(
    1040             'href="http://localhost/app/customers/K1000000/documents/%s/sample"'
     1061            'href="http://localhost/app/customers/K1000000/'
     1062            'documents/%s/sample"'
    10411063            % docid in self.browser.contents)
    10421064        # Customer can submit the form. The form is also saved.
     
    10451067        self.assertEqual(document.title, 'My third doc')
    10461068        self.assertEqual(document.state, 'submitted')
    1047         self.assertTrue('Document State: submitted for verification' in self.browser.contents)
     1069        self.assertTrue(
     1070            'Document State: submitted for verification'
     1071            in self.browser.contents)
    10481072        # Customer can't edit the document once it has been submitted
    10491073        self.browser.open(self.documents_path + '/%s/edit' % docid)
    1050         self.assertTrue('The requested form is locked' in self.browser.contents)
     1074        self.assertTrue(
     1075            'The requested form is locked' in self.browser.contents)
    10511076
    10521077    def test_manage_upload_sample_file(self):
     
    10551080        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
    10561081        self.browser.open(self.customer_path + '/documents/DOC1/manage')
    1057         # Create a pseudo image file and select it to be uploaded 
     1082        # Create a pseudo image file and select it to be uploaded
    10581083        image = open(SAMPLE_IMAGE, 'rb')
    10591084        ctrl = self.browser.getControl(name='samplescanmanageupload')
     
    10611086        file_ctrl.add_file(image, filename='my_sample_scan.jpg')
    10621087        # The Save action does not upload files
    1063         self.browser.getControl("Save").click() # submit form
     1088        self.browser.getControl("Save").click()  # submit form
    10641089        self.assertFalse(
    1065             'href="http://localhost/app/customers/K1000000/documents/DOC1/sample"'
     1090            'href="http://localhost/app/customers/K1000000/'
     1091            'documents/DOC1/sample"'
    10661092            in self.browser.contents)
    10671093        # ... but the correct upload submit button does
     
    10731099            name='upload_samplescanmanageupload').click()
    10741100        self.assertTrue(
    1075             'href="http://localhost/app/customers/K1000000/documents/DOC1/sample"'
     1101            'href="http://localhost/app/customers/K1000000/'
     1102            'documents/DOC1/sample"'
    10761103            in self.browser.contents)
    10771104        # Browsing the link shows a real image
     
    10981125            'Uploaded file is too big' in self.browser.contents)
    10991126        # We do not rely on filename extensions given by uploaders
    1100         image = open(SAMPLE_IMAGE, 'rb') # a jpg-file
     1127        image = open(SAMPLE_IMAGE, 'rb')  # a jpg-file
    11011128        ctrl = self.browser.getControl(name='samplescanmanageupload')
    11021129        file_ctrl = ctrl.mech_control
     
    11521179        self.browser.getControl(name="form.title").value = 'My PDF Document'
    11531180        self.browser.getControl("Add document").click()
    1154         docid = [i for i in self.customer['documents'].keys() if len(i) > 10][0]
     1181        docid = [
     1182            i for i in self.customer['documents'].keys() if len(i) > 10][0]
    11551183        self.browser.open(self.documents_path + '/%s/manage' % docid)
    11561184        # Create a pseudo image file and select it to be uploaded
     
    11771205            name='upload_pdfscanmanageupload').click()
    11781206        self.assertTrue(
    1179             'href="http://localhost/app/customers/K1000000/documents/%s/sample.pdf">%s.pdf</a>'
     1207            'href="http://localhost/app/customers/K1000000/'
     1208            'documents/%s/sample.pdf">%s.pdf</a>'
    11801209            % (docid, docid[:9]) in self.browser.contents)
    11811210        # Browsing the link shows a real pdf
     
    11941223        self.browser.getLink("Download documents overview").click()
    11951224        self.assertEqual(self.browser.headers['Status'], '200 Ok')
    1196         self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
     1225        self.assertEqual(
     1226            self.browser.headers['Content-Type'], 'application/pdf')
    11971227        path = os.path.join(samples_dir(), 'documents_overview_slip.pdf')
    11981228        open(path, 'wb').write(self.browser.contents)
     
    12001230        # Officers can open document slips which shows a thumbnail of
    12011231        # the jpeg file attached.
    1202         file_id = IFileStoreNameChooser(self.document).chooseName(attr='sample.jpg')
     1232        file_id = IFileStoreNameChooser(self.document).chooseName(
     1233            attr='sample.jpg')
    12031234        fs = ExtFileStore(root=self.dc_root)
    12041235        jpegfile = open(SAMPLE_IMAGE, 'rb')
     
    12071238        self.browser.getLink("Download document slip").click()
    12081239        self.assertEqual(self.browser.headers['Status'], '200 Ok')
    1209         self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
     1240        self.assertEqual(
     1241            self.browser.headers['Content-Type'], 'application/pdf')
    12101242        path = os.path.join(samples_dir(), 'document_slip.pdf')
    12111243        open(path, 'wb').write(self.browser.contents)
     
    12161248        self.customer['documents'].addDocument(pdfdocument)
    12171249        # Add pdf file
    1218         file_id = IFileStoreNameChooser(pdfdocument).chooseName(attr='sample.pdf')
     1250        file_id = IFileStoreNameChooser(pdfdocument).chooseName(
     1251            attr='sample.pdf')
    12191252        fs = ExtFileStore(root=self.dc_root)
    12201253        pdffile = open(SAMPLE_PDF, 'rb')
    12211254        fs.createFile(file_id, pdffile)
    1222         docid = [i for i in self.customer['documents'].keys() if len(i) > 10][0]
     1255        docid = [i for i in self.customer['documents'].keys()
     1256                 if len(i) > 10][0]
    12231257        self.browser.open(self.customer_path + '/documents/' + docid)
    12241258        self.browser.getLink("Download document slip").click()
    12251259        self.assertEqual(self.browser.headers['Status'], '200 Ok')
    1226         self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
     1260        self.assertEqual(
     1261            self.browser.headers['Content-Type'], 'application/pdf')
    12271262        path = os.path.join(samples_dir(), 'pdfdocument_slip.pdf')
    12281263        open(path, 'wb').write(self.browser.contents)
     
    12321267        # A proper file name chooser is registered for customer documents.
    12331268        # This is not a UI test. It's just a functional test.
    1234         file_id = IFileStoreNameChooser(self.document).chooseName(attr='sample')
     1269        file_id = IFileStoreNameChooser(self.document).chooseName(
     1270            attr='sample')
    12351271        fs = ExtFileStore(root=self.dc_root)
    12361272        fs.createFile(file_id, StringIO('my sample 1'))
    12371273        result = fs.getFileByContext(self.document, attr='sample')
    1238         self.assertEqual(file_id, '__file-customerdocument__01000/K1000000/sample_DOC1_K1000000')
     1274        self.assertEqual(
     1275            file_id, '__file-customerdocument__01000/'
     1276            'K1000000/sample_DOC1_K1000000')
    12391277        self.assertEqual(result.read(), 'my sample 1')
    1240         self.assertEqual(self.document.connected_files[0][1].read(), 'my sample 1')
     1278        self.assertEqual(
     1279            self.document.connected_files[0][1].read(), 'my sample 1')
    12411280        self.document.setMD5()
    1242         self.assertEqual(self.document.sample_md5, 'a406995ee8eb6772bacf51aa4b0caa24')
     1281        self.assertEqual(
     1282            self.document.sample_md5, 'a406995ee8eb6772bacf51aa4b0caa24')
    12431283        return
    12441284
     
    12461286class ContractUITests(CustomersFullSetup):
    12471287    # Tests for contract related views and pages
     1288
     1289    never_ending_button_text = (
     1290        'Select payment method (final submission)')
     1291
     1292    def add_product_option(self, contract):
     1293        prodoption = ProductOption()
     1294        prodoption.title = u'Any product option'
     1295        prodoption.fee = Decimal('88.8')
     1296        prodoption.currency = 'EUR'
     1297        contract.product_options = [prodoption, ]
     1298
     1299    def prepare_payment_select(self):
     1300        IWorkflowState(self.customer).setState('approved')
     1301        IWorkflowState(self.document).setState('verified')
     1302        self.contract.document_object = self.document
     1303        self.add_product_option(self.contract)
     1304        IWorkflowState(self.contract).setState('created')
     1305        # login as customer
     1306        self.browser.open(self.login_path)
     1307        self.browser.getControl(name="form.login").value = self.customer_id
     1308        self.browser.getControl(name="form.password").value = 'cpwd'
     1309        self.browser.getControl("Login").click()
    12481310
    12491311    def test_manage_contract(self):
     
    12611323        self.browser.getControl("Add contract").click()
    12621324        self.assertTrue('Sample Contract added.' in self.browser.contents)
    1263         conid = [i for i in self.customer['contracts'].keys() if len(i) > 10][0]
     1325        conid = [i for i in self.customer['contracts'].keys()
     1326                 if len(i) > 10][0]
    12641327        contract = self.customer['contracts'][conid]
    12651328        self.assertEqual(
    1266             self.browser.url, self.contracts_path + '/%s/selectproduct' % conid)
     1329            self.browser.url,
     1330            self.contracts_path + '/%s/selectproduct' % conid)
    12671331        # SAM is in the correct contract_category
    12681332        self.assertTrue('<option value="SAM">' in self.browser.contents)
    12691333        # So far last_product_id is None.
    1270         self.assertTrue(self.customer['contracts'][conid].last_product_id is None)
     1334        self.assertTrue(
     1335            self.customer['contracts'][conid].last_product_id is None)
    12711336        self.browser.getControl(name="form.product_object").value = ['SAM']
    12721337        self.browser.getControl("Save and proceed").click()
     
    12741339            self.browser.url, self.contracts_path + '/%s/manage' % conid)
    12751340        self.browser.getLink("View").click()
    1276         self.assertEqual(self.browser.url, self.contracts_path + '/%s/index' % conid)
     1341        self.assertEqual(
     1342            self.browser.url, self.contracts_path + '/%s/index' % conid)
    12771343        self.assertEqual(contract.tc_dict, {'en': u'Hello world'})
    12781344
     
    13421408        self.assertFalse('Add contract' in self.browser.contents)
    13431409        self.browser.open(self.contracts_path + '/addcontract')
    1344         self.assertTrue('The requested form is locked' in self.browser.contents)
     1410        self.assertTrue(
     1411            'The requested form is locked' in self.browser.contents)
    13451412        IWorkflowState(self.customer).setState(APPROVED)
    13461413        self.browser.open(self.contracts_path)
     
    13501417        self.browser.getControl("Add contract").click()
    13511418        self.assertTrue('Sample Contract added.' in self.browser.contents)
    1352         conid = [i for i in self.customer['contracts'].keys() if len(i) > 10][0]
     1419        conid = [i for i in self.customer['contracts'].keys()
     1420                 if len(i) > 10][0]
    13531421        contract = self.customer['contracts'][conid]
    13541422        self.assertEqual(
    1355             self.browser.url, self.contracts_path + '/%s/selectproduct' % conid)
     1423            self.browser.url,
     1424            self.contracts_path + '/%s/selectproduct' % conid)
    13561425        # SAM is in the correct contract_category ...
    13571426        self.assertTrue('<option value="SAM">' in self.browser.contents)
     
    13591428        self.assertFalse('<option value="LIC">' in self.browser.contents)
    13601429        # So far last_product_id is None.
    1361         self.assertTrue(self.customer['contracts'][conid].last_product_id is None)
     1430        self.assertTrue(
     1431            self.customer['contracts'][conid].last_product_id is None)
    13621432        self.browser.getControl(name="form.product_object").value = ['SAM']
    13631433        self.browser.getControl("Save and proceed").click()
     
    13661436        # Document is a required field on edit form page.
    13671437        self.browser.getControl("Save").click()
    1368         self.assertTrue('Document: <span class="error">Required input is missing.</span>'
     1438        self.assertTrue(
     1439            'Document: <span class="error">Required input is missing.</span>'
    13691440            in self.browser.contents)
    13701441        # But our document can't be selected because it's not submitted
     
    13761447        # After saving the form, last_product_id and other attributes are set
    13771448        self.assertTrue('Form has been saved.' in self.browser.contents)
    1378         self.assertEqual(self.customer['contracts'][conid].last_product_id, 'SAM')
     1449        self.assertEqual(
     1450            self.customer['contracts'][conid].last_product_id, 'SAM')
    13791451        self.assertEqual(contract.title, 'Our Sample Product')
    13801452        self.assertEqual(contract.product_object, self.product)
     
    13821454        # Saving the form again does not unset last_product_id
    13831455        self.browser.getControl("Save").click()
    1384         self.assertEqual(self.customer['contracts'][conid].last_product_id, 'SAM')
     1456        self.assertEqual(
     1457            self.customer['contracts'][conid].last_product_id, 'SAM')
    13851458        self.assertTrue('Form has been saved.' in self.browser.contents)
    13861459        # So far we have not yet set product options.
     
    13901463        prodoption.fee = Decimal('88.8')
    13911464        prodoption.currency = 'EUR'
    1392         contract.product_options = [prodoption,]
     1465        contract.product_options = [prodoption, ]
    13931466        self.browser.open(self.contracts_path + '/%s/edit' % conid)
    13941467        # We can see both the stored and the recent product options
    13951468        # from the chosen product.
    1396         self.assertTrue('<option selected="selected" value="Any product option">'
    1397                         'Any product option @ 88.8 Euro</option>'
    1398                         in self.browser.contents)
     1469        self.assertTrue(
     1470            '<option selected="selected" value="Any product option">'
     1471            'Any product option @ 88.8 Euro</option>'
     1472            in self.browser.contents)
    13991473        self.assertTrue('<option value="First option">First option '
    14001474                        '@ 99.9 US Dollar</option>' in self.browser.contents)
     
    14021476        self.browser.getControl(
    14031477            name="form.product_options.0.").value = ['First option']
    1404         self.assertEqual(contract.product_options[0].title, 'Any product option')
     1478        self.assertEqual(
     1479            contract.product_options[0].title, 'Any product option')
    14051480        self.browser.getControl("Save").click()
    14061481        self.assertEqual(contract.product_options[0].title, 'First option')
    14071482        self.browser.getLink("View").click()
    1408         self.assertTrue('<span>First option @ 99.9 US Dollar</span>' in self.browser.contents)
    1409         self.assertEqual(self.browser.url, self.contracts_path + '/%s/index' % conid)
     1483        self.assertTrue(
     1484            '<span>First option @ 99.9 US Dollar</span>'
     1485            in self.browser.contents)
     1486        self.assertEqual(
     1487            self.browser.url, self.contracts_path + '/%s/index' % conid)
    14101488        # An href attribute is referring to the document and product objects
    14111489        self.assertTrue('<a href="http://localhost/app/products/SAM">SAM -'
    14121490            in self.browser.contents)
    14131491        self.assertTrue(
    1414             '<a href="http://localhost/app/customers/K1000000/documents/DOC1">DOC1 -'
     1492            '<a href="http://localhost/app/customers/K1000000/'
     1493            'documents/DOC1">DOC1 -'
    14151494            in self.browser.contents)
    14161495        # Customer can submit the form if confirmation box is ticket.
     
    14181497        self.browser.getLink("Edit").click()
    14191498        self.browser.getControl("Proceed to checkout").click()
    1420         self.assertTrue('confirm your acceptance of these by ticking' in self.browser.contents)
     1499        self.assertTrue(
     1500            'confirm your acceptance of these by ticking'
     1501            in self.browser.contents)
    14211502        self.assertEqual(contract.state, 'created')
    14221503        self.browser.getControl(name="confirm_tc").value = True
    14231504        self.browser.getControl("Proceed to checkout").click()
    14241505        self.assertEqual(contract.state, 'created')
     1506        radio_ctrl = self.browser.getControl(name='gw')
     1507        radio_ctrl.value = [radio_ctrl.options[0]]  # pick first payment opt
    14251508        self.browser.getControl("Select payment method").click()
    14261509        self.assertEqual(contract.state, 'awaiting')
    14271510        # Customer can't edit the contract once it has been submitted
    14281511        self.browser.open(self.contracts_path + '/%s/edit' % conid)
    1429         self.assertTrue('The requested form is locked' in self.browser.contents)
     1512        self.assertTrue(
     1513            'The requested form is locked' in self.browser.contents)
    14301514
    14311515    def test_view_slips(self):
     
    14351519        self.browser.getLink("Download contracts overview").click()
    14361520        self.assertEqual(self.browser.headers['Status'], '200 Ok')
    1437         self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
     1521        self.assertEqual(
     1522            self.browser.headers['Content-Type'], 'application/pdf')
    14381523        path = os.path.join(samples_dir(), 'contracts_overview_slip.pdf')
    14391524        open(path, 'wb').write(self.browser.contents)
     
    14511536        self.browser.getLink("Download contract slip").click()
    14521537        self.assertEqual(self.browser.headers['Status'], '200 Ok')
    1453         self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
     1538        self.assertEqual(
     1539            self.browser.headers['Content-Type'], 'application/pdf')
    14541540        path = os.path.join(samples_dir(), 'contract_slip.pdf')
    14551541        open(path, 'wb').write(self.browser.contents)
     
    14861572        # InvalidTransitionError is catched
    14871573        self.assertTrue(
    1488             '<div class="alert alert-warning">Attached documents must be verified first.</div>'
     1574            '<div class="alert alert-warning">Attached documents '
     1575            'must be verified first.</div>'
    14891576            in self.browser.contents)
    14901577        self.browser.open(self.contracts_path + '/CON1/trigtrans')
     
    14931580        self.browser.getControl("Apply").click()
    14941581        self.assertEqual(IWorkflowState(self.contract).getState(), 'approved')
     1582
     1583    def test_select_payment(self):
     1584        # select payment
     1585        self.prepare_payment_select()
     1586        self.browser.open('%s/CON1/edit' % self.contracts_path)
     1587        self.browser.getControl("Proceed to checkout").click()
     1588        self.assertTrue(
     1589            "Select payment method" in self.browser.contents)
     1590        self.assertTrue(
     1591            'Credit Card (Demo Payments)' in self.browser.contents)
     1592
     1593    def test_select_payment_no_choice(self):
     1594        # we get warned if no payment was selected
     1595        self.prepare_payment_select()
     1596        self.browser.open(
     1597            '%s/CON1/select_payment_method' % self.contracts_path)
     1598        self.browser.getControl(self.never_ending_button_text).click()
     1599        self.assertTrue(
     1600            'Please pick a payment method' in self.browser.contents)
     1601
     1602    def test_select_payment_demo_provider(self):
     1603        # we can proceed with payments if we select a payment method
     1604        self.prepare_payment_select()
     1605        self.browser.open(
     1606            '%s/CON1/select_payment_method' % self.contracts_path)
     1607        radio_ctrl = self.browser.getControl(name='gw')
     1608        radio_ctrl.displayValue = ['Credit Card (Demo Payments)']
     1609        self.browser.getControl(self.never_ending_button_text).click()
  • main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_contract.py

    r12259 r12741  
    1919Tests for contracts.
    2020"""
     21import decimal
    2122from zope.interface.verify import verifyClass, verifyObject
    22 from zope.component import createObject
     23from zope.component import createObject, getUtility, getUtilitiesFor
     24from zope.component.hooks import setSite
    2325from hurry.workflow.interfaces import (
    2426    IWorkflowInfo, IWorkflowState, InvalidTransitionError)
    2527from waeup.ikoba.customers.interfaces import (
    2628    IContractsContainer, IContract)
    27 from waeup.ikoba.interfaces import IObjectHistory
    2829from waeup.ikoba.customers.contracts import (
    29     ContractsContainer, SampleContract)
     30    ContractsContainer, SampleContract, ContractPayer, ContractFinder,
     31    PayableContract,
     32    )
     33from waeup.ikoba.app import Company
     34from waeup.ikoba.customers.customer import Customer
     35from waeup.ikoba.payments.interfaces import (
     36    IPaymentItem, IPayer, IPayableFinder, IPayable,
     37    )
     38from waeup.ikoba.products.productoptions import ProductOption
    3039from waeup.ikoba.testing import (FunctionalLayer, FunctionalTestCase)
     40
    3141
    3242class ContractsContainerTestCase(FunctionalTestCase):
     
    8999        self.assertTrue('Contract created by system' in messages)
    90100        return
     101
     102
     103class TestContractHelpers(FunctionalTestCase):
     104
     105    layer = FunctionalLayer
     106
     107    def test_payer_adapter(self):
     108        # we can adapt IContract to IPayer (i.e. create a payer)
     109        customer = Customer()
     110        customer.firstname, customer.lastname = u'Anna', u'Tester'
     111        contract = createObject(u'waeup.SampleContract')
     112        customer['contracts'] = ContractsContainer()
     113        customer['contracts'].addContract(contract)
     114        result = IPayer(contract)
     115        self.assertTrue(isinstance(result, ContractPayer))
     116        verifyObject(IPayer, result)
     117        self.assertEqual(result.first_name, u'Anna')
     118        self.assertEqual(result.last_name, u'Tester')
     119        self.assertEqual(result.payer_id, customer.customer_id)
     120
     121    def test_contract_finder_iface(self):
     122        # we have a contract finder that returns IPayableFinder data.
     123        verifyClass(IPayableFinder, ContractFinder)
     124
     125    def test_contract_finder_registered(self):
     126        # the contract finder is a utility registered on startup
     127        util = getUtility(IPayableFinder, name='contracts_finder')
     128        self.assertTrue(isinstance(util, ContractFinder))
     129        utils = [util for name, util in getUtilitiesFor(IPayableFinder)
     130                 if isinstance(util, ContractFinder)]
     131        self.assertEqual(len(utils), 1)
     132
     133    def create_contract_and_site(self):
     134        contract = SampleContract()
     135        option1 = ProductOption(u"Fee 1", decimal.Decimal("31.10"), "USD")
     136        option2 = ProductOption(u"Fee 2", decimal.Decimal("12.12"), "USD")
     137        contract.product_options = [option1, option2]
     138        contract.contract_id = u'CON1234'
     139        self.getRootFolder()['app'] = Company()
     140        app = self.getRootFolder()['app']
     141        setSite(app)
     142        return contract, app
     143
     144    def test_contract_finder(self):
     145        # the contract finder can really find contracts
     146        contract, app = self.create_contract_and_site()
     147        app['mycontract'] = contract  # trigger cataloging
     148        finder = ContractFinder()
     149        result = finder.get_payable_by_id('CON1234')
     150        self.assertTrue(result is contract)
     151
     152    def test_contract_finder_not_stored(self):
     153        # we get none if an id is not stored
     154        contract, app = self.create_contract_and_site()
     155        app['mycontract'] = contract  # trigger cataloging
     156        finder = ContractFinder()
     157        result = finder.get_payable_by_id('Not-a-valid-id')
     158        self.assertTrue(result is None)
     159
     160    def test_contract_finder_no_catalog(self):
     161        # contract finder does not complain about missing catalog
     162        finder = ContractFinder()
     163        result = finder.get_payable_by_id('CON1234')
     164        self.assertTrue(result is None)
     165
     166
     167class TestContractAsPayable(FunctionalTestCase):
     168
     169    layer = FunctionalLayer
     170
     171    def test_adaptable(self):
     172        # we can turn contracts into payables.
     173        contract = SampleContract()
     174        payable = IPayable(contract)
     175        self.assertTrue(payable is not None)
     176        self.assertTrue(isinstance(payable, PayableContract))
     177
     178    def test_payable_iface(self):
     179        # PayableContracts really provide IPayable
     180        contract = SampleContract()
     181        option1 = ProductOption(u"Fee 1", decimal.Decimal("31.10"), "USD")
     182        option2 = ProductOption(u"Fee 2", decimal.Decimal("12.12"), "USD")
     183        contract.product_options = [option1, option2]
     184        payable = PayableContract(contract)
     185        verifyObject(IPayable, payable)
     186        verifyClass(IPayable, PayableContract)
     187
     188    def test_payable_simple_attributes(self):
     189        # the simple attribs are set correctly, according to context contract
     190        contract = SampleContract()
     191        contract.title = u'the title'
     192        option1 = ProductOption(u"Fee 1", decimal.Decimal("31.10"), "EUR")
     193        option2 = ProductOption(u"Fee 2", decimal.Decimal("12.12"), "EUR")
     194        contract.product_options = [option1, option2]
     195        payable = PayableContract(contract)
     196        self.assertTrue(contract.contract_id, payable.payable_id)
     197        self.assertEqual(payable.title, contract.title)
     198        self.assertEqual(payable.currency, 'EUR')
     199
     200    def test_payable_items(self):
     201        # we can get payment items from payable
     202        contract = SampleContract()
     203        option1 = ProductOption(u"Fee 1", decimal.Decimal("31.10"), "EUR")
     204        option2 = ProductOption(u"Fee 2", decimal.Decimal("12.12"), "EUR")
     205        contract.product_options = [option1, option2]
     206        payable = PayableContract(contract)
     207        items = payable.payment_items
     208        self.assertTrue(isinstance(items, tuple))
     209        self.assertEqual(len(items), 2)
     210        verifyObject(IPaymentItem, items[0])
     211        verifyObject(IPaymentItem, items[1])
     212        self.assertEqual(items[0].item_id, '0')
     213        self.assertEqual(items[0].title, u'Fee 1')
     214        self.assertEqual(items[0].amount, decimal.Decimal("31.10"))
     215
     216    def test_payable_no_items(self):
     217        # payables work also with no options set on contract
     218        contract = SampleContract()
     219        payable = PayableContract(contract)
     220        items = payable.payment_items
     221        self.assertTrue(isinstance(items, tuple))
     222        self.assertEqual(len(items), 0)
     223        self.assertEqual(payable.currency, None)
     224
     225    def test_different_currencies_forbiddedn(self):
     226        # we do not accept different currencies in payment items
     227        contract = SampleContract()
     228        option1 = ProductOption(u"Fee 1", decimal.Decimal("31.10"), "EUR")
     229        option2 = ProductOption(u"Fee 2", decimal.Decimal("12.12"), "USD")
     230        contract.product_options = [option1, option2]
     231        self.assertRaises(ValueError, PayableContract, contract)
  • main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_customer.py

    r12297 r12741  
    2121import re
    2222import unittest
    23 import grok
    2423from cStringIO import StringIO
    25 from datetime import tzinfo
    26 from zope.component import getUtility, queryUtility, createObject
    27 from zope.catalog.interfaces import ICatalog
     24from zope.component import getUtility, getUtilitiesFor
     25from zope.component.hooks import setSite
    2826from zope.component.interfaces import IFactory
    29 from zope.event import notify
    3027from zope.interface import verify
    31 from zope.schema.interfaces import RequiredMissing
    3228from waeup.ikoba.interfaces import IExtFileStore, IFileStoreNameChooser
     29from waeup.ikoba.app import Company
    3330from waeup.ikoba.customers.customer import (
    34     Customer, CustomerFactory, handle_customer_removed, path_from_custid)
     31    Customer, CustomerFactory, handle_customer_removed, path_from_custid,
     32    CustomerPayer, CustomerFinder,
     33    )
    3534from waeup.ikoba.customers.interfaces import (
    3635    ICustomer, ICustomerNavigation, ICustomersUtils)
    3736from waeup.ikoba.customers.tests.test_batching import CustomerImportExportSetup
     37from waeup.ikoba.payments.interfaces import IPayer, IPayerFinder
    3838from waeup.ikoba.testing import FunctionalLayer, FunctionalTestCase
     39
    3940
    4041class HelperTests(unittest.TestCase):
     
    5960            path_from_custid('KM123456'), u'00120/KM123456')
    6061        return
     62
    6163
    6264class CustomerTest(FunctionalTestCase):
     
    9294
    9395
     96class TestCustomerHelpers(FunctionalTestCase):
     97
     98    layer = FunctionalLayer
     99
     100    def test_payer_adapter(self):
     101        # we can adapt ICustomer to IPayer (i.e. create a payer)
     102        customer = Customer()
     103        customer.firstname, customer.lastname = u'Anna', u'Tester'
     104        result = IPayer(customer)
     105        self.assertTrue(isinstance(result, CustomerPayer))
     106        verify.verifyObject(IPayer, result)
     107        self.assertEqual(result.first_name, u'Anna')
     108        self.assertEqual(result.last_name, u'Tester')
     109        self.assertEqual(result.payer_id, customer.customer_id)
     110
     111    def test_customer_finder_iface(self):
     112        # we have a customer finder that returns IPayableFinder data.
     113        verify.verifyClass(IPayerFinder, CustomerFinder)
     114
     115    def test_customer_finder_registered(self):
     116        # the customer finder is a utility registered on startup
     117        util = getUtility(IPayerFinder, name='customer_finder')
     118        self.assertTrue(isinstance(util, CustomerFinder))
     119        utils = [util for name, util in getUtilitiesFor(IPayerFinder)
     120                 if isinstance(util, CustomerFinder)]
     121        self.assertEqual(len(utils), 1)
     122
     123    def create_customer_and_site(self):
     124        customer = Customer()
     125        customer.customer_id = u'CUST1'
     126        self.getRootFolder()['app'] = Company()
     127        app = self.getRootFolder()['app']
     128        setSite(app)
     129        return customer, app
     130
     131    def test_customer_finder(self):
     132        # the customer finder can really find customers
     133        customer, app = self.create_customer_and_site()
     134        app['mycustomer'] = customer  # trigger cataloging
     135        finder = CustomerFinder()
     136        result = finder.get_payer_by_id('CUST1')
     137        self.assertTrue(result is customer)
     138
     139    def test_customer_finder_not_stored(self):
     140        # we get none if an id is not stored
     141        customer, app = self.create_customer_and_site()
     142        app['mycustomer'] = customer  # trigger cataloging
     143        finder = CustomerFinder()
     144        result = finder.get_payer_by_id('Not-a-valid-id')
     145        self.assertTrue(result is None)
     146
     147    def test_customer_finder_no_catalog(self):
     148        # customer finder does not complain about missing catalog
     149        finder = CustomerFinder()
     150        result = finder.get_payer_by_id('CUST1')
     151        self.assertTrue(result is None)
     152
     153
    94154class CustomerRemovalTests(CustomerImportExportSetup):
    95155    # Test handle_customer_removed
     
    192252        return
    193253
     254
    194255class CustomerFactoryTest(FunctionalTestCase):
    195256
Note: See TracChangeset for help on using the changeset viewer.