source: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/contracts.py @ 12388

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

Save 'terms and conditions' when adding a contract.

Prevent changing product_object after adding contract.

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