Add Replace Party wizard

issue5820
review26581003
This commit is contained in:
Cédric Krier committed 2016-10-02 15:18:38 +02:00
1 parent 12294c209f
commit 3cd9694f5f
9 files changed
+220 -5

No files matched your search

+1
View File
@@ -1,3 +1,4 @@
* Add Replace Party wizard
* Remove streetbis from Address
* Add address format per country and language
* Restrict subdivision address to first level
+2
View File
@@ -16,6 +16,7 @@ def register():
PartyCategory,
PartyIdentifier,
CheckVIESResult,
PartyReplaceAsk,
Address,
AddressFormat,
ContactMechanism,
@@ -23,4 +24,5 @@ def register():
module='party', type_='model')
Pool.register(
CheckVIES,
PartyReplace,
module='party', type_='wizard')
+124 -4
View File
@@ -4,18 +4,19 @@ from itertools import groupby
import stdnum.eu.vat as vat
import stdnum.exceptions
from sql import Null
from sql import Null, Column
from sql.functions import CharLength
from trytond.model import ModelView, ModelSQL, fields, Unique
from trytond.wizard import Wizard, StateTransition, StateView, Button
from trytond.pyson import Eval
from trytond.pyson import Eval, Bool
from trytond.transaction import Transaction
from trytond.pool import Pool
from trytond import backend
__all__ = ['Party', 'PartyCategory', 'PartyIdentifier',
'CheckVIESResult', 'CheckVIES']
'CheckVIESResult', 'CheckVIES',
'PartyReplace', 'PartyReplaceAsk']
VAT_COUNTRIES = [('', '')]
STATES = {
@@ -48,7 +49,14 @@ class Party(ModelSQL, ModelView):
'Contact Mechanisms', states=STATES, depends=DEPENDS)
categories = fields.Many2Many('party.party-party.category',
'party', 'category', 'Categories', states=STATES, depends=DEPENDS)
active = fields.Boolean('Active', select=True)
active = fields.Boolean('Active', select=True, states={
'readonly': Bool(Eval('replaced_by')),
},
depends=['replaced_by'])
replaced_by = fields.Many2One('party.party', "Replaced By", readonly=True,
states={
'invisible': ~Eval('replaced_by'),
})
full_name = fields.Function(fields.Char('Full Name'), 'get_full_name')
phone = fields.Function(fields.Char('Phone'), 'get_mechanism')
mobile = fields.Function(fields.Char('Mobile'), 'get_mechanism')
@@ -388,3 +396,115 @@ class CheckVIES(Wizard):
'parties_succeed': [p.id for p in self.result.parties_succeed],
'parties_failed': [p.id for p in self.result.parties_failed],
}
class PartyReplace(Wizard):
"Replace Party"
__name__ = 'party.replace'
start_state = 'ask'
ask = StateView('party.replace.ask', 'party.replace_ask_view_form', [
Button("Cancel", 'end', 'tryton-cancel'),
Button("Replace", 'replace', 'tryton-find-replace', default=True),
])
replace = StateTransition()
@classmethod
def __setup__(cls):
super(PartyReplace, cls).__setup__()
cls._error_messages.update({
'different_name': ("Parties have different names: "
"%(source_name)s vs %(destination_name)s."),
'different_vat_code': ("Parties have different VAT Code: "
"%(source_code)s vs %(destination_code)s."),
})
def check_similarity(self):
source = self.ask.source
destination = self.ask.destination
if source.name != destination.name:
key = 'party.replace name %s %s' % (source.id, destination.id)
self.raise_user_warning(key, 'different_name', {
'source_name': source.name,
'destination_name': destination.name,
})
if source.vat_code != destination.vat_code:
key = 'party.replace vat_code %s %s' % (source.id, destination.id)
self.raise_user_warning(key, 'different_vat_code', {
'source_code': source.vat_code,
'destination_code': destination.vat_code,
})
def transition_replace(self):
pool = Pool()
Address = pool.get('party.address')
ContactMechanism = pool.get('party.contact_mechanism')
transaction = Transaction()
self.check_similarity()
source = self.ask.source
destination = self.ask.destination
Address.write(list(source.addresses), {
'active': False,
})
ContactMechanism.write(list(source.contact_mechanisms), {
'active': False,
})
source.replaced_by = destination
source.active = False
source.save()
cursor = transaction.connection.cursor()
for model_name, field_name in self.fields_to_replace():
Model = pool.get(model_name)
table = Model.__table__()
column = Column(table, field_name)
where = column == source.id
if transaction.database.has_returning():
returning = [table.id]
else:
cursor.execute(*table.select(table.id, where=where))
ids = [x[0] for x in cursor]
returning = None
cursor.execute(*table.update(
[column],
[destination.id],
where=where,
returning=returning))
if transaction.database.has_returning():
ids = [x[0] for x in cursor]
Model._insert_history(ids)
return 'end'
@classmethod
def fields_to_replace(cls):
return [
('party.address', 'party'),
('party.contact_mechanism', 'party'),
]
class PartyReplaceAsk(ModelView):
"Replace Party"
__name__ = 'party.replace.ask'
source = fields.Many2One('party.party', "Source", required=True)
destination = fields.Many2One('party.party', "Destination", required=True,
domain=[
('id', '!=', Eval('source', -1)),
],
depends=['source'])
@classmethod
def default_source(cls):
context = Transaction().context
if context.get('active_model') == 'party.party':
return context.get('active_id')
@fields.depends('source')
def on_change_source(self):
if self.source and self.source.replaced_by:
self.destination = self.source.replaced_by
+21
View File
@@ -133,5 +133,26 @@ this repository contains the full copyright notices and license terms. -->
<field name="name">check_vies_result</field>
</record>
<record model="ir.action.wizard" id="wizard_replace">
<field name="name">Replace</field>
<field name="wiz_name">party.replace</field>
<field name="model">party.party</field>
</record>
<record model="ir.action-res.group"
id="wizard_replace-group_party_admin">
<field name="action" ref="wizard_replace"/>
<field name="group" ref="group_party_admin"/>
</record>
<record model="ir.action.keyword" id="wizard_replace_keyword1">
<field name="keyword">form_action</field>
<field name="model">party.party,-1</field>
<field name="action" ref="wizard_replace"/>
</record>
<record model="ir.ui.view" id="replace_ask_view_form">
<field name="model">party.replace.ask</field>
<field name="type">form</field>
<field name="name">replace_ask_form</field>
</record>
</data>
</tryton>
+11 -1
View File
@@ -53,6 +53,12 @@ for dep in info.get('depends', []):
requires.append(get_require_version('trytond_%s' % dep))
requires.append(get_require_version('trytond'))
tests_require = [get_require_version('proteus')]
dependency_links = []
if minor_version % 2:
# Add development index for testing with proteus
dependency_links.append('https://trydevpi.tryton.org/')
setup(name=name,
version=version,
description='Tryton module with parties and addresses',
@@ -70,7 +76,7 @@ setup(name=name,
package_data={
'trytond.modules.party': (info.get('xml', [])
+ ['tryton.cfg', 'view/*.xml', 'locale/*.po', '*.odt',
'icons/*.svg']),
'icons/*.svg', 'tests/*.rst']),
},
classifiers=[
'Development Status :: 5 - Production/Stable',
@@ -116,5 +122,9 @@ setup(name=name,
""",
test_suite='tests',
test_loader='trytond.test_loader:Loader',
tests_require=tests_require,
use_2to3=True,
convert_2to3_doctests=[
'tests/scenario_party_replace.rst',
],
)
+43
View File
@@ -0,0 +1,43 @@
======================
Party Replace Scenario
======================
Imports::
>>> from proteus import Model, Wizard
>>> from trytond.tests.tools import install_modules
Install party::
>>> config = install_modules('party')
Create a party::
>>> Party = Model.get('party.party')
>>> party1 = Party(name='Pam')
>>> party1.save()
>>> address1, = party1.addresses
Create a second party similar party::
>>> party2 = Party(name='Pam')
>>> party2.save()
>>> address2, = party2.addresses
Replace the second by the first party::
>>> replace = Wizard('party.replace', models=[party2])
>>> replace.form.source == party2
True
>>> replace.form.destination = party1
>>> replace.execute('replace')
>>> party2.reload()
>>> bool(party2.active)
False
>>> address2.reload()
>>> address2.party == party1
True
>>> bool(address2.active)
False
+9
View File
@@ -1,8 +1,12 @@
# This file is part of Tryton. 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 ModuleTestCase, with_transaction
from trytond.tests.test_tryton import doctest_teardown
from trytond.tests.test_tryton import doctest_checker
from trytond.pool import Pool
from trytond.transaction import Transaction
@@ -121,4 +125,9 @@ class PartyTestCase(ModuleTestCase):
def suite():
suite = trytond.tests.test_tryton.suite()
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(PartyTestCase))
suite.addTests(doctest.DocFileSuite(
'scenario_party_replace.rst',
tearDown=doctest_teardown, encoding='utf-8',
checker=doctest_checker,
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
return suite
+2
View File
@@ -11,6 +11,8 @@ this repository contains the full copyright notices and license terms. -->
<field name="active" xexpand="0" width="25"/>
<!-- Add here some checkboxes ! -->
</group>
<label name="replaced_by"/>
<field name="replaced_by"/>
<notebook colspan="6">
<page string="General" id="general">
<field name="addresses" mode="form,tree" colspan="4"
+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0"?>
<form>
<label name="source" string="Party"/>
<field name="source"/>
<label name="destination" string="Replace By"/>
<field name="destination"/>
</form>