[REF] base: enhance inheritability
split fields_view_get() and used an abstact mixin class in place of of FormatAddress [IMP] base: added a m2o field (ir.ui.view) on res.country to decide the layout of the form view to input addresses.
This commit is contained in:
@@ -10,8 +10,6 @@ from odoo.tools.translate import _
|
||||
from odoo.tools import email_re, email_split
|
||||
from odoo.exceptions import UserError, AccessError
|
||||
|
||||
from odoo.addons.base.res.res_partner import FormatAddress
|
||||
|
||||
from . import crm_stage
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
@@ -51,11 +49,11 @@ CRM_LEAD_FIELDS_TO_MERGE = [
|
||||
'partner_name']
|
||||
|
||||
|
||||
class Lead(FormatAddress, models.Model):
|
||||
class Lead(models.Model):
|
||||
_name = "crm.lead"
|
||||
_description = "Lead/Opportunity"
|
||||
_order = "priority desc,activity_date_deadline,id desc"
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin', 'utm.mixin']
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin', 'utm.mixin', 'format.address.mixin']
|
||||
_mail_mass_mailing = _('Leads / Opportunities')
|
||||
|
||||
def _default_probability(self):
|
||||
@@ -311,15 +309,15 @@ class Lead(FormatAddress, models.Model):
|
||||
return super(Lead, self.with_context(context)).copy(default=default)
|
||||
|
||||
@api.model
|
||||
def fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False):
|
||||
def _fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False):
|
||||
if self._context.get('opportunity_id'):
|
||||
opportunity = self.browse(self._context['opportunity_id'])
|
||||
action = opportunity.get_formview_action()
|
||||
if action.get('views') and any(view_id for view_id in action['views'] if view_id[1] == view_type):
|
||||
view_id = next(view_id[0] for view_id in action['views'] if view_id[1] == view_type)
|
||||
res = super(Lead, self).fields_view_get(view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu)
|
||||
res = super(Lead, self)._fields_view_get(view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu)
|
||||
if view_type == 'form':
|
||||
res['arch'] = self.fields_view_get_address(res['arch'])
|
||||
res['arch'] = self._fields_view_get_address(res['arch'])
|
||||
return res
|
||||
|
||||
# ----------------------------------------
|
||||
|
||||
@@ -433,7 +433,7 @@
|
||||
}
|
||||
}
|
||||
&.o_form_editable .o_address_format {
|
||||
input.o_address_city {
|
||||
.o_address_city {
|
||||
width: 38%;
|
||||
}
|
||||
div.o_address_state {
|
||||
|
||||
@@ -32,17 +32,27 @@ class Country(models.Model):
|
||||
_description = 'Country'
|
||||
_order = 'name'
|
||||
|
||||
name = fields.Char(string='Country Name', required=True, translate=True, help='The full name of the country.')
|
||||
code = fields.Char(string='Country Code', size=2,
|
||||
help='The ISO country code in two chars. \nYou can use this field for quick search.')
|
||||
address_format = fields.Text(help="""You can state here the usual format to use for the \
|
||||
addresses belonging to this country.\n\nYou can use the python-style string patern with all the field of the address \
|
||||
(for example, use '%(street)s' to display the field 'street') plus
|
||||
\n%(state_name)s: the name of the state
|
||||
\n%(state_code)s: the code of the state
|
||||
\n%(country_name)s: the name of the country
|
||||
\n%(country_code)s: the code of the country""",
|
||||
default='%(street)s\n%(street2)s\n%(city)s %(state_code)s %(zip)s\n%(country_name)s')
|
||||
name = fields.Char(
|
||||
string='Country Name', required=True, translate=True, help='The full name of the country.')
|
||||
code = fields.Char(
|
||||
string='Country Code', size=2,
|
||||
help='The ISO country code in two chars. \nYou can use this field for quick search.')
|
||||
address_format = fields.Text(
|
||||
help="Display format to use for addresses belonging to this country.\n\n"
|
||||
"You can use python-style string pattern with all the fields of the address "
|
||||
"(for example, use '%(street)s' to display the field 'street') plus"
|
||||
"\n%(state_name)s: the name of the state"
|
||||
"\n%(state_code)s: the code of the state"
|
||||
"\n%(country_name)s: the name of the country"
|
||||
"\n%(country_code)s: the code of the country",
|
||||
default='%(street)s\n%(street2)s\n%(city)s %(state_code)s %(zip)s\n%(country_name)s')
|
||||
address_view_id = fields.Many2one(
|
||||
comodel_name='ir.ui.view', string="Address View",
|
||||
domain=[('model', '=', 'res.partner'), ('type', '=', 'form')],
|
||||
help="Use this field if you want to replace the usual way to encode a complete address. "
|
||||
"Note that the address_format field is used to modify the way to display addresses "
|
||||
"(in reports for example), while this field is used to modify the input form for "
|
||||
"addresses.")
|
||||
currency_id = fields.Many2one('res.currency', string='Currency')
|
||||
image = fields.Binary(attachment=True)
|
||||
phone_code = fields.Integer(string='Country Calling Code')
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
<field name="model">res.country</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<div class="oe_button_box">
|
||||
</div>
|
||||
<field name="image" widget="image" class="oe_avatar"/>
|
||||
<group>
|
||||
<group>
|
||||
@@ -28,9 +30,12 @@
|
||||
<field name="code"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="address_format" groups="base.group_no_one" placeholder="Address format..."/>
|
||||
<field name="phone_code"/>
|
||||
</group>
|
||||
<group string="Advanced Address Formatting" groups="base.group_no_one" >
|
||||
<field name="address_format" placeholder="Address format..."/>
|
||||
<field name="address_view_id"/>
|
||||
</group>
|
||||
</group>
|
||||
<label for="state_ids"/>
|
||||
<field name="state_ids">
|
||||
|
||||
@@ -28,11 +28,6 @@ WARNING_MESSAGE = [
|
||||
WARNING_HELP = _('Selecting the "Warning" option will notify user with the message, Selecting "Blocking Message" will throw an exception with the message and block the flow. The Message has to be written in the next field.')
|
||||
|
||||
|
||||
ADDRESS_FORMAT_CLASSES = {
|
||||
'%(city)s %(state_code)s\n%(zip)s': 'o_city_state',
|
||||
'%(zip)s %(city)s': 'o_zip_city'
|
||||
}
|
||||
|
||||
ADDRESS_FIELDS = ('street', 'street2', 'zip', 'city', 'state_id', 'country_id')
|
||||
@api.model
|
||||
def _lang_get(self):
|
||||
@@ -44,26 +39,30 @@ def _tz_get(self):
|
||||
return [(tz, tz) for tz in sorted(pytz.all_timezones, key=lambda tz: tz if not tz.startswith('Etc/') else '_')]
|
||||
|
||||
|
||||
class FormatAddress(object):
|
||||
@api.model
|
||||
def fields_view_get_address(self, arch):
|
||||
address_format = self.env.user.company_id.country_id.address_format or ''
|
||||
for format_pattern, format_class in ADDRESS_FORMAT_CLASSES.iteritems():
|
||||
if format_pattern in address_format:
|
||||
doc = etree.fromstring(arch)
|
||||
for address_node in doc.xpath("//div[@class='o_address_format']"):
|
||||
# add address format class to address block
|
||||
address_node.attrib['class'] += ' ' + format_class
|
||||
if format_class.startswith('o_zip'):
|
||||
zip_fields = address_node.xpath("//field[@name='zip']")
|
||||
city_fields = address_node.xpath("//field[@name='city']")
|
||||
if zip_fields and city_fields:
|
||||
# move zip field before city field
|
||||
city_fields[0].addprevious(zip_fields[0])
|
||||
arch = etree.tostring(doc)
|
||||
break
|
||||
return arch
|
||||
class FormatAddressMixin(models.AbstractModel):
|
||||
_name = "format.address.mixin"
|
||||
|
||||
def _fields_view_get_address(self, arch):
|
||||
# consider the country of the user, not the country of the partner we want to display
|
||||
address_view_id = self.env.user.company_id.country_id.address_view_id
|
||||
if address_view_id and not self._context.get('no_address_format'):
|
||||
#render the partner address accordingly to address_view_id
|
||||
doc = etree.fromstring(arch)
|
||||
for address_node in doc.xpath("//div[@class='o_address_format']"):
|
||||
Partner = self.env['res.partner'].with_context(no_address_format=True)
|
||||
sub_view = Partner.fields_view_get(
|
||||
view_id=address_view_id.id, view_type='form', toolbar=False, submenu=False)
|
||||
sub_view_node = etree.fromstring(sub_view['arch'])
|
||||
#if the model is different than res.partner, there are chances that the view won't work
|
||||
#(e.g fields not present on the model). In that case we just return arch
|
||||
if self._name != 'res.partner':
|
||||
try:
|
||||
self.env['ir.ui.view'].postprocess_and_fields(self._name, sub_view_node, None)
|
||||
except ValueError:
|
||||
return arch
|
||||
address_node.getparent().replace(address_node, sub_view_node)
|
||||
arch = etree.tostring(doc)
|
||||
return arch
|
||||
|
||||
class PartnerCategory(models.Model):
|
||||
_description = 'Partner Tags'
|
||||
@@ -127,8 +126,9 @@ class PartnerTitle(models.Model):
|
||||
|
||||
_sql_constraints = [('name_uniq', 'unique (name)', "Title name already exists !")]
|
||||
|
||||
class Partner(models.Model, FormatAddress):
|
||||
class Partner(models.Model):
|
||||
_description = 'Partner'
|
||||
_inherit = ['format.address.mixin']
|
||||
_name = "res.partner"
|
||||
_order = "display_name"
|
||||
|
||||
@@ -307,12 +307,12 @@ class Partner(models.Model, FormatAddress):
|
||||
return tools.image_resize_image_big(image.encode('base64'))
|
||||
|
||||
@api.model
|
||||
def fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False):
|
||||
def _fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False):
|
||||
if (not view_id) and (view_type == 'form') and self._context.get('force_email'):
|
||||
view_id = self.env.ref('base.view_partner_simple_form').id
|
||||
res = super(Partner, self).fields_view_get(view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu)
|
||||
res = super(Partner, self)._fields_view_get(view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu)
|
||||
if view_type == 'form':
|
||||
res['arch'] = self.fields_view_get_address(res['arch'])
|
||||
res['arch'] = self._fields_view_get_address(res['arch'])
|
||||
return res
|
||||
|
||||
@api.constrains('parent_id')
|
||||
|
||||
+28
-20
@@ -1336,23 +1336,8 @@ class BaseModel(object):
|
||||
return result
|
||||
|
||||
@api.model
|
||||
def fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False):
|
||||
""" fields_view_get([view_id | view_type='form'])
|
||||
|
||||
Get the detailed composition of the requested view like fields, model, view architecture
|
||||
|
||||
:param view_id: id of the view or None
|
||||
:param view_type: type of the view to return if view_id is None ('form', 'tree', ...)
|
||||
:param toolbar: true to include contextual actions
|
||||
:param submenu: deprecated
|
||||
:return: dictionary describing the composition of the requested view (including inherited views and extensions)
|
||||
:raise AttributeError:
|
||||
* if the inherited view has unknown position to work with other than 'before', 'after', 'inside', 'replace'
|
||||
* if some tag other than 'position' is found in parent view
|
||||
:raise Invalid ArchitectureError: if there is view type other than form, tree, calendar, search etc defined on the structure
|
||||
"""
|
||||
def _fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False):
|
||||
View = self.env['ir.ui.view']
|
||||
|
||||
result = {
|
||||
'model': self._name,
|
||||
'field_parent': False,
|
||||
@@ -1380,7 +1365,6 @@ class BaseModel(object):
|
||||
# otherwise try to find the lowest priority matching ir.ui.view
|
||||
view_id = View.default_view(self._name, view_type)
|
||||
|
||||
# context for post-processing might be overriden
|
||||
if view_id:
|
||||
# read the view with inherited views applied
|
||||
root_view = View.browse(view_id).read_combined(['id', 'name', 'field_parent', 'type', 'model', 'arch'])
|
||||
@@ -1389,9 +1373,7 @@ class BaseModel(object):
|
||||
result['type'] = root_view['type']
|
||||
result['view_id'] = root_view['id']
|
||||
result['field_parent'] = root_view['field_parent']
|
||||
# override context from postprocessing
|
||||
if root_view['model'] != self._name:
|
||||
View = View.with_context(base_model_name=root_view['model'])
|
||||
result['base_model'] = root_view['model']
|
||||
else:
|
||||
# fallback on default views methods if no ir.ui.view could be found
|
||||
try:
|
||||
@@ -1401,6 +1383,32 @@ class BaseModel(object):
|
||||
result['name'] = 'default'
|
||||
except AttributeError:
|
||||
raise UserError(_("No default view of type '%s' could be found !") % view_type)
|
||||
return result
|
||||
|
||||
@api.model
|
||||
def fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False):
|
||||
""" fields_view_get([view_id | view_type='form'])
|
||||
|
||||
Get the detailed composition of the requested view like fields, model, view architecture
|
||||
|
||||
:param view_id: id of the view or None
|
||||
:param view_type: type of the view to return if view_id is None ('form', 'tree', ...)
|
||||
:param toolbar: true to include contextual actions
|
||||
:param submenu: deprecated
|
||||
:return: dictionary describing the composition of the requested view (including inherited views and extensions)
|
||||
:raise AttributeError:
|
||||
* if the inherited view has unknown position to work with other than 'before', 'after', 'inside', 'replace'
|
||||
* if some tag other than 'position' is found in parent view
|
||||
:raise Invalid ArchitectureError: if there is view type other than form, tree, calendar, search etc defined on the structure
|
||||
"""
|
||||
View = self.env['ir.ui.view']
|
||||
|
||||
# Get the view arch and all other attributes describing the composition of the view
|
||||
result = self._fields_view_get(view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu)
|
||||
|
||||
# Override context for postprocessing
|
||||
if view_id and result.get('base_model', self._name) != self._name:
|
||||
View = View.with_context(base_model_name=result['base_model'])
|
||||
|
||||
# Apply post processing, groups and modifiers etc...
|
||||
xarch, xfields = View.postprocess_and_fields(self._name, etree.fromstring(result['arch']), view_id)
|
||||
|
||||
Reference in New Issue
Block a user