Files
odoo_source/addons/base_address_extended/models/res_partner.py
T
Nasreddin Boulif (bon) 866c90926b [FIX] base_address_extended: split 'correctly' street name
Issue

	- Install "Contacts" & 'base_address_extended' modules
	- Go to contacts and create a contact with value:
	  - street name : Chaussee de Namur
	  - House : 40
	- Save and edit again
	- Set country to 'Netherlands' then save.

	Address not splited well (wrong values for street name and house).
	The issue is not present in UI but is in tests.
	(Issue in UI from odoo 14.0+)

Cause

	There is 2 '_split_street_with_params'
	(inverse function that set street fields):
	- partner_autocomplete_address_extended (removed in 14.0)
	- base_address_extended

	The 'spliting' logic is not the same in both function.

	In UI, looks like it does not call '_split_street_with_params'
	from 'base_address_extended' since no super(Partner, self)...

	However in testing, it does call it from module
	'base_address_extended' and therefore trigger the issue.

Solution

	Adapt _split_street_with_params function
	(in 'base_address_extended' module):
	If previous field (from 'street_format') to parse
	in 'street_raw' is 'street_name', then:
	- set `tmp` to: splitted (max 1 split) street_raw
	  with current field seperator
	- set `append_previous, sep, tmp[0]` to:
	  splitted tmp[0] (first part of splited street_raw) with `rpartition(' ')`
	- add 'append_previous' to 'street_name' value
	- join 'tmp' values and set it to street
	  (should equal to what left from `rpartition(' ')` split
	  + seconf part of first normal split)
	- continue normal parsing flow

opw-2476096

closes odoo/odoo#70886

X-original-commit: 74116c982a5ddef65a05ef90017ac4ce63bdf58f
Signed-off-by: bon-odoo <nboulif@users.noreply.github.com>
2021-05-17 14:28:24 +00:00

140 lines
6.2 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import re
from odoo import api, fields, models, _
from odoo.exceptions import UserError
class Partner(models.Model):
_inherit = ['res.partner']
street_name = fields.Char(
'Street Name', compute='_compute_street_data', inverse='_inverse_street_data', store=True)
street_number = fields.Char(
'House', compute='_compute_street_data', inverse='_inverse_street_data', store=True)
street_number2 = fields.Char(
'Door', compute='_compute_street_data', inverse='_inverse_street_data', store=True)
def _inverse_street_data(self):
"""Updates the street field.
Writes the `street` field on the partners when one of the sub-fields in STREET_FIELDS
has been touched"""
street_fields = self._get_street_fields()
for partner in self:
street_format = (partner.country_id.street_format or
'%(street_number)s/%(street_number2)s %(street_name)s')
previous_field = None
previous_pos = 0
street_value = ""
separator = ""
# iter on fields in street_format, detected as '%(<field_name>)s'
for re_match in re.finditer(r'%\(\w+\)s', street_format):
# [2:-2] is used to remove the extra chars '%(' and ')s'
field_name = re_match.group()[2:-2]
field_pos = re_match.start()
if field_name not in street_fields:
raise UserError(_("Unrecognized field %s in street format.", field_name))
if not previous_field:
# first iteration: add heading chars in street_format
if partner[field_name]:
street_value += street_format[0:field_pos] + partner[field_name]
else:
# get the substring between 2 fields, to be used as separator
separator = street_format[previous_pos:field_pos]
if street_value and partner[field_name]:
street_value += separator
if partner[field_name]:
street_value += partner[field_name]
previous_field = field_name
previous_pos = re_match.end()
# add trailing chars in street_format
street_value += street_format[previous_pos:]
partner.street = street_value
@api.depends('street')
def _compute_street_data(self):
"""Splits street value into sub-fields.
Recomputes the fields of STREET_FIELDS when `street` of a partner is updated"""
street_fields = self._get_street_fields()
for partner in self:
if not partner.street:
for field in street_fields:
partner[field] = None
continue
street_format = (partner.country_id.street_format or
'%(street_number)s/%(street_number2)s %(street_name)s')
street_raw = partner.street
vals = self._split_street_with_params(street_raw, street_format)
# assign the values to the fields
for k, v in vals.items():
partner[k] = v
for k in set(street_fields) - set(vals):
partner[k] = None
def _split_street_with_params(self, street_raw, street_format):
street_fields = self._get_street_fields()
vals = {}
previous_pos = 0
field_name = None
# iter on fields in street_format, detected as '%(<field_name>)s'
for re_match in re.finditer(r'%\(\w+\)s', street_format):
field_pos = re_match.start()
if not field_name:
#first iteration: remove the heading chars
street_raw = street_raw[field_pos:]
# get the substring between 2 fields, to be used as separator
separator = street_format[previous_pos:field_pos]
field_value = None
if separator and field_name:
#maxsplit set to 1 to unpack only the first element and let the rest untouched
tmp = street_raw.split(separator, 1)
if previous_greedy in vals:
# attach part before space to preceding greedy field
append_previous, sep, tmp[0] = tmp[0].rpartition(' ')
street_raw = separator.join(tmp)
vals[previous_greedy] += sep + append_previous
if len(tmp) == 2:
field_value, street_raw = tmp
vals[field_name] = field_value
if field_value or not field_name:
previous_greedy = None
if field_name == 'street_name' and separator == ' ':
previous_greedy = field_name
# select next field to find (first pass OR field found)
# [2:-2] is used to remove the extra chars '%(' and ')s'
field_name = re_match.group()[2:-2]
else:
# value not found: keep looking for the same field
pass
if field_name not in street_fields:
raise UserError(_("Unrecognized field %s in street format.", field_name))
previous_pos = re_match.end()
# last field value is what remains in street_raw minus trailing chars in street_format
trailing_chars = street_format[previous_pos:]
if trailing_chars and street_raw.endswith(trailing_chars):
vals[field_name] = street_raw[:-len(trailing_chars)]
else:
vals[field_name] = street_raw
return vals
def write(self, vals):
res = super(Partner, self).write(vals)
if 'country_id' in vals and 'street' not in vals:
self._inverse_street_data()
return res
def _formatting_address_fields(self):
"""Returns the list of address fields usable to format addresses."""
return super(Partner, self)._formatting_address_fields() + self._get_street_fields()
def _get_street_fields(self):
"""Returns the fields that can be used in a street format.
Overwrite this function if you want to add your own fields."""
return ['street_name', 'street_number', 'street_number2']