diff --git a/__init__.py b/__init__.py
index 16f373d..7407569 100644
--- a/__init__.py
+++ b/__init__.py
@@ -1,7 +1,10 @@
# The COPYRIGHT file at the top level of this repository contains the full
# copyright notices and license terms.
from trytond.pool import Pool
+from .party import *
def register():
Pool.register(
+ Address,
+ CountryZip,
module='party_zip', type_='model')
diff --git a/doc/index.rst b/doc/index.rst
new file mode 100644
index 0000000..1c0e8cc
--- /dev/null
+++ b/doc/index.rst
@@ -0,0 +1,12 @@
+Party Zip Module
+################
+
+The Party Zip module adds the country zip field to addresses as well as menu
+entries to Country Zip and Subdivision models.
+
+If installed, the module will make zip and city fields in addresses readonly
+so they can only be filled in with the new country zip field. This allows all
+addresses to have a zip/city that is previously created in the database.
+
+The module also takes care of updating all addresses if zip, city, subdivision
+or country fields are changed in the zip record.
diff --git a/party.py b/party.py
index d44479e..bf12e6c 100644
--- a/party.py
+++ b/party.py
@@ -1,33 +1,123 @@
# The COPYRIGHT file at the top level of this repository contains the full
# copyright notices and license terms.
-from trytond.model import ModelSQL, ModelView
-from trytond.pool import PoolMeta
+from trytond.model import fields
+from trytond.pool import PoolMeta, Pool
+from trytond.pyson import Eval, If, Bool
-__all__ = ['Address']
-__metaclass__ = PoolMeta
-
-
-# TODO: Create a view that adds 'country_zip' field and hides the following
-# fields in the form view:
-#
-# - zip
-# - city
-# - country
-# - subdivision
-#
-# (I think it is better to hide rather than replace so that other inheriting
-# modules will still be compatible)
-#
-# For tree view I think it is better to keep existing fields
+__all__ = ['Address', 'CountryZip']
class Address:
__name__ = 'party.address'
- country_zip = fields.Many2One('country.zip', 'Location')
+ __metaclass__ = PoolMeta
+ country_zip = fields.Many2One('country.zip', 'Location',
+ ondelete='RESTRICT', domain=[
+ If(Bool(Eval('country')), ('country', '=', Eval('country', -1)),
+ ()),
+ If(Bool(Eval('subdivision')),
+ ('subdivision', '=', Eval('subdivision', -1)), ()),
+ ], depends=['country', 'subdivision'])
-# TODO: Given that I think there are some issues if we try to redefine those
-# fields as Function ones, we could consider storing their value in the
-# database on write. The problem we need to consider is what happens if a field
-# from country.zip is changed. Should we 'simply' propagate the new values to
-# existing addresses?
+ @classmethod
+ def __setup__(cls):
+ super(Address, cls).__setup__()
+ cls.zip.readonly = True
+ cls.city.readonly = True
+ cls.country.states['readonly'] |= Bool(Eval('country_zip'))
+ cls.subdivision.states['readonly'] |= Bool(Eval('country_zip'))
+ @staticmethod
+ def update_zip_values(CountryZip, values):
+ values = values.copy()
+ if 'country_zip' in values:
+ if values['country_zip']:
+ country_zip, = CountryZip.search([
+ ('id', '=', values['country_zip']),
+ ], limit=1)
+ values['zip'] = country_zip.zip
+ values['city'] = country_zip.city
+ values['country'] = country_zip.country.id
+ values['subdivision'] = (country_zip.subdivision.id if
+ country_zip.subdivision else None)
+ else:
+ values['zip'] = None
+ values['city'] = None
+ return values
+
+ @classmethod
+ def create(cls, vlist):
+ CountryZip = Pool().get('country.zip')
+ new_vlist = []
+ for values in vlist:
+ new_vlist.append(cls.update_zip_values(CountryZip, values))
+ super(Address, cls).create(new_vlist)
+
+ @classmethod
+ def write(cls, *args):
+ CountryZip = Pool().get('country.zip')
+ actions = iter(args)
+ new_args = []
+ for addresses, values in zip(actions, actions):
+ new_args.append(addresses)
+ new_args.append(cls.update_zip_values(CountryZip, values))
+ super(Address, cls).write(*new_args)
+
+ @fields.depends('country_zip')
+ def on_change_country_zip(self):
+ if self.country_zip:
+ self.zip = self.country_zip.zip
+ self.city = self.country_zip.city
+ self.country = self.country_zip.country
+ self.subdivision = self.country_zip.subdivision
+ else:
+ self.zip = None
+ self.city = None
+
+
+class CountryZip:
+ __name__ = 'country.zip'
+ __metaclass__ = PoolMeta
+
+ def get_rec_name(self, name):
+ res = []
+ if self.zip:
+ res.append(self.zip)
+ if self.city:
+ res.append(self.city)
+ res = [' '.join(res)]
+ if self.subdivision:
+ res.append(self.subdivision.rec_name)
+ res = [' - '.join(res)]
+ res.append('(%s)' % self.country.rec_name)
+ return ' '.join(res)
+
+ @classmethod
+ def search_rec_name(cls, name, clause):
+ return ['OR',
+ [('zip',) + tuple(clause[1:])],
+ [('city',) + tuple(clause[1:])],
+ ]
+
+ @classmethod
+ def write(cls, *args):
+ Address = Pool().get('party.address')
+
+ super(CountryZip, cls).write(*args)
+
+ actions = iter(args)
+ fields = set(['zip', 'city', 'country', 'subdivision'])
+ to_update = []
+ for zips, values in zip(actions, actions):
+ intersec = set(values.keys()) & fields
+ if not intersec:
+ continue
+ addresses = Address.search([
+ ('country_zip', 'in', [x.id for x in zips]),
+ ])
+ to_update.append(addresses)
+ address_values = {}
+ for field in intersec:
+ address_values[field] = values[field]
+ to_update.append(address_values)
+
+ Address.write(*to_update)
diff --git a/party.xml b/party.xml
new file mode 100644
index 0000000..00b19e6
--- /dev/null
+++ b/party.xml
@@ -0,0 +1,65 @@
+
+
+
+ party.address
+
+ address_form
+
+
+
+ Subdivisions
+ country.subdivision
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Subdivisions
+ country.subdivision
+
+
+
+
+
+
+
+
+
+
+
+
+
+ form_relate
+ country.country,-1
+
+
+
+
+ Zips
+ country.zip
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/test_party_zip.py b/tests/test_party_zip.py
index ad4345b..3e57098 100644
--- a/tests/test_party_zip.py
+++ b/tests/test_party_zip.py
@@ -1,33 +1,92 @@
# The COPYRIGHT file at the top level of this repository contains the full
# copyright notices and license terms.
import unittest
-# import doctest
import trytond.tests.test_tryton
-from trytond.tests.test_tryton import test_view, test_depends
-# TODO: Remove if no sceneario needed.
-# from trytond.tests.test_tryton import doctest_setup, doctest_teardown
+from trytond.tests.test_tryton import ModuleTestCase, with_transaction
+from trytond.pool import Pool
-class TestCase(unittest.TestCase):
- 'Test module'
+class PartyZipTestCase(ModuleTestCase):
+ 'Test PartyZip module'
+ module = 'party_zip'
- def setUp(self):
- trytond.tests.test_tryton.install_module('party_zip')
+ @with_transaction()
+ def test_address(self):
+ 'Create address'
+ pool = Pool()
+ Party = pool.get('party.party')
+ Address = pool.get('party.address')
+ Country = pool.get('country.country')
+ Subdivision = pool.get('country.subdivision')
+ Zip = pool.get('country.zip')
- def test0005views(self):
- 'Test views'
- test_view('party_zip')
+ country1, country2 = Country.create([{
+ 'name': 'Country 1',
+ }, {
+ 'name': 'Country 2',
+ }])
+ subdivision1, subdivision2 = Subdivision.create([{
+ 'code': '1',
+ 'name': 'Subdivision 1',
+ 'type': 'area',
+ 'country': country1.id,
+ }, {
+ 'code': '2',
+ 'name': 'Subdivision 2',
+ 'type': 'area',
+ 'country': country2.id,
+ }])
+ zip1, zip2 = Zip.create([{
+ 'zip': 'zip1',
+ 'city': 'city1',
+ 'country': country1.id,
+ 'subdivision': subdivision1.id,
+ }, {
+ 'zip': 'zip2',
+ 'city': 'city2',
+ 'country': country2.id,
+ 'subdivision': subdivision2.id,
+ }])
+ party1, = Party.create([{
+ 'name': 'Party 1',
+ }])
+ address, = Address.create([{
+ 'party': party1.id,
+ 'street': 'St sample, 15',
+ 'city': 'City',
+ }])
+ self.assertEqual(address.zip, None)
+ self.assertEqual(address.city, None)
+ Address.write([address, {
+ 'country_zip': zip1,
+ }])
+ self.assertEqual(address.zip, 'zip1')
+ self.assertEqual(address.city, 'city1')
+ self.assertEqual(address.country.id, country1.id)
+ self.assertEqual(address.subdivision.id, subdivision1.id)
- def test0006depends(self):
- 'Test depends'
- test_depends()
+ Address.write([address, {
+ 'country_zip': zip2,
+ }])
+ self.assertEqual(address.zip, 'zip2')
+ self.assertEqual(address.city, 'city2')
+ self.assertEqual(address.country.id, country2.id)
+ self.assertEqual(address.subdivision.id, subdivision2.id)
+
+ Zip.write([zip2, {
+ 'zip': 'ZIP 3',
+ 'city': 'CITY 3',
+ 'country': country1.id,
+ 'subdivision': subdivision1.id,
+ }])
+ address, = Address.browse([address.id])
+ self.assertEqual(address.zip, 'ZIP3')
+ self.assertEqual(address.city, 'CITY 3')
+ self.assertEqual(address.country.id, country1.id)
+ self.assertEqual(address.subdivision.id, subdivision1.id)
def suite():
suite = trytond.tests.test_tryton.suite()
- suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCase))
- # TODO: remove if no scenario needed.
- #suite.addTests(doctest.DocFileSuite('scenario_party_zip.rst',
- # setUp=doctest_setup, tearDown=doctest_teardown, encoding='utf-8',
- # optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
+ suite.addTests(unittest.TestLoader().loadTestsFromTestCase(PartyZipTestCase))
return suite
diff --git a/tryton.cfg b/tryton.cfg
index 423880e..d23f4bc 100644
--- a/tryton.cfg
+++ b/tryton.cfg
@@ -4,3 +4,4 @@ depends:
country
party
xml:
+ party.xml
diff --git a/view/address_form.xml b/view/address_form.xml
new file mode 100644
index 0000000..55fa34d
--- /dev/null
+++ b/view/address_form.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+