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
Line 
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"""
19Customer contract components.
20"""
21import grok
22from zope.component import getUtility
23from zope.component.interfaces import IFactory
24from zope.interface import implementedBy
25from zope.schema import getFields
26from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
27from waeup.ikoba.interfaces import MessageFactory as _
28from waeup.ikoba.interfaces import (
29    IObjectHistory, VERIFIED, APPROVED, PROVISIONALLY, IIDSource)
30from waeup.ikoba.customers.interfaces import (
31    IContractsContainer, ICustomerNavigation, IContract,
32    IContractSelectProduct, ICustomersUtils, ISampleContract,
33    ISampleContractProcess, ISampleContractEdit, ISampleContractOfficialUse)
34from waeup.ikoba.payments.interfaces import IPayer
35from waeup.ikoba.payments.payment import PaymentItem
36from waeup.ikoba.utils.helpers import attrs_to_fields
37
38
39class ContractsContainer(grok.Container):
40    """This is a container for customer contracts.
41    """
42    grok.implements(IContractsContainer, ICustomerNavigation)
43    grok.provides(IContractsContainer)
44
45    def addContract(self, contract):
46        if not IContract.providedBy(contract):
47            raise TypeError(
48                'ContractsContainers contain only IContract instances')
49        self[contract.contract_id] = contract
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
59ContractsContainer = attrs_to_fields(ContractsContainer)
60
61
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
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
101class ContractBase(grok.Container):
102    """This is a customer contract baseclass.
103    """
104    grok.implements(IContractSelectProduct)  # Neccessary for the
105                                             # selectproduct page (why?)
106    grok.baseclass()
107
108    def __init__(self):
109        super(ContractBase, self).__init__()
110        # The site doesn't exist in unit tests
111        source = getUtility(IIDSource)
112        self.contract_id = unicode(source.get_hex_uuid())
113        self.last_product_id = None
114        self.tc_dict = {}
115        self.title = None
116        self.valid_to = None
117        self.valid_from = None
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(
133                ICustomersUtils).TRANSLATED_CONTRACT_STATES
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:
145            return self.history.messages[-1].split(' - ')[0]
146        except IndexError:
147            return
148
149    @property
150    def customer(self):
151        try:
152            return self.__parent__.__parent__
153        except AttributeError:
154            return None
155
156    @property
157    def user_id(self):
158        if self.customer is not None:
159            return self.customer.customer_id
160        return
161
162    def writeLogMessage(self, view, message):
163        return self.__parent__.__parent__.writeLogMessage(view, message)
164
165    @property
166    def is_editable(self):
167        try:
168            # Customer must have requested
169            cond1 = self.customer.state in getUtility(
170                ICustomersUtils).CONMANAGE_CUSTOMER_STATES
171            # Contract must be in state created
172            cond2 = self.state in getUtility(
173                ICustomersUtils).CONMANAGE_CONTRACT_STATES
174            if not (cond1 and cond2):
175                return False
176        except AttributeError:
177            pass
178        return True
179
180    @property
181    def is_approvable(self):
182        if self.customer and not self.customer.state in (
183            APPROVED, PROVISIONALLY):
184            return False, _("Customer has not yet been approved.")
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:
190                    return False, _(
191                        "Attached documents must be verified first.")
192        return True, None
193
194    @property
195    def translated_class_name(self):
196        try:
197            CONTYPES_DICT = getUtility(ICustomersUtils).CONTYPES_DICT
198            return CONTYPES_DICT[self.class_name]
199        except KeyError:
200            return
201
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
211
212
213class SampleContract(ContractBase):
214    """This is a sample contract.
215    """
216
217    grok.implements(
218        ISampleContractProcess,  # must come before ISampleContract
219        ISampleContract,
220        ISampleContractEdit,
221        ICustomerNavigation)
222
223    contract_category = 'sample'
224
225    form_fields_interface = ISampleContract
226
227    edit_form_fields_interface = ISampleContractEdit
228
229    ou_form_fields_interface = ISampleContractOfficialUse
230
231    check_docs_interface = ISampleContract
232
233SampleContract = attrs_to_fields(SampleContract)
234
235
236# Contracts must be importable. So we might need a factory.
237class SampleContractFactory(grok.GlobalUtility):
238    """A factory for contracts.
239    """
240    grok.implements(IFactory)
241    grok.name(u'waeup.SampleContract')
242    title = u"Create a new contract.",
243    description = u"This factory instantiates new sample contract instances."
244
245    def __call__(self, *args, **kw):
246        return SampleContract(*args, **kw)
247
248    def getInterfaces(self):
249        return implementedBy(SampleContract)
250
251
252@grok.subscribe(IContract, grok.IObjectAddedEvent)
253def handle_contract_added(contract, event):
254    """If an contract is added the transition create is fired.
255    The latter produces a logging message.
256    """
257    if IWorkflowState(contract).getState() is None:
258        IWorkflowInfo(contract).fireTransition('create')
259    return
Note: See TracBrowser for help on using the repository browser.