source: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/vocabularies.py @ 12329

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

Define a marker interface needed for the registration of the
IkobaSequenceWidget? when using the List-Choice-ProductOptionSourceFactory?
field combination primarily in IContract as a replacement for the
SourceOrderedMultiSelectWidget?.

  • Property svn:keywords set to Id
File size: 6.9 KB
Line 
1## $Id: vocabularies.py 12329 2014-12-27 09:47:46Z 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"""Vocabularies and sources for the customer section.
19"""
20import grok
21from zope.component import getUtility, queryUtility
22from zope.catalog.interfaces import ICatalog
23from zope.interface import implements, directlyProvides
24from zope.schema.interfaces import (
25    ISource, IContextSourceBinder, ValidationError)
26from zc.sourcefactory.basic import BasicSourceFactory
27from zc.sourcefactory.contextual import BasicContextualSourceFactory
28from zc.sourcefactory.source import FactoredContextualSource
29from zc.sourcefactory.interfaces import IContextualSource
30from waeup.ikoba.sourcefactory import SmartBasicContextualSourceFactory
31from waeup.ikoba.interfaces import SimpleIkobaVocabulary, SUBMITTED, VERIFIED
32from waeup.ikoba.interfaces import MessageFactory as _
33from waeup.ikoba.utils.helpers import get_sorted_preferred
34from waeup.ikoba.utils.countries import COUNTRIES
35
36#: a tuple of tuples (<COUNTRY-NAME>, <ISO-CODE>) with Nigeria first.
37COUNTRIES = get_sorted_preferred(COUNTRIES, ['NG'])
38nats_vocab = SimpleIkobaVocabulary(*COUNTRIES)
39
40
41class GenderSource(BasicSourceFactory):
42    """A gender source delivers basically a mapping
43       ``{'m': 'Male', 'f': 'Female'}``
44
45       Using a source, we make sure that the tokens (which are
46       stored/expected for instance from CSV files) are something one
47       can expect and not cryptic IntIDs.
48    """
49    def getValues(self):
50        return ['m', 'f']
51
52    def getToken(self, value):
53        return value[0].lower()
54
55    def getTitle(self, value):
56        if value == 'm':
57            return _('male')
58        if value == 'f':
59            return _('female')
60
61
62class RegNumNotInSource(ValidationError):
63    """Registration number exists already
64    """
65    # The docstring of ValidationErrors is used as error description
66    # by zope.formlib.
67    pass
68
69
70class RegNumberSource(object):
71    """A source that accepts any entry for a certain field if not used
72    already.
73
74    Using this kind of source means a way of setting an invariant.
75
76    We accept a value iff:
77    - the value cannot be found in catalog or
78    - the value can be found as part of some item but the bound item
79      is the context object itself.
80    """
81    implements(ISource)
82    cat_name = 'customers_catalog'
83    field_name = 'reg_number'
84    validation_error = RegNumNotInSource
85    comp_field = 'customer_id'
86
87    def __init__(self, context):
88        self.context = context
89        return
90
91    def __contains__(self, value):
92        """We accept all values not already given to other customers.
93        """
94        cat = queryUtility(ICatalog, self.cat_name)
95        if cat is None:
96            return True
97        kw = {self.field_name: (value, value)}
98        results = cat.searchResults(**kw)
99        for entry in results:
100            if not hasattr(self.context, self.comp_field):
101                # we have no context with comp_field (most probably
102                # while adding a new object, where the container is
103                # the context) which means that the value was given
104                # already to another object (as _something_ was found in
105                # the catalog with that value). Fail on first round.
106                raise self.validation_error(value)
107            if getattr(entry, self.comp_field) != getattr(
108                self.context, self.comp_field):
109                # An entry already given to another customer is not in our
110                # range of acceptable values.
111                raise self.validation_error(value)
112                #return False
113        return True
114
115
116def contextual_reg_num_source(context):
117    source = RegNumberSource(context)
118    return source
119
120directlyProvides(contextual_reg_num_source, IContextSourceBinder)
121
122
123class ConCatProductSource(BasicContextualSourceFactory):
124    """A contract category product source delivers all products
125    which belong to a certain contract_category.
126    """
127
128    def getValues(self, context):
129        concat = getattr(context, 'contract_category', None)
130        products = grok.getSite()['products'].values()
131        if not concat:
132            return products
133        resultlist = [
134            value for value in products if value.contract_category == concat]
135        return resultlist
136
137    def getToken(self, context, value):
138        return value.product_id
139
140    def getTitle(self, context, value):
141        return "%s - %s" % (value.product_id, value.title)
142
143
144class CustomerDocumentSource(BasicContextualSourceFactory):
145    """A customer document source delivers all submitted and verified documents
146    of the context customer.
147    """
148
149    def getValues(self, context):
150        # When checking conversion during import, contracts do not belong to
151        # customers. Thus all portal documents must be returned.
152        user_id = getattr(getattr(context, 'customer', None), 'user_id', None)
153        catalog = getUtility(ICatalog, name='documents_catalog')
154        results = catalog.searchResults(user_id=(user_id, user_id))
155        resultlist = [
156            value for value in results if value.state in (SUBMITTED, VERIFIED)]
157        return resultlist
158
159    def getToken(self, context, value):
160        return value.document_id
161
162    def getTitle(self, context, value):
163        return "%s - %s" % (value.document_id, value.title)
164
165
166class IProductOptionSource(IContextualSource):
167    """A source operating in context.
168
169    This is a marker interface needed for the registration of the
170    IkobaSequenceWidget when using the List-Choice-ProductOptionSourceFactory
171    field combination primarily in IContract as a replacement for
172    the SourceOrderedMultiSelectWidget.
173    """
174
175
176class ProductOptionSource(FactoredContextualSource):
177
178      implements(IProductOptionSource)
179
180
181class ProductOptionSourceFactory(BasicContextualSourceFactory):
182    """A product option source delivers all options belonging to
183    a selected product.
184    """
185
186    source_class = ProductOptionSource
187
188    def getValues(self, context):
189        product = getattr(context, 'product_object', None)
190        if product is None:
191            return []
192        return product.options
193
194    def getToken(self, context, value):
195        return value.title
196
197    def getTitle(self, context, value):
198        return "%s @ %s %s" % (value.title, value.fee, value.currency)
Note: See TracBrowser for help on using the repository browser.