## $Id: vocabularies.py 9778 2012-12-06 15:45:03Z henrik $ ## ## Copyright (C) 2014 Uli Fouquet & Henrik Bettermann ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## """Vocabularies and sources for the customer section. """ from zope.component import getUtility, queryUtility from zope.catalog.interfaces import ICatalog from zope.interface import implements, directlyProvides from zope.schema.interfaces import ISource, IContextSourceBinder from zope.schema.interfaces import ValidationError from zc.sourcefactory.basic import BasicSourceFactory from zc.sourcefactory.contextual import BasicContextualSourceFactory from waeup.ikoba.interfaces import SimpleIkobaVocabulary from waeup.ikoba.interfaces import MessageFactory as _ from waeup.ikoba.utils.helpers import get_sorted_preferred from waeup.ikoba.utils.countries import COUNTRIES #: a tuple of tuples (, ) with Nigeria first. COUNTRIES = get_sorted_preferred(COUNTRIES, ['NG']) nats_vocab = SimpleIkobaVocabulary(*COUNTRIES) class GenderSource(BasicSourceFactory): """A gender source delivers basically a mapping ``{'m': 'Male', 'f': 'Female'}`` Using a source, we make sure that the tokens (which are stored/expected for instance from CSV files) are something one can expect and not cryptic IntIDs. """ def getValues(self): return ['m', 'f'] def getToken(self, value): return value[0].lower() def getTitle(self, value): if value == 'm': return _('male') if value == 'f': return _('female') class RegNumNotInSource(ValidationError): """Registration number exists already """ # The docstring of ValidationErrors is used as error description # by zope.formlib. pass class RegNumberSource(object): """A source that accepts any entry for a certain field if not used already. Using this kind of source means a way of setting an invariant. We accept a value iff: - the value cannot be found in catalog or - the value can be found as part of some item but the bound item is the context object itself. """ implements(ISource) cat_name = 'customers_catalog' field_name = 'reg_number' validation_error = RegNumNotInSource comp_field = 'customer_id' def __init__(self, context): self.context = context return def __contains__(self, value): """We accept all values not already given to other customers. """ cat = queryUtility(ICatalog, self.cat_name) if cat is None: return True kw = {self.field_name: (value, value)} results = cat.searchResults(**kw) for entry in results: if not hasattr(self.context, self.comp_field): # we have no context with comp_field (most probably # while adding a new object, where the container is # the context) which means that the value was given # already to another object (as _something_ was found in # the catalog with that value). Fail on first round. raise self.validation_error(value) if getattr(entry, self.comp_field) != getattr( self.context, self.comp_field): # An entry already given to another customer is not in our # range of acceptable values. raise self.validation_error(value) #return False return True def contextual_reg_num_source(context): source = RegNumberSource(context) return source directlyProvides(contextual_reg_num_source, IContextSourceBinder)