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

Last change on this file since 12725 was 12718, checked in by uli, 10 years ago

contract finder does not complain about missing catalog.

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