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

Last change on this file since 12696 was 12683, checked in by uli, 10 years ago

Add adapter to turn lists of product opitons into payment items.

  • Property svn:keywords set to Id
File size: 7.2 KB
Line 
1## $Id: contracts.py 12683 2015-03-07 04:18:42Z 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 IPaymentItem
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 ContractBase(grok.Container):
76    """This is a customer contract baseclass.
77    """
78    grok.implements(IContractSelectProduct)  # Neccessary for the
79                                             # selectproduct page (why?)
80    grok.baseclass()
81
82    def __init__(self):
83        super(ContractBase, self).__init__()
84        # The site doesn't exist in unit tests
85        source = getUtility(IIDSource)
86        self.contract_id = unicode(source.get_hex_uuid())
87        self.last_product_id = None
88        self.tc_dict = {}
89        self.title = None
90        self.valid_to = None
91        self.valid_from = None
92        return
93
94    @property
95    def history(self):
96        history = IObjectHistory(self)
97        return history
98
99    @property
100    def state(self):
101        return IWorkflowState(self).getState()
102
103    @property
104    def translated_state(self):
105        try:
106            TRANSLATED_STATES = getUtility(
107                ICustomersUtils).TRANSLATED_CONTRACT_STATES
108            return TRANSLATED_STATES[self.state]
109        except KeyError:
110            return
111
112    @property
113    def class_name(self):
114        return self.__class__.__name__
115
116    @property
117    def formatted_transition_date(self):
118        try:
119            return self.history.messages[-1].split(' - ')[0]
120        except IndexError:
121            return
122
123    @property
124    def customer(self):
125        try:
126            return self.__parent__.__parent__
127        except AttributeError:
128            return None
129
130    @property
131    def user_id(self):
132        if self.customer is not None:
133            return self.customer.customer_id
134        return
135
136    def writeLogMessage(self, view, message):
137        return self.__parent__.__parent__.writeLogMessage(view, message)
138
139    @property
140    def is_editable(self):
141        try:
142            # Customer must have requested
143            cond1 = self.customer.state in getUtility(
144                ICustomersUtils).CONMANAGE_CUSTOMER_STATES
145            # Contract must be in state created
146            cond2 = self.state in getUtility(
147                ICustomersUtils).CONMANAGE_CONTRACT_STATES
148            if not (cond1 and cond2):
149                return False
150        except AttributeError:
151            pass
152        return True
153
154    @property
155    def is_approvable(self):
156        if self.customer and not self.customer.state in (
157            APPROVED, PROVISIONALLY):
158            return False, _("Customer has not yet been approved.")
159        for key, field in getFields(self.check_docs_interface).items():
160            if key.endswith('_object'):
161                obj = getattr(self, key, None)
162                state = getattr(obj, 'state', None)
163                if state and state != VERIFIED:
164                    return False, _(
165                        "Attached documents must be verified first.")
166        return True, None
167
168    @property
169    def translated_class_name(self):
170        try:
171            CONTYPES_DICT = getUtility(ICustomersUtils).CONTYPES_DICT
172            return CONTYPES_DICT[self.class_name]
173        except KeyError:
174            return
175
176    @property
177    def fee_based(self):
178        if self.product_options:
179            amount = 0
180            for option in self.product_options:
181                amount += option.fee
182            if amount:
183                return True
184        return False
185
186
187class SampleContract(ContractBase):
188    """This is a sample contract.
189    """
190
191    grok.implements(
192        ISampleContractProcess,  # must come before ISampleContract
193        ISampleContract,
194        ISampleContractEdit,
195        ICustomerNavigation)
196
197    contract_category = 'sample'
198
199    form_fields_interface = ISampleContract
200
201    edit_form_fields_interface = ISampleContractEdit
202
203    ou_form_fields_interface = ISampleContractOfficialUse
204
205    check_docs_interface = ISampleContract
206
207SampleContract = attrs_to_fields(SampleContract)
208
209
210# Contracts must be importable. So we might need a factory.
211class SampleContractFactory(grok.GlobalUtility):
212    """A factory for contracts.
213    """
214    grok.implements(IFactory)
215    grok.name(u'waeup.SampleContract')
216    title = u"Create a new contract.",
217    description = u"This factory instantiates new sample contract instances."
218
219    def __call__(self, *args, **kw):
220        return SampleContract(*args, **kw)
221
222    def getInterfaces(self):
223        return implementedBy(SampleContract)
224
225
226@grok.subscribe(IContract, grok.IObjectAddedEvent)
227def handle_contract_added(contract, event):
228    """If an contract is added the transition create is fired.
229    The latter produces a logging message.
230    """
231    if IWorkflowState(contract).getState() is None:
232        IWorkflowInfo(contract).fireTransition('create')
233    return
Note: See TracBrowser for help on using the repository browser.