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

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

Improve contract management. Tests will follow which show that customers can only do what they are allowed to do.

  • Property svn:keywords set to Id
File size: 6.3 KB
RevLine 
[12089]1## $Id: contracts.py 12337 2014-12-29 23:05:40Z 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"""
[12097]19Customer contract components.
[12089]20"""
21import os
22import grok
23from zope.component import queryUtility, getUtility
24from zope.component.interfaces import IFactory
25from zope.interface import implementedBy
[12144]26from zope.schema import getFields
[12089]27from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
28from waeup.ikoba.interfaces import MessageFactory as _
[12258]29from waeup.ikoba.interfaces import (
30    IIkobaUtils, IObjectHistory, VERIFIED, IIDSource)
[12089]31from waeup.ikoba.customers.interfaces import (
[12097]32    IContractsContainer, ICustomerNavigation,
[12333]33    IContract, IContractProcess, IContractEdit, ICustomersUtils,
34    ISampleContract, ISampleContractProcess, ISampleContractEdit)
[12097]35from waeup.ikoba.customers.utils import generate_contract_id
[12089]36from waeup.ikoba.utils.helpers import attrs_to_fields
37
[12097]38class ContractsContainer(grok.Container):
39    """This is a container for customer contracts.
[12089]40    """
[12097]41    grok.implements(IContractsContainer, ICustomerNavigation)
42    grok.provides(IContractsContainer)
[12089]43
[12097]44    def addContract(self, contract):
45        if not IContract.providedBy(contract):
[12089]46            raise TypeError(
[12097]47                'ContractsContainers contain only IContract instances')
48        self[contract.contract_id] = contract
[12089]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
[12097]58ContractsContainer = attrs_to_fields(ContractsContainer)
[12089]59
[12097]60class ContractBase(grok.Container):
61    """This is a customer contract baseclass.
[12089]62    """
[12333]63
[12089]64    grok.baseclass()
65
[12144]66    check_docs_interface = None
67
[12097]68    contract_category = None
[12092]69
[12103]70    form_fields_interface = None
71
72    edit_form_fields_interface = None
73
[12089]74    def __init__(self):
[12097]75        super(ContractBase, self).__init__()
[12089]76        # The site doesn't exist in unit tests
[12258]77        source = getUtility(IIDSource)
78        self.contract_id = unicode(source.get_hex_uuid())
[12094]79        self.last_product_id = None
[12089]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(
[12097]95                ICustomersUtils).TRANSLATED_CONTRACT_STATES
[12089]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:
[12210]107            return self.history.messages[-1].split(' - ')[0]
108        except IndexError:
[12089]109            return
110
111    @property
[12336]112    def title(self):
113        return getattr(
114            getattr(self, 'product_object', None),
115            'contract_autotitle', None)
116
117    @property
[12089]118    def customer(self):
119        try:
120            return self.__parent__.__parent__
121        except AttributeError:
122            return None
123
[12289]124    @property
125    def user_id(self):
126        if self.customer is not None:
127            return self.customer.customer_id
128        return
129
[12089]130    def writeLogMessage(self, view, message):
131        return self.__parent__.__parent__.writeLogMessage(view, message)
132
133    @property
[12337]134    def is_editable(self):
[12089]135        try:
136            # Customer must be approved
137            cond1 = self.customer.state in getUtility(
[12099]138                ICustomersUtils).CONMANAGE_CUSTOMER_STATES
[12097]139            # Contract must be in state created
[12089]140            cond2 = self.state in getUtility(
[12099]141                ICustomersUtils).CONMANAGE_CONTRACT_STATES
[12089]142            if not (cond1 and cond2):
143                return False
144        except AttributeError:
145            pass
146        return True
147
148    @property
[12144]149    def is_approvable(self):
150        for key, field in getFields(self.check_docs_interface).items():
151            if key.endswith('_object'):
152                obj = getattr(self, key, None)
153                state = getattr(obj, 'state', None)
154                if state and state != VERIFIED:
[12168]155                    return False, _("Attached documents must be verified first.")
156        return True, None
[12144]157
158    @property
[12089]159    def translated_class_name(self):
160        try:
[12099]161            CONTYPES_DICT = getUtility(ICustomersUtils).CONTYPES_DICT
162            return CONTYPES_DICT[self.class_name]
[12089]163        except KeyError:
164            return
165
166
[12097]167class SampleContract(ContractBase):
168    """This is a sample contract.
[12089]169    """
170
[12333]171    grok.implements(
172        ISampleContractProcess, # must come before ISampleContract
173        ISampleContract,
174        ISampleContractEdit,
175        ICustomerNavigation)
[12103]176
[12097]177    contract_category = 'sample'
[12092]178
[12103]179    form_fields_interface = ISampleContract
180
181    edit_form_fields_interface = ISampleContractEdit
182
[12144]183    check_docs_interface = ISampleContract
184
[12097]185SampleContract = attrs_to_fields(SampleContract)
[12089]186
187
[12097]188# Contracts must be importable. So we might need a factory.
189class SampleContractFactory(grok.GlobalUtility):
190    """A factory for contracts.
[12089]191    """
192    grok.implements(IFactory)
[12097]193    grok.name(u'waeup.SampleContract')
194    title = u"Create a new contract.",
195    description = u"This factory instantiates new sample contract instances."
[12089]196
197    def __call__(self, *args, **kw):
[12097]198        return SampleContract(*args, **kw)
[12089]199
200    def getInterfaces(self):
[12097]201        return implementedBy(SampleContract)
[12089]202
[12097]203@grok.subscribe(IContract, grok.IObjectAddedEvent)
204def handle_contract_added(contract, event):
205    """If an contract is added the transition create is fired.
[12090]206    The latter produces a logging message.
207    """
[12097]208    if IWorkflowState(contract).getState() is None:
209        IWorkflowInfo(contract).fireTransition('create')
[12090]210    return
Note: See TracBrowser for help on using the repository browser.