source: main/waeup.ikoba/branches/uli-payments/src/waeup/ikoba/customers/contracts.py @ 12703

Last change on this file since 12703 was 12698, checked in by uli, 10 years ago

Add customer attribute.

  • Property svn:keywords set to Id
File size: 7.7 KB
RevLine 
[12089]1## $Id: contracts.py 12698 2015-03-09 02:04:06Z uli $
2##
3## Copyright (C) 2014 Uli Fouquet & Henrik Bettermann
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
8##
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13##
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18"""
[12097]19Customer contract components.
[12089]20"""
21import grok
[12681]22from zope.component import getUtility
[12089]23from zope.component.interfaces import IFactory
24from zope.interface import implementedBy
[12144]25from zope.schema import getFields
[12089]26from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
27from waeup.ikoba.interfaces import MessageFactory as _
[12258]28from waeup.ikoba.interfaces import (
[12681]29    IObjectHistory, VERIFIED, APPROVED, PROVISIONALLY, IIDSource)
[12089]30from waeup.ikoba.customers.interfaces import (
[12681]31    IContractsContainer, ICustomerNavigation, IContract,
32    IContractSelectProduct, ICustomersUtils, ISampleContract,
33    ISampleContractProcess, ISampleContractEdit, ISampleContractOfficialUse)
[12697]34from waeup.ikoba.payments.interfaces import IPayer
[12683]35from waeup.ikoba.payments.payment import PaymentItem
[12089]36from waeup.ikoba.utils.helpers import attrs_to_fields
37
[12681]38
[12097]39class ContractsContainer(grok.Container):
40    """This is a container for customer contracts.
[12089]41    """
[12097]42    grok.implements(IContractsContainer, ICustomerNavigation)
43    grok.provides(IContractsContainer)
[12089]44
[12097]45    def addContract(self, contract):
46        if not IContract.providedBy(contract):
[12089]47            raise TypeError(
[12097]48                'ContractsContainers contain only IContract instances')
49        self[contract.contract_id] = contract
[12089]50        return
51
52    @property
53    def customer(self):
54        return self.__parent__
55
56    def writeLogMessage(self, view, message):
57        return self.__parent__.writeLogMessage(view, message)
58
[12097]59ContractsContainer = attrs_to_fields(ContractsContainer)
[12089]60
[12681]61
[12683]62def payment_items_from_contract(contract):
63    """Turn contract product options into payment items.
64    """
65    result = []
66    for num, option in enumerate(contract.product_options):
67        item = PaymentItem()
68        item.item_id = u'%s' % num
69        item.title = option.title
70        item.amount = option.fee
71        result.append(item)
72    return result
73
74
[12697]75class ContractPayer(grok.Adapter):
76    """Adapter to turn contracts into IPayers.
77    """
78    grok.implements(IPayer)
79    grok.context(IContract)
80
81    def __init__(self, context):
82        self.context = context
83
84    @property
85    def _customer(self):
86        return self.context.customer
87
88    @property
89    def first_name(self):
90        return getattr(self._customer, 'firstname', None)
91
92    @property
93    def last_name(self):
94        return getattr(self._customer, 'lastname', None)
95
96    @property
97    def payer_id(self):
98        return getattr(self._customer, 'customer_id', None)
99
100
[12097]101class ContractBase(grok.Container):
102    """This is a customer contract baseclass.
[12089]103    """
[12681]104    grok.implements(IContractSelectProduct)  # Neccessary for the
105                                             # selectproduct page (why?)
[12089]106    grok.baseclass()
107
108    def __init__(self):
[12097]109        super(ContractBase, self).__init__()
[12089]110        # The site doesn't exist in unit tests
[12258]111        source = getUtility(IIDSource)
112        self.contract_id = unicode(source.get_hex_uuid())
[12094]113        self.last_product_id = None
[12363]114        self.tc_dict = {}
[12580]115        self.title = None
[12633]116        self.valid_to = None
117        self.valid_from = None
[12089]118        return
119
120    @property
121    def history(self):
122        history = IObjectHistory(self)
123        return history
124
125    @property
126    def state(self):
127        return IWorkflowState(self).getState()
128
129    @property
130    def translated_state(self):
131        try:
132            TRANSLATED_STATES = getUtility(
[12097]133                ICustomersUtils).TRANSLATED_CONTRACT_STATES
[12089]134            return TRANSLATED_STATES[self.state]
135        except KeyError:
136            return
137
138    @property
139    def class_name(self):
140        return self.__class__.__name__
141
142    @property
143    def formatted_transition_date(self):
144        try:
[12210]145            return self.history.messages[-1].split(' - ')[0]
146        except IndexError:
[12089]147            return
148
149    @property
150    def customer(self):
151        try:
152            return self.__parent__.__parent__
153        except AttributeError:
154            return None
155
[12289]156    @property
157    def user_id(self):
158        if self.customer is not None:
159            return self.customer.customer_id
160        return
161
[12089]162    def writeLogMessage(self, view, message):
163        return self.__parent__.__parent__.writeLogMessage(view, message)
164
165    @property
[12337]166    def is_editable(self):
[12089]167        try:
[12573]168            # Customer must have requested
[12089]169            cond1 = self.customer.state in getUtility(
[12099]170                ICustomersUtils).CONMANAGE_CUSTOMER_STATES
[12097]171            # Contract must be in state created
[12089]172            cond2 = self.state in getUtility(
[12099]173                ICustomersUtils).CONMANAGE_CONTRACT_STATES
[12089]174            if not (cond1 and cond2):
175                return False
176        except AttributeError:
177            pass
178        return True
179
180    @property
[12144]181    def is_approvable(self):
[12573]182        if self.customer and not self.customer.state in (
183            APPROVED, PROVISIONALLY):
[12352]184            return False, _("Customer has not yet been approved.")
[12144]185        for key, field in getFields(self.check_docs_interface).items():
186            if key.endswith('_object'):
187                obj = getattr(self, key, None)
188                state = getattr(obj, 'state', None)
189                if state and state != VERIFIED:
[12681]190                    return False, _(
191                        "Attached documents must be verified first.")
[12168]192        return True, None
[12144]193
194    @property
[12089]195    def translated_class_name(self):
196        try:
[12099]197            CONTYPES_DICT = getUtility(ICustomersUtils).CONTYPES_DICT
198            return CONTYPES_DICT[self.class_name]
[12089]199        except KeyError:
200            return
201
[12663]202    @property
203    def fee_based(self):
204        if self.product_options:
205            amount = 0
206            for option in self.product_options:
207                amount += option.fee
208            if amount:
209                return True
210        return False
[12089]211
[12663]212
[12097]213class SampleContract(ContractBase):
214    """This is a sample contract.
[12089]215    """
216
[12333]217    grok.implements(
[12681]218        ISampleContractProcess,  # must come before ISampleContract
[12333]219        ISampleContract,
220        ISampleContractEdit,
221        ICustomerNavigation)
[12103]222
[12097]223    contract_category = 'sample'
[12092]224
[12103]225    form_fields_interface = ISampleContract
226
227    edit_form_fields_interface = ISampleContractEdit
228
[12500]229    ou_form_fields_interface = ISampleContractOfficialUse
230
[12144]231    check_docs_interface = ISampleContract
232
[12097]233SampleContract = attrs_to_fields(SampleContract)
[12089]234
235
[12097]236# Contracts must be importable. So we might need a factory.
237class SampleContractFactory(grok.GlobalUtility):
238    """A factory for contracts.
[12089]239    """
240    grok.implements(IFactory)
[12097]241    grok.name(u'waeup.SampleContract')
242    title = u"Create a new contract.",
243    description = u"This factory instantiates new sample contract instances."
[12089]244
245    def __call__(self, *args, **kw):
[12097]246        return SampleContract(*args, **kw)
[12089]247
248    def getInterfaces(self):
[12097]249        return implementedBy(SampleContract)
[12089]250
[12681]251
[12097]252@grok.subscribe(IContract, grok.IObjectAddedEvent)
253def handle_contract_added(contract, event):
254    """If an contract is added the transition create is fired.
[12090]255    The latter produces a logging message.
256    """
[12097]257    if IWorkflowState(contract).getState() is None:
258        IWorkflowInfo(contract).fireTransition('create')
[12090]259    return
Note: See TracBrowser for help on using the repository browser.