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

Last change on this file since 12731 was 12728, checked in by uli, 10 years ago

Remove obsolete func.

  • Property svn:keywords set to Id
File size: 9.1 KB
Line 
1## $Id: contracts.py 12728 2015-03-11 10:16:58Z 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.catalog.interfaces import ICatalog
23from zope.component import getUtility, queryUtility
24from zope.component.interfaces import IFactory
25from zope.interface import implementedBy
26from zope.schema import getFields
27from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
28from waeup.ikoba.interfaces import MessageFactory as _
29from waeup.ikoba.interfaces import (
30    IObjectHistory, VERIFIED, APPROVED, PROVISIONALLY, IIDSource)
31from waeup.ikoba.customers.interfaces import (
32    IContractsContainer, ICustomerNavigation, IContract,
33    IContractSelectProduct, ICustomersUtils, ISampleContract,
34    ISampleContractProcess, ISampleContractEdit, ISampleContractOfficialUse)
35from waeup.ikoba.payments.interfaces import IPayer, IPayableFinder, IPayable
36from waeup.ikoba.payments.payment import PaymentItem
37from waeup.ikoba.utils.helpers import attrs_to_fields
38
39
40class ContractsContainer(grok.Container):
41    """This is a container for customer contracts.
42    """
43    grok.implements(IContractsContainer, ICustomerNavigation)
44    grok.provides(IContractsContainer)
45
46    def addContract(self, contract):
47        if not IContract.providedBy(contract):
48            raise TypeError(
49                'ContractsContainers contain only IContract instances')
50        self[contract.contract_id] = contract
51        return
52
53    @property
54    def customer(self):
55        return self.__parent__
56
57    def writeLogMessage(self, view, message):
58        return self.__parent__.writeLogMessage(view, message)
59
60ContractsContainer = attrs_to_fields(ContractsContainer)
61
62
63class ContractPayer(grok.Adapter):
64    """Adapter to turn contracts into IPayers.
65    """
66    grok.implements(IPayer)
67    grok.context(IContract)
68
69    def __init__(self, context):
70        self.context = context
71
72    @property
73    def _customer(self):
74        return self.context.customer
75
76    @property
77    def first_name(self):
78        return getattr(self._customer, 'firstname', None)
79
80    @property
81    def last_name(self):
82        return getattr(self._customer, 'lastname', None)
83
84    @property
85    def payer_id(self):
86        return getattr(self._customer, 'customer_id', None)
87
88
89class ContractBase(grok.Container):
90    """This is a customer contract baseclass.
91    """
92    grok.implements(IContractSelectProduct)  # Neccessary for the
93                                             # selectproduct page (why?)
94    grok.baseclass()
95
96    def __init__(self):
97        super(ContractBase, self).__init__()
98        # The site doesn't exist in unit tests
99        source = getUtility(IIDSource)
100        self.contract_id = unicode(source.get_hex_uuid())
101        self.last_product_id = None
102        self.tc_dict = {}
103        self.title = None
104        self.valid_to = None
105        self.valid_from = None
106        return
107
108    @property
109    def history(self):
110        history = IObjectHistory(self)
111        return history
112
113    @property
114    def state(self):
115        return IWorkflowState(self).getState()
116
117    @property
118    def translated_state(self):
119        try:
120            TRANSLATED_STATES = getUtility(
121                ICustomersUtils).TRANSLATED_CONTRACT_STATES
122            return TRANSLATED_STATES[self.state]
123        except KeyError:
124            return
125
126    @property
127    def class_name(self):
128        return self.__class__.__name__
129
130    @property
131    def formatted_transition_date(self):
132        try:
133            return self.history.messages[-1].split(' - ')[0]
134        except IndexError:
135            return
136
137    @property
138    def customer(self):
139        try:
140            return self.__parent__.__parent__
141        except AttributeError:
142            return None
143
144    @property
145    def user_id(self):
146        if self.customer is not None:
147            return self.customer.customer_id
148        return
149
150    def writeLogMessage(self, view, message):
151        return self.__parent__.__parent__.writeLogMessage(view, message)
152
153    @property
154    def is_editable(self):
155        try:
156            # Customer must have requested
157            cond1 = self.customer.state in getUtility(
158                ICustomersUtils).CONMANAGE_CUSTOMER_STATES
159            # Contract must be in state created
160            cond2 = self.state in getUtility(
161                ICustomersUtils).CONMANAGE_CONTRACT_STATES
162            if not (cond1 and cond2):
163                return False
164        except AttributeError:
165            pass
166        return True
167
168    @property
169    def is_approvable(self):
170        if self.customer and not self.customer.state in (
171            APPROVED, PROVISIONALLY):
172            return False, _("Customer has not yet been approved.")
173        for key, field in getFields(self.check_docs_interface).items():
174            if key.endswith('_object'):
175                obj = getattr(self, key, None)
176                state = getattr(obj, 'state', None)
177                if state and state != VERIFIED:
178                    return False, _(
179                        "Attached documents must be verified first.")
180        return True, None
181
182    @property
183    def translated_class_name(self):
184        try:
185            CONTYPES_DICT = getUtility(ICustomersUtils).CONTYPES_DICT
186            return CONTYPES_DICT[self.class_name]
187        except KeyError:
188            return
189
190    @property
191    def fee_based(self):
192        if self.product_options:
193            amount = 0
194            for option in self.product_options:
195                amount += option.fee
196            if amount:
197                return True
198        return False
199
200
201class SampleContract(ContractBase):
202    """This is a sample contract.
203    """
204
205    grok.implements(
206        ISampleContractProcess,  # must come before ISampleContract
207        ISampleContract,
208        ISampleContractEdit,
209        ICustomerNavigation)
210
211    contract_category = 'sample'
212
213    form_fields_interface = ISampleContract
214
215    edit_form_fields_interface = ISampleContractEdit
216
217    ou_form_fields_interface = ISampleContractOfficialUse
218
219    check_docs_interface = ISampleContract
220
221SampleContract = attrs_to_fields(SampleContract)
222
223
224# Contracts must be importable. So we might need a factory.
225class SampleContractFactory(grok.GlobalUtility):
226    """A factory for contracts.
227    """
228    grok.implements(IFactory)
229    grok.name(u'waeup.SampleContract')
230    title = u"Create a new contract.",
231    description = u"This factory instantiates new sample contract instances."
232
233    def __call__(self, *args, **kw):
234        return SampleContract(*args, **kw)
235
236    def getInterfaces(self):
237        return implementedBy(SampleContract)
238
239
240@grok.subscribe(IContract, grok.IObjectAddedEvent)
241def handle_contract_added(contract, event):
242    """If an contract is added the transition create is fired.
243    The latter produces a logging message.
244    """
245    if IWorkflowState(contract).getState() is None:
246        IWorkflowInfo(contract).fireTransition('create')
247    return
248
249
250class ContractFinder(grok.GlobalUtility):
251    grok.name('contracts_finder')
252    grok.implements(IPayableFinder)
253
254    def get_payable_by_id(self, contract_id):
255        catalog = queryUtility(ICatalog, 'contracts_catalog')
256        if catalog is None:
257            return None
258        result = catalog.searchResults(
259            contract_id=(contract_id, contract_id))
260        result = [x for x in result]
261        if not result:
262            return None
263        # there should not be more than one result really.
264        return result[0]
265
266
267class PayableContract(grok.Adapter):
268    """Adapter to adapt IContracts to IPayable.
269    """
270
271    grok.context(IContract)
272    grok.implements(IPayable)
273
274    def __init__(self, context):
275        self.context = context
276        currencies = set([x.currency for x in context.product_options])
277        if len(currencies) > 1:
278            raise ValueError(
279                "Only contracts with same currency for all options allowed.")
280        return
281
282    @property
283    def payable_id(self):
284        return self.context.contract_id
285
286    @property
287    def title(self):
288        return self.context.title
289
290    @property
291    def currency(self):
292        if not len(self.context.product_options):
293            return None
294        return self.context.product_options[0].currency
295
296    @property
297    def payment_items(self):
298        result = []
299        for num, option in enumerate(self.context.product_options):
300            item = PaymentItem()
301            item.item_id = u'%s' % num
302            item.title = option.title
303            item.amount = option.fee
304            result.append(item)
305        return tuple(result)
Note: See TracBrowser for help on using the repository browser.