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

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

Move ProductOption? interfaces to productoptions to avoid nasty circular imports.

Use ISO_4217_CURRENCIES.

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