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

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

Show first 9 instead of first 6 id characters.

  • Property svn:keywords set to Id
File size: 7.3 KB
RevLine 
[12092]1## $Id: vocabularies.py 12445 2015-01-12 07:01:55Z henrik $
[11958]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"""
[12092]20import grok
[11958]21from zope.component import getUtility, queryUtility
22from zope.catalog.interfaces import ICatalog
23from zope.interface import implements, directlyProvides
[12329]24from zope.schema.interfaces import (
25    ISource, IContextSourceBinder, ValidationError)
[11958]26from zc.sourcefactory.basic import BasicSourceFactory
27from zc.sourcefactory.contextual import BasicContextualSourceFactory
[12329]28from zc.sourcefactory.source import FactoredContextualSource
29from zc.sourcefactory.interfaces import IContextualSource
[12127]30from waeup.ikoba.sourcefactory import SmartBasicContextualSourceFactory
[12100]31from waeup.ikoba.interfaces import SimpleIkobaVocabulary, SUBMITTED, VERIFIED
[11958]32from waeup.ikoba.interfaces import MessageFactory as _
33from waeup.ikoba.utils.helpers import get_sorted_preferred
34from waeup.ikoba.utils.countries import COUNTRIES
[12343]35from waeup.ikoba.payments.currencies import ISO_4217_CURRENCIES
[11958]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
[11985]62
[11958]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
[11985]70
[11958]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'
[11985]87
[11958]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
[11985]116
[11958]117def contextual_reg_num_source(context):
118    source = RegNumberSource(context)
119    return source
[11985]120
[11958]121directlyProvides(contextual_reg_num_source, IContextSourceBinder)
[12092]122
123
[12100]124class ConCatProductSource(BasicContextualSourceFactory):
125    """A contract category product source delivers all products
[12097]126    which belong to a certain contract_category.
[12092]127    """
128
129    def getValues(self, context):
[12098]130        concat = getattr(context, 'contract_category', None)
[12092]131        products = grok.getSite()['products'].values()
[12098]132        if not concat:
[12093]133            return products
[12092]134        resultlist = [
[12098]135            value for value in products if value.contract_category == concat]
[12092]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)
[12100]143
144
145class CustomerDocumentSource(BasicContextualSourceFactory):
[12101]146    """A customer document source delivers all submitted and verified documents
[12100]147    of the context customer.
148    """
149
150    def getValues(self, context):
[12127]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))
[12100]156        resultlist = [
[12127]157            value for value in results if value.state in (SUBMITTED, VERIFIED)]
[12100]158        return resultlist
159
160    def getToken(self, context, value):
161        return value.document_id
162
163    def getTitle(self, context, value):
[12445]164        return "%s... - %s" % (value.document_id[:9], value.title)
[12324]165
[12329]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):
[12324]183    """A product option source delivers all options belonging to
184    a selected product.
185    """
186
[12329]187    source_class = ProductOptionSource
188
[12324]189    def getValues(self, context):
[12340]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
[12324]201
202    def getToken(self, context, value):
203        return value.title
204
205    def getTitle(self, context, value):
[12343]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.