Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Arthur Emynon
Odoo customer relationship management
Commits
1836b334
Commit
1836b334
authored
Jan 14, 2022
by
Arthur Emynon
Browse files
Added address formatting for contact and partner
parent
a2c255b3
Changes
5
Hide whitespace changes
Inline
Side-by-side
models/contact.py
View file @
1836b334
# -*- coding: utf-8 -*-
from
odoo
import
models
,
fields
,
api
from
odoo.exceptions
import
ValidationError
class
Contact
(
models
.
Model
):
_name
=
"customer.contact"
...
...
@@ -19,10 +20,13 @@ class Contact(models.Model):
street2
=
fields
.
Char
(
string
=
"Rue 2"
)
city
=
fields
.
Char
(
string
=
"Ville"
)
zip
=
fields
.
Char
(
string
=
"Code postal"
)
country_id
=
fields
.
Many2one
(
"res.country"
,
string
=
"Pays"
)
country_id
=
fields
.
Many2one
(
"res.country"
,
string
=
"Pays"
,
default
=
lambda
self
:
self
.
_default_country
()
)
state_id
=
fields
.
Many2one
(
"res.country.state"
,
string
=
"Région"
)
department_id
=
fields
.
Many2one
(
"res.country.department"
,
string
=
"Département"
)
# Sites
site_ids
=
fields
.
One2many
(
"customer.site"
,
"contact_id"
,
string
=
"Sites"
)
# ========================
# Computations and actions
# ========================
...
...
@@ -37,3 +41,89 @@ class Contact(models.Model):
name
=
contact
.
name
res
.
append
((
contact
.
id
,
name
))
return
res
# Address
# There is a inclusion relation that allows to deduce other fields from the completion of some of them:
# zip < department < state < country
# TODO: Check if onchanges are called recursively. If so, check: - When?; - Can this generate loops of some sort?
# TODO: This does not allow exceptions. See e.g. https://fr.wikipedia.org/wiki/Code_postal_en_France#Cas_particuliers
# TODO: This only works for France (and it assumes that a country called "France" is France)
# TODO: It should probably be an option to disable auto-completion by zipcode for France...
# TODO: Add cities to these?
# TODO: Not very smart when changing the country...
@
api
.
onchange
(
'zip'
)
def
_onchange_zip
(
record
):
if
record
.
country_id
.
name
==
"France"
and
record
.
zip
and
len
(
record
.
zip
)
==
5
:
department_code
=
record
.
zip
[:
2
]
if
not
record
.
department_id
or
record
.
department_id
.
code
!=
department_code
:
departments
=
record
.
env
[
'res.country.department'
].
search
([
(
'code'
,
'='
,
department_code
),
(
'country_id'
,
'='
,
record
.
country_id
.
id
)],
limit
=
2
)
if
len
(
departments
)
==
1
:
record
.
department_id
=
departments
[
0
]
elif
record
.
department_id
:
record
.
department_id
=
False
@
api
.
onchange
(
'department_id'
)
def
_onchange_department_id
(
record
):
if
record
.
department_id
and
record
.
department_id
.
state_id
!=
record
.
state_id
:
record
.
state_id
=
record
.
department_id
.
state_id
if
record
.
country_id
.
name
==
"France"
and
record
.
zip
and
len
(
record
.
zip
)
==
5
and
(
not
record
.
department_id
or
record
.
zip
[:
2
]
!=
record
.
department_id
.
code
):
record
.
zip
=
False
@
api
.
onchange
(
'state_id'
)
def
_onchange_state_id
(
record
):
if
record
.
state_id
and
record
.
state_id
.
country_id
!=
record
.
country_id
:
record
.
country_id
=
record
.
state_id
.
country_id
if
record
.
department_id
and
record
.
department_id
.
state_id
!=
record
.
state_id
:
record
.
department_id
=
False
@
api
.
onchange
(
'country_id'
)
def
_onchange_country_id
(
record
):
if
record
.
state_id
and
record
.
state_id
.
country_id
!=
record
.
country_id
:
record
.
state_id
=
False
# TODO: Add an option to manage default country?
def
_default_country
(
self
):
france_ids
=
self
.
env
[
'res.country'
].
name_search
(
'France'
,
operator
=
'='
,
limit
=
2
)
print
(
france_ids
)
if
len
(
france_ids
)
!=
1
:
# Default exit if France does not exists or there are too many Frances.. (should not happen?)
return
None
else
:
print
(
france_ids
[
0
])
print
(
france_ids
[
0
][
0
])
return
france_ids
[
0
][
0
]
# Address copying
def
_copy_address_from
(
self
,
source
):
"""
Copy address from the source. Copied fields are:
- street (Char)
- street2 (Char)
- city (Char)
- zip (Char)
- country_id (Many2one -- res.country)
- state_id (Many2one -- res.state)
- department_id (Many2one -- res.department)
The fields mentionned above must exist in the source.
(Hence, the source should be a customer.site, a customer.partner or a customer.contact.)
Both `source` is an individual records (i.e. a "singleton" recordset), while `self` may be a recordset.
"""
for
record
in
self
:
record
.
country_id
=
source
.
country_id
record
.
state_id
=
source
.
state_id
record
.
department_id
=
source
.
department_id
record
.
zip
=
source
.
zip
record
.
city
=
source
.
city
record
.
street2
=
source
.
street2
record
.
street
=
source
.
street
def
action_copy_address_from_partner
(
self
):
for
record
in
self
:
record
.
_copy_address_from
(
record
.
partner_id
)
return
True
models/partner.py
View file @
1836b334
# -*- coding: utf-8 -*-
from
odoo
import
models
,
fields
from
odoo
import
models
,
fields
,
api
from
odoo.exceptions
import
ValidationError
class
Partner
(
models
.
Model
):
_name
=
"customer.partner"
...
...
@@ -18,10 +19,69 @@ class Partner(models.Model):
street2
=
fields
.
Char
(
string
=
"Rue 2"
)
city
=
fields
.
Char
(
string
=
"Ville"
)
zip
=
fields
.
Char
(
string
=
"Code postal"
)
country_id
=
fields
.
Many2one
(
"res.country"
,
string
=
"Pays"
)
country_id
=
fields
.
Many2one
(
"res.country"
,
string
=
"Pays"
,
default
=
lambda
self
:
self
.
_default_country
()
)
state_id
=
fields
.
Many2one
(
"res.country.state"
,
string
=
"Région"
)
department_id
=
fields
.
Many2one
(
"res.country.department"
,
string
=
"Département"
)
# Contacts
contact_ids
=
fields
.
One2many
(
"customer.contact"
,
"partner_id"
,
string
=
"Contacts"
)
site_ids
=
fields
.
One2many
(
"customer.site"
,
"partner_id"
,
string
=
"Sites"
)
# ========================
# Computations and actions
# ========================
# Address
# There is a inclusion relation that allows to deduce other fields from the completion of some of them:
# zip < department < state < country
# TODO: Check if onchanges are called recursively. If so, check: - When?; - Can this generate loops of some sort?
# TODO: This does not allow exceptions. See e.g. https://fr.wikipedia.org/wiki/Code_postal_en_France#Cas_particuliers
# TODO: This only works for France (and it assumes that a country called "France" is France)
# TODO: It should probably be an option to disable auto-completion by zipcode for France...
# TODO: Add cities to these?
# TODO: Not very smart when changing the country...
@
api
.
onchange
(
'zip'
)
def
_onchange_zip
(
record
):
if
record
.
country_id
.
name
==
"France"
and
record
.
zip
and
len
(
record
.
zip
)
==
5
:
department_code
=
record
.
zip
[:
2
]
if
not
record
.
department_id
or
record
.
department_id
.
code
!=
department_code
:
departments
=
record
.
env
[
'res.country.department'
].
search
([
(
'code'
,
'='
,
department_code
),
(
'country_id'
,
'='
,
record
.
country_id
.
id
)],
limit
=
2
)
if
len
(
departments
)
==
1
:
record
.
department_id
=
departments
[
0
]
elif
record
.
department_id
:
record
.
department_id
=
False
@
api
.
onchange
(
'department_id'
)
def
_onchange_department_id
(
record
):
if
record
.
department_id
and
record
.
department_id
.
state_id
!=
record
.
state_id
:
record
.
state_id
=
record
.
department_id
.
state_id
if
record
.
country_id
.
name
==
"France"
and
record
.
zip
and
len
(
record
.
zip
)
==
5
and
(
not
record
.
department_id
or
record
.
zip
[:
2
]
!=
record
.
department_id
.
code
):
record
.
zip
=
False
@
api
.
onchange
(
'state_id'
)
def
_onchange_state_id
(
record
):
if
record
.
state_id
and
record
.
state_id
.
country_id
!=
record
.
country_id
:
record
.
country_id
=
record
.
state_id
.
country_id
if
record
.
department_id
and
record
.
department_id
.
state_id
!=
record
.
state_id
:
record
.
department_id
=
False
@
api
.
onchange
(
'country_id'
)
def
_onchange_country_id
(
record
):
if
record
.
state_id
and
record
.
state_id
.
country_id
!=
record
.
country_id
:
record
.
state_id
=
False
# TODO: Add an option to manage default country?
def
_default_country
(
self
):
france_ids
=
self
.
env
[
'res.country'
].
name_search
(
'France'
,
operator
=
'='
,
limit
=
2
)
print
(
france_ids
)
if
len
(
france_ids
)
!=
1
:
# Default exit if France does not exists or there are too many Frances.. (should not happen?)
return
None
else
:
print
(
france_ids
[
0
])
print
(
france_ids
[
0
][
0
])
return
france_ids
[
0
][
0
]
models/site.py
View file @
1836b334
...
...
@@ -72,37 +72,37 @@ class CustomerSite(models.Model):
# TODO: Not very smart when changing the country...
@
api
.
onchange
(
'zip'
)
def
_onchange_zip
(
site
):
if
site
.
country_id
.
name
==
"France"
and
site
.
zip
and
len
(
site
.
zip
)
==
5
:
department_code
=
site
.
zip
[:
2
]
if
not
site
.
department_id
or
site
.
department_id
.
code
!=
department_code
:
departments
=
site
.
env
[
'res.country.department'
].
search
([
def
_onchange_zip
(
record
):
if
record
.
country_id
.
name
==
"France"
and
record
.
zip
and
len
(
record
.
zip
)
==
5
:
department_code
=
record
.
zip
[:
2
]
if
not
record
.
department_id
or
record
.
department_id
.
code
!=
department_code
:
departments
=
record
.
env
[
'res.country.department'
].
search
([
(
'code'
,
'='
,
department_code
),
(
'country_id'
,
'='
,
site
.
country_id
.
id
)],
(
'country_id'
,
'='
,
record
.
country_id
.
id
)],
limit
=
2
)
if
len
(
departments
)
==
1
:
site
.
department_id
=
departments
[
0
]
elif
site
.
department_id
:
site
.
department_id
=
False
record
.
department_id
=
departments
[
0
]
elif
record
.
department_id
:
record
.
department_id
=
False
@
api
.
onchange
(
'department_id'
)
def
_onchange_department_id
(
site
):
if
site
.
department_id
and
site
.
department_id
.
state_id
!=
site
.
state_id
:
site
.
state_id
=
site
.
department_id
.
state_id
if
site
.
country_id
.
name
==
"France"
and
site
.
zip
and
len
(
site
.
zip
)
==
5
and
(
not
site
.
department_id
or
site
.
zip
[:
2
]
!=
site
.
department_id
.
code
):
site
.
zip
=
False
def
_onchange_department_id
(
record
):
if
record
.
department_id
and
record
.
department_id
.
state_id
!=
record
.
state_id
:
record
.
state_id
=
record
.
department_id
.
state_id
if
record
.
country_id
.
name
==
"France"
and
record
.
zip
and
len
(
record
.
zip
)
==
5
and
(
not
record
.
department_id
or
record
.
zip
[:
2
]
!=
record
.
department_id
.
code
):
record
.
zip
=
False
@
api
.
onchange
(
'state_id'
)
def
_onchange_state_id
(
site
):
if
site
.
state_id
and
site
.
state_id
.
country_id
!=
site
.
country_id
:
site
.
country_id
=
site
.
state_id
.
country_id
if
site
.
department_id
and
site
.
department_id
.
state_id
!=
site
.
state_id
:
site
.
department_id
=
False
def
_onchange_state_id
(
record
):
if
record
.
state_id
and
record
.
state_id
.
country_id
!=
record
.
country_id
:
record
.
country_id
=
record
.
state_id
.
country_id
if
record
.
department_id
and
record
.
department_id
.
state_id
!=
record
.
state_id
:
record
.
department_id
=
False
@
api
.
onchange
(
'country_id'
)
def
_onchange_country_id
(
site
):
if
site
.
state_id
and
site
.
state_id
.
country_id
!=
site
.
country_id
:
site
.
state_id
=
False
def
_onchange_country_id
(
record
):
if
record
.
state_id
and
record
.
state_id
.
country_id
!=
record
.
country_id
:
record
.
state_id
=
False
# TODO: Add an option to manage default country?
def
_default_country
(
self
):
...
...
@@ -133,23 +133,23 @@ class CustomerSite(models.Model):
Both `source` is an individual records (i.e. a "singleton" recordset), while `self` may be a recordset.
"""
for
site
in
self
:
site
.
country_id
=
source
.
country_id
site
.
state_id
=
source
.
state_id
site
.
department_id
=
source
.
department_id
site
.
zip
=
source
.
zip
site
.
city
=
source
.
city
site
.
street2
=
source
.
street2
site
.
street
=
source
.
street
for
record
in
self
:
record
.
country_id
=
source
.
country_id
record
.
state_id
=
source
.
state_id
record
.
department_id
=
source
.
department_id
record
.
zip
=
source
.
zip
record
.
city
=
source
.
city
record
.
street2
=
source
.
street2
record
.
street
=
source
.
street
def
action_copy_address_from_partner
(
self
):
for
site
in
self
:
site
.
_copy_address_from
(
site
.
partner_id
)
for
record
in
self
:
record
.
_copy_address_from
(
record
.
partner_id
)
return
True
def
action_copy_address_from_contact
(
self
):
for
site
in
self
:
site
.
_copy_address_from
(
site
.
contact_id
)
for
record
in
self
:
record
.
_copy_address_from
(
record
.
contact_id
)
return
True
...
...
views/contact_views.xml
View file @
1836b334
<?xml version="1.0"?>
<odoo>
<record
id=
"customer_contact_view_form"
model=
"ir.ui.view"
>
<field
name=
"name"
>
customer.contact.form
</field>
<field
name=
"model"
>
customer.contact
</field>
<field
name=
"arch"
type=
"xml"
>
<form>
<sheet>
<h1>
<field
name=
"name"
/>
</h1>
<group
string=
"Contact et Adresse"
>
<group
name=
"partner"
>
<label
for=
"partner_id"
/>
<div
name=
"partner"
class=
"copiable_address"
>
<field
name=
"partner_id"
class=
"copiable_field"
/>
<button
string=
"Copier adresse"
class=
"copy_button"
name=
"action_copy_address_from_partner"
type=
"object"
/>
</div>
</group>
<group
name=
"address"
>
<label
for=
"street"
string=
"Adresse"
/>
<div
class=
"o_address_format o_city_zip_department o_state_country"
>
<field
name=
"street"
placeholder=
"Rue"
class=
"o_address_street"
/>
<field
name=
"street2"
placeholder=
"Rue 2"
class=
"o_address_street"
/>
<field
name=
"city"
placeholder=
"Ville"
class=
"o_address_city"
/>
<field
name=
"zip"
placeholder=
"Code postal"
class=
"o_address_zip"
/>
<field
name=
"department_id"
placeholder=
"Département"
class=
"o_address_department"
options=
"{'no_create': True, 'no_open': True}"
context=
"{'state_id': state_id, 'country_id': country_id}"
/>
<field
name=
"state_id"
placeholder=
"Région"
class=
"o_address_state"
options=
"{'no_create': True, 'no_open': True}"
context=
"{'country_id': country_id}"
/>
<field
name=
"country_id"
placeholder=
"Pays"
class=
"o_address_country"
options=
"{'no_create': True, 'no_open': True}"
/>
</div>
</group>
</group>
<group
string=
"Sites"
>
<field
name=
"site_ids"
/>
</group>
</sheet>
</form>
</field>
</record>
<record
id=
"customer_contact_action"
model=
"ir.actions.act_window"
>
<field
name=
"name"
>
Contact
</field>
<field
name=
"res_model"
>
customer.contact
</field>
...
...
views/partner_views.xml
View file @
1836b334
<?xml version="1.0"?>
<odoo>
<record
id=
"customer_partner_view_form"
model=
"ir.ui.view"
>
<field
name=
"name"
>
customer.partner.form
</field>
<field
name=
"model"
>
customer.partner
</field>
<field
name=
"arch"
type=
"xml"
>
<form>
<sheet>
<h1>
<field
name=
"name"
/>
</h1>
<group
string=
"Contact et Adresse"
>
<group
name=
"address"
>
<label
for=
"street"
string=
"Adresse"
/>
<div
class=
"o_address_format o_city_zip_department o_state_country"
>
<field
name=
"street"
placeholder=
"Rue"
class=
"o_address_street"
/>
<field
name=
"street2"
placeholder=
"Rue 2"
class=
"o_address_street"
/>
<field
name=
"city"
placeholder=
"Ville"
class=
"o_address_city"
/>
<field
name=
"zip"
placeholder=
"Code postal"
class=
"o_address_zip"
/>
<field
name=
"department_id"
placeholder=
"Département"
class=
"o_address_department"
options=
"{'no_create': True, 'no_open': True}"
context=
"{'state_id': state_id, 'country_id': country_id}"
/>
<field
name=
"state_id"
placeholder=
"Région"
class=
"o_address_state"
options=
"{'no_create': True, 'no_open': True}"
context=
"{'country_id': country_id}"
/>
<field
name=
"country_id"
placeholder=
"Pays"
class=
"o_address_country"
options=
"{'no_create': True, 'no_open': True}"
/>
</div>
</group>
</group>
<group
string=
"Contacts"
>
<field
name=
"contact_ids"
/>
</group>
<group
string=
"Sites"
>
<field
name=
"site_ids"
/>
</group>
</sheet>
</form>
</field>
</record>
<record
id=
"customer_partner_action"
model=
"ir.actions.act_window"
>
<field
name=
"name"
>
Compte
</field>
<field
name=
"res_model"
>
customer.partner
</field>
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment