# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
##
## Copyright (C) 2005-2012 Async Open Source <http://www.async.com.br>
## All rights reserved
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
##
## You should have received a copy of the GNU Lesser General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., or visit: http://www.gnu.org/.
##
## Author(s): Stoq Team <stoq-devel@async.com.br>
##
from kiwi.datatypes import ValidationError
from kiwi.python import AttributeForwarder
from stoqlib.api import api
from stoqlib.domain.address import Address, CityLocation
from stoqlib.gui.editors.baseeditor import BaseEditorSlave
from stoqlib.lib.countries import get_countries
from stoqlib.lib.translation import stoqlib_gettext
from stoqlib.lib.message import info
from stoqlib.lib.parameters import sysparam
from stoqlib.lib.pluginmanager import get_plugin_manager
_ = stoqlib_gettext
[docs]class CityLocationMixin(object):
"""A mixin class for city locations
Use this mixin in a multiple inheritance editor to have
it's city location validated and prefilled with right data.
For this to happen, you need to have:
- A proxy entry for 'city' accessible at self.city
- A proxy entry for 'state' accessible at self.state
- A proxy combo entry for 'country' accessible at self.country
"""
#
# BaseEditorSlave
#
[docs] def setup_proxies(self):
self._confirming = False
self.country.prefill(get_countries())
self._cache_l10n_fields()
self._prefill_states()
self.city.set_exact_completion()
city_completion = self.city.get_completion()
city_completion.set_minimum_key_length = 2
[docs] def validate_confirm(self):
self._confirming = True
self.force_validation()
self.city.validate(force=True)
if self.city.is_valid():
rv = True
else:
info(_("The city is not valid"))
rv = False
if not self.country.read():
info(_("The country is not valid"))
rv = False
self._confirming = False
return rv
#
# Private
#
def _cache_l10n_fields(self):
self._city_l10n = api.get_l10n_field('city',
self.model.country)
self._state_l10n = api.get_l10n_field('state',
self.model.country)
def _prefill_states(self):
self.state.prefill(self._state_l10n.state_list)
def _prefill_cities(self, force=False):
completion = self.city.get_completion()
if not completion:
# Completion wasn't set yet
return
if len(completion.get_model()) and not force:
return
self.city.prefill([]) # mimic missing .clear method
cities = CityLocation.get_cities_by(self.store,
state=self.model.state,
country=self.model.country)
self.city.prefill(list(cities))
#
# Callbacks
#
[docs] def on_state__focus_out_event(self, entry, event):
self._prefill_cities(force=True)
self.city.validate(force=True)
[docs] def on_state__validate(self, entry, state):
if not self._state_l10n.validate(state):
return ValidationError(_("%s is not valid") % self._state_l10n.label)
[docs] def on_city__focus_out_event(self, entry, event):
self.city.validate(force=True)
[docs] def on_city__validate(self, entry, city):
if sysparam.get_bool('ALLOW_REGISTER_NEW_LOCATIONS'):
return
if self.city.is_focus() and not self._confirming:
# Delay the validation until the user typed the whole city
return
if not self._city_l10n.validate(city,
self.model.state, self.model.country):
return ValidationError(_("%s is not valid") % self._city_l10n.label)
[docs] def on_city__content_changed(self, widget):
city = widget.read()
if city:
self._prefill_cities()
[docs] def after_state__content_changed(self, widget):
if self.state.is_focus():
# Delay the prefill and validation as those will do a lot
# of database queries for each letter typed in here.
return
self._prefill_cities(force=True)
self.city.validate(force=True)
[docs] def after_country__content_changed(self, widget):
self._cache_l10n_fields()
self._prefill_states()
self._prefill_cities(force=True)
self.state.validate(force=True)
self.city.validate(force=True)
class _AddressModel(AttributeForwarder):
attributes = [
'streetnumber',
'district',
'street',
'complement',
'postal_code',
'is_main_address',
'is_valid_model',
'city_location',
]
def __init__(self, target, store):
"""
:param target: an address
:param store: a store
"""
AttributeForwarder.__init__(self, target)
self.store = store
self.city = target.city_location.city
self.state = target.city_location.state
self.country = target.city_location.country
def _city_location_changed(self):
return (self.city != self.city_location.city or
self.state != self.city_location.state or
self.country != self.city_location.country)
def ensure_address(self):
changed = self._city_location_changed()
if changed:
location = CityLocation.get_or_create(
city=self.city,
state=self.state,
country=self.country,
store=self.store)
self.target.city_location = location
[docs]class AddressSlave(BaseEditorSlave, CityLocationMixin):
model_type = _AddressModel
gladefile = 'AddressSlave'
proxy_widgets = [
'streetnumber',
'district',
'street',
'complement',
'postal_code',
'streetnumber_check',
'city',
'state',
'country',
]
def __init__(self, store, person, model=None, is_main_address=True,
visual_mode=False, db_form=None):
self.person = person
self.is_main_address = (model and model.is_main_address
or is_main_address)
self.db_form = db_form
if model is not None:
model = store.fetch(model)
model = _AddressModel(model, store)
plugin = get_plugin_manager()
self._nfe_active = plugin.is_any_active(['nfe', 'nfce'])
BaseEditorSlave.__init__(self, store, model, visual_mode=visual_mode)
#
# BaseEditorSlave
#
[docs] def create_model(self, store):
address = Address(person=self.person,
city_location=CityLocation.get_default(store),
is_main_address=self.is_main_address,
store=store)
return _AddressModel(address, store)
[docs] def set_model(self, model):
""" Changes proxy model. This method is used when this slave is
attached as a container for the main address and the main address
needs to be changed, so this slave must reflect the new address
defined.
"""
self.model.ensure_address()
self.model = model
self.proxy.set_model(self.model)
[docs] def on_confirm(self):
self.model.ensure_address()
[docs] def setup_proxies(self):
CityLocationMixin.setup_proxies(self)
# FIXME: Implement l10n here
self.postal_code.set_mask('00000-000')
if self.db_form:
self._update_forms()
self.proxy = self.add_proxy(self.model,
AddressSlave.proxy_widgets)
# Not using self._statel10n and self._city_l10n here because we need
# to get the label name based on SUGGESTED_COUNTRY and not on the
# country on model.
for field, label in [
('state', self.state_lbl),
('city', self.city_lbl)]:
l10n_field = api.get_l10n_field(field)
label.set_text(l10n_field.label + ':')
# Enable if we already have a number or if we are adding a new address.
self.streetnumber_check.set_active(bool(self.model.streetnumber)
or not self.edit_mode)
self._update_streetnumber()
[docs] def validate_confirm(self):
if self._nfe_active:
# If the plugin is active we must validate the model
return (CityLocationMixin.validate_confirm(self) and
self.model.is_valid_model())
return CityLocationMixin.validate_confirm(self)
#
# Private
#
def _update_streetnumber(self):
if not self.visual_mode:
# Don't do that on visual mode. Visual mode will handle
# the sensitive property on all widgets properly.
active = self.streetnumber_check.get_active()
self.streetnumber.set_sensitive(active)
if not active:
self.model.streetnumber = None
self.streetnumber.set_text('')
return
if not self.model.streetnumber:
self.streetnumber.set_text('')
def _update_forms(self):
self.db_form.update_widget(self.district, other=self.district_lbl)
self.db_form.update_widget(self.street, other=self.address_lbl)
self.db_form.update_widget(self.streetnumber, u'street_number',
other=self.streetnumber_check)
self.db_form.update_widget(self.postal_code,
other=self.postal_code_lbl)
self.db_form.update_widget(self.complement,
other=self.complement_lbl)
self.db_form.update_widget(self.city,
other=self.city_lbl)
self.db_form.update_widget(self.state,
other=self.state_lbl)
self.db_form.update_widget(self.country,
other=self.country_lbl)
#
# Kiwi callbacks
#
[docs] def on_streetnumber__validate(self, entry, streetnumber):
if streetnumber <= 0:
return ValidationError(_("Number cannot be zero or less than zero"))
[docs] def on_streetnumber_check__clicked(self, check_button):
self._update_streetnumber()