Finish the module: do the polishing, tests and docs.

This commit is contained in:
Albert Cervera i Areny 2016-07-20 11:08:12 +02:00
parent e65608dc38
commit 599a9b5a83
7 changed files with 283 additions and 43 deletions

View File

@ -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')

12
doc/index.rst Normal file
View File

@ -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.

138
party.py
View File

@ -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)

65
party.xml Normal file
View File

@ -0,0 +1,65 @@
<tryton>
<data>
<record model="ir.ui.view" id="address_view_form">
<field name="model">party.address</field>
<field name="inherit" ref="party.address_view_form"/>
<field name="name">address_form</field>
</record>
<record model="ir.action.act_window" id="act_subdivision_form">
<field name="name">Subdivisions</field>
<field name="res_model">country.subdivision</field>
</record>
<record model="ir.action.act_window.view" id="act_subdivision_form_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="country.subdivision_view_tree"/>
<field name="act_window" ref="act_subdivision_form"/>
</record>
<record model="ir.action.act_window.view" id="act_subdivision_form_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="country.subdivision_view_form"/>
<field name="act_window" ref="act_subdivision_form"/>
</record>
<menuitem parent="country.menu_country_form" action="act_subdivision_form"
id="menu_subdivision_form" string="Subdivisions"/>
<record model="ir.action.act_window" id="act_subdivision_form2">
<field name="name">Subdivisions</field>
<field name="res_model">country.subdivision</field>
<field name="domain"
eval="[('country', 'in', Eval('active_ids'))]" pyson="1"/>
</record>
<record model="ir.action.act_window.view" id="act_subdivision_form_view2_1">
<field name="sequence" eval="10"/>
<field name="view" ref="country.subdivision_view_tree"/>
<field name="act_window" ref="act_subdivision_form2"/>
</record>
<record model="ir.action.act_window.view" id="act_subdivision_form_view2_2">
<field name="sequence" eval="20"/>
<field name="view" ref="country.subdivision_view_form"/>
<field name="act_window" ref="act_subdivision_form2"/>
</record>
<record model="ir.action.keyword" id="act_subdivision_form_keyword1">
<field name="keyword">form_relate</field>
<field name="model">country.country,-1</field>
<field name="action" ref="act_subdivision_form2"/>
</record>
<record model="ir.action.act_window" id="act_zip_form">
<field name="name">Zips</field>
<field name="res_model">country.zip</field>
</record>
<record model="ir.action.act_window.view" id="act_zip_form_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="country.zip_view_list"/>
<field name="act_window" ref="act_zip_form"/>
</record>
<record model="ir.action.act_window.view" id="act_zip_form_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="country.zip_view_form"/>
<field name="act_window" ref="act_zip_form"/>
</record>
<menuitem parent="country.menu_country_form" action="act_zip_form"
id="menu_zip_form"/>
</data>
</tryton>

View File

@ -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

View File

@ -4,3 +4,4 @@ depends:
country
party
xml:
party.xml

10
view/address_form.xml Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<data>
<xpath expr="/form/label[@name='zip']" position="before">
<label name="country_zip"/>
<field name="country_zip" colspan="3"/>
<newline/>
</xpath>
</data>