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

Last change on this file since 12677 was 12663, checked in by Henrik Bettermann, 10 years ago

Extend contract workflow to integrate payment.

Prepare (empty) page to select payment method and finally create a payment object.

  • Property svn:keywords set to Id
File size: 6.7 KB
Line 
1## $Id: contracts.py 12663 2015-03-05 07:28:31Z henrik $
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 os
22import grok
23from zope.component import queryUtility, getUtility
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    IIkobaUtils, IObjectHistory,
31    VERIFIED, APPROVED, PROVISIONALLY,
32    IIDSource)
33from waeup.ikoba.customers.interfaces import (
34    IContractsContainer, ICustomerNavigation,
35    IContract, IContractSelectProduct, ICustomersUtils,
36    ISampleContract, ISampleContractProcess, ISampleContractEdit,
37    ISampleContractOfficialUse)
38from waeup.ikoba.customers.utils import generate_contract_id
39from waeup.ikoba.utils.helpers import attrs_to_fields
40
41class ContractsContainer(grok.Container):
42    """This is a container for customer contracts.
43    """
44    grok.implements(IContractsContainer, ICustomerNavigation)
45    grok.provides(IContractsContainer)
46
47    def addContract(self, contract):
48        if not IContract.providedBy(contract):
49            raise TypeError(
50                'ContractsContainers contain only IContract instances')
51        self[contract.contract_id] = contract
52        return
53
54    @property
55    def customer(self):
56        return self.__parent__
57
58    def writeLogMessage(self, view, message):
59        return self.__parent__.writeLogMessage(view, message)
60
61ContractsContainer = attrs_to_fields(ContractsContainer)
62
63class ContractBase(grok.Container):
64    """This is a customer contract baseclass.
65    """
66    grok.implements(IContractSelectProduct)  # Necesary for the selectproduct page
67
68    grok.baseclass()
69
70    def __init__(self):
71        super(ContractBase, self).__init__()
72        # The site doesn't exist in unit tests
73        source = getUtility(IIDSource)
74        self.contract_id = unicode(source.get_hex_uuid())
75        self.last_product_id = None
76        self.tc_dict = {}
77        self.title = None
78        self.valid_to = None
79        self.valid_from = None
80        return
81
82    @property
83    def history(self):
84        history = IObjectHistory(self)
85        return history
86
87    @property
88    def state(self):
89        return IWorkflowState(self).getState()
90
91    @property
92    def translated_state(self):
93        try:
94            TRANSLATED_STATES = getUtility(
95                ICustomersUtils).TRANSLATED_CONTRACT_STATES
96            return TRANSLATED_STATES[self.state]
97        except KeyError:
98            return
99
100    @property
101    def class_name(self):
102        return self.__class__.__name__
103
104    @property
105    def formatted_transition_date(self):
106        try:
107            return self.history.messages[-1].split(' - ')[0]
108        except IndexError:
109            return
110
111    @property
112    def customer(self):
113        try:
114            return self.__parent__.__parent__
115        except AttributeError:
116            return None
117
118    @property
119    def user_id(self):
120        if self.customer is not None:
121            return self.customer.customer_id
122        return
123
124    def writeLogMessage(self, view, message):
125        return self.__parent__.__parent__.writeLogMessage(view, message)
126
127    @property
128    def is_editable(self):
129        try:
130            # Customer must have requested
131            cond1 = self.customer.state in getUtility(
132                ICustomersUtils).CONMANAGE_CUSTOMER_STATES
133            # Contract must be in state created
134            cond2 = self.state in getUtility(
135                ICustomersUtils).CONMANAGE_CONTRACT_STATES
136            if not (cond1 and cond2):
137                return False
138        except AttributeError:
139            pass
140        return True
141
142    @property
143    def is_approvable(self):
144        if self.customer and not self.customer.state in (
145            APPROVED, PROVISIONALLY):
146            return False, _("Customer has not yet been approved.")
147        for key, field in getFields(self.check_docs_interface).items():
148            if key.endswith('_object'):
149                obj = getattr(self, key, None)
150                state = getattr(obj, 'state', None)
151                if state and state != VERIFIED:
152                    return False, _("Attached documents must be verified first.")
153        return True, None
154
155    @property
156    def translated_class_name(self):
157        try:
158            CONTYPES_DICT = getUtility(ICustomersUtils).CONTYPES_DICT
159            return CONTYPES_DICT[self.class_name]
160        except KeyError:
161            return
162
163    @property
164    def fee_based(self):
165        if self.product_options:
166            amount = 0
167            for option in self.product_options:
168                amount += option.fee
169            if amount:
170                return True
171        return False
172
173
174class SampleContract(ContractBase):
175    """This is a sample contract.
176    """
177
178    grok.implements(
179        ISampleContractProcess, # must come before ISampleContract
180        ISampleContract,
181        ISampleContractEdit,
182        ICustomerNavigation)
183
184    contract_category = 'sample'
185
186    form_fields_interface = ISampleContract
187
188    edit_form_fields_interface = ISampleContractEdit
189
190    ou_form_fields_interface = ISampleContractOfficialUse
191
192    check_docs_interface = ISampleContract
193
194SampleContract = attrs_to_fields(SampleContract)
195
196
197# Contracts must be importable. So we might need a factory.
198class SampleContractFactory(grok.GlobalUtility):
199    """A factory for contracts.
200    """
201    grok.implements(IFactory)
202    grok.name(u'waeup.SampleContract')
203    title = u"Create a new contract.",
204    description = u"This factory instantiates new sample contract instances."
205
206    def __call__(self, *args, **kw):
207        return SampleContract(*args, **kw)
208
209    def getInterfaces(self):
210        return implementedBy(SampleContract)
211
212@grok.subscribe(IContract, grok.IObjectAddedEvent)
213def handle_contract_added(contract, event):
214    """If an contract is added the transition create is fired.
215    The latter produces a logging message.
216    """
217    if IWorkflowState(contract).getState() is None:
218        IWorkflowInfo(contract).fireTransition('create')
219    return
Note: See TracBrowser for help on using the repository browser.