Compare commits

...

No commits in common. '12.0' and '7.0' have entirely different histories.
12.0 ... 7.0

  1. 17
      .coveragerc
  2. 20
      .editorconfig
  3. 26
      .gitignore
  4. 35
      .travis.yml
  5. 4
      ChangeLog
  6. 15
      README.md
  7. 23
      __unported__/base_address_category/__init__.py
  8. 52
      __unported__/base_address_category/__openerp__.py
  9. 94
      __unported__/base_address_category/base_address.py
  10. 4
      __unported__/base_address_category/base_address_view.xml
  11. 3
      __unported__/base_address_category/security/ir.model.access.csv
  12. 4
      __unported__/base_address_category/security/security.xml
  13. 39
      __unported__/partner_address_ldap/__init__.py
  14. 74
      __unported__/partner_address_ldap/__openerp__.py
  15. 545
      __unported__/partner_address_ldap/address.py
  16. 115
      __unported__/partner_address_ldap/address_view.xml
  17. 81
      __unported__/partner_address_ldap/company.py
  18. 25
      __unported__/partner_address_ldap/company_view.xml
  19. 82
      __unported__/partner_address_ldap/i18n/fr_FR.po
  20. 50
      __unported__/partner_address_ldap/partner.py
  21. 4
      __unported__/partner_address_ldap/security/security.xml
  22. 10
      __unported__/partner_address_ldap/wizard.xml
  23. 32
      __unported__/partner_address_ldap/wizard/__init__.py
  24. 174
      __unported__/partner_address_ldap/wizard/wiz_import_adresses.py
  25. 22
      account_partner_merge/__init__.py
  26. 34
      account_partner_merge/__openerp__.py
  27. 17
      account_partner_merge/account_partner_merge_view.xml
  28. 40
      account_partner_merge/partner_merge.py
  29. 90
      animal/README.rst
  30. 3
      animal/__init__.py
  31. 29
      animal/__manifest__.py
  32. 25
      animal/data/animal.breed.csv
  33. 2
      animal/data/animal.color.csv
  34. 4
      animal/data/animal.species.csv
  35. 2
      animal/data/ir.module.category.csv
  36. 565
      animal/i18n/animal.pot
  37. 567
      animal/i18n/es.po
  38. 8
      animal/models/__init__.py
  39. 42
      animal/models/animal.py
  40. 12
      animal/models/animal_breed.py
  41. 14
      animal/models/animal_color.py
  42. 12
      animal/models/animal_species.py
  43. 3
      animal/readme/CONTRIBUTORS.rst
  44. 1
      animal/readme/DESCRIPTION.rst
  45. 3
      animal/readme/USAGE.rst
  46. 8
      animal/security/ir.model.access.csv
  47. 10
      animal/security/res_groups.xml
  48. BIN
      animal/static/description/icon.png
  49. 433
      animal/static/description/index.html
  50. BIN
      animal/static/img/avatar.png
  51. 156
      animal/views/animal.xml
  52. 53
      animal/views/animal_breed.xml
  53. 55
      animal/views/animal_color.xml
  54. 54
      animal/views/animal_species.xml
  55. 41
      animal/views/menu.xml
  56. 90
      animal_owner/README.rst
  57. 3
      animal_owner/__init__.py
  58. 16
      animal_owner/__manifest__.py
  59. 48
      animal_owner/i18n/animal_owner.pot
  60. 50
      animal_owner/i18n/es.po
  61. 3
      animal_owner/models/__init__.py
  62. 11
      animal_owner/models/animal.py
  63. 27
      animal_owner/models/res_partner.py
  64. 3
      animal_owner/readme/CONTRIBUTORS.rst
  65. 1
      animal_owner/readme/DESCRIPTION.rst
  66. 3
      animal_owner/readme/USAGE.rst
  67. BIN
      animal_owner/static/description/icon.png
  68. 433
      animal_owner/static/description/index.html
  69. 58
      animal_owner/views/animal.xml
  70. 22
      animal_owner/views/res_partner.xml
  71. 22
      base_contact/__init__.py
  72. 56
      base_contact/__openerp__.py
  73. 239
      base_contact/base_contact.py
  74. 29
      base_contact/base_contact_demo.xml
  75. 204
      base_contact/base_contact_view.xml
  76. 191
      base_contact/i18n/base_contact.pot
  77. 188
      base_contact/i18n/de.po
  78. 187
      base_contact/i18n/fr.po
  79. 26
      base_contact/tests/__init__.py
  80. 201
      base_contact/tests/test_base_contact.py
  81. 28
      base_contact_function/__init__.py
  82. 73
      base_contact_function/__openerp__.py
  83. 265
      base_contact_function/i18n/base_contact_function.pot
  84. 288
      base_contact_function/i18n/fr.po
  85. 97
      base_contact_function/res_partner.py
  86. 59
      base_contact_function/res_partner_category.py
  87. 47
      base_contact_function/res_partner_category_function.py
  88. 54
      base_contact_function/res_partner_category_view.xml
  89. 51
      base_contact_function/res_partner_function.py
  90. 43
      base_contact_function/res_partner_function_view.xml
  91. 527
      base_contact_function/res_partner_view.xml
  92. 3
      base_contact_function/security/ir.model.access.csv
  93. 31
      base_contact_function/tests/__init__.py
  94. 105
      base_contact_function/tests/test_functions.py
  95. 193
      base_contact_function/tests/test_partner_category.py
  96. 23
      base_contact_function_partner_firstname/__init__.py
  97. 51
      base_contact_function_partner_firstname/__openerp__.py
  98. 27
      base_contact_function_partner_firstname/i18n/base_contact_function_partner_firstname.pot
  99. 27
      base_contact_function_partner_firstname/i18n/fr.po
  100. 79
      base_contact_function_partner_firstname/res_partner.py

17
.coveragerc

@ -0,0 +1,17 @@
# Config file .coveragerc
[report]
include =
*/partner-contact/*
omit =
*/tests/*
*__init__.py
# Regexes for lines to exclude from consideration
exclude_lines =
# Have to re-enable the standard pragma
pragma: no cover
# Don't complain about null context checking
if context is None:

20
.editorconfig

@ -1,20 +0,0 @@
# Configuration for known file extensions
[*.{css,js,json,less,md,py,rst,sass,scss,xml,yaml,yml}]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[.eslintrc,*.{json,yml,yaml,rst,md}]
indent_size = 2
# Do not configure editor for libs and autogenerated content
[*/static/{lib,src/lib}/**,*/static/description/index.html,*/readme/../README.rst]
charset = unset
end_of_line = unset
indent_size = unset
indent_style = unset
insert_final_newline = false
trim_trailing_whitespace = false

26
.gitignore

@ -1,3 +1,11 @@
# buildout
tools/.*
tools/bin
tools/develop-eggs
tools/eggs
tools/etc
tools/parts
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
@ -6,8 +14,6 @@ __pycache__/
*.so
# Distribution / packaging
.Python
env/
bin/
build/
develop-eggs/
@ -21,14 +27,12 @@ var/
*.egg-info/
.installed.cfg
*.egg
*.eggs
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.cache
@ -41,20 +45,6 @@ coverage.xml
# Pycharm
.idea
# Mr Developer
.mr.developer.cfg
.project
.pydevproject
# Rope
.ropeproject
# Sphinx documentation
docs/_build/
# Backup files
*~
*.swp
# OSX Files
*.DS_Store

35
.travis.yml

@ -2,7 +2,6 @@ sudo: false
cache: pip
addons:
postgresql: "9.6"
apt:
packages:
- expect-dev # provides unbuffer utility
@ -11,30 +10,26 @@ addons:
language: python
python:
- "3.5"
stages:
- linting
- test
jobs:
include:
- stage: linting
env:
- LINT_CHECK="1"
- stage: test
env:
- TESTS="1" ODOO_REPO="odoo/odoo" MAKEPOT="1"
- stage: test
env:
- TESTS="1" ODOO_REPO="OCA/OCB"
- "2.7"
env:
global:
- VERSION="12.0" TESTS="0" LINT_CHECK="0" TRANSIFEX="0"
- VERSION="7.0" TESTS="0" LINT_CHECK="0" TRANSIFEX="0"
- TRANSIFEX_USER='transbot@odoo-community.org'
- secure: "E0Y9b59yzsYaSZUG8aVdRbWHltEUBz8EIXla06YNLNsP0bsWUIWcgkVNCMhgQHBlBEGWZJ/2hcX+frDNx65IscEQrwHgfOM86AW32krRy6vKkkgQuhC04UM91dkp/bqT/bjP4Jzsn0khikBIa4mVA6ou2t1D4f3h5njhLdlAXHM="
matrix:
- LINT_CHECK="1"
- TRANSIFEX="1"
- TESTS="1" ODOO_REPO="odoo/odoo"
- TESTS="1" ODOO_REPO="OCA/OCB"
virtualenv:
system_site_packages: true
install:
- git clone --depth=1 https://github.com/OCA/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools
- git clone https://github.com/OCA/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools
- export PATH=${HOME}/maintainer-quality-tools/travis:${PATH}
- travis_install_nightly

4
ChangeLog

@ -0,0 +1,4 @@
2015-10-01 - Maxime Chambreuil
* removed symbolic links for better_zip. Use base_location directly in your instance.
Be careful when migrating as the xml ids have changed.

15
README.md

@ -1,13 +1,12 @@
[![Runbot Status](https://runbot.odoo-community.org/runbot/badge/flat/134/12.0.svg)](https://runbot.odoo-community.org/runbot/repo/github-com-oca-partner-contact-134)
[![Build Status](https://travis-ci.org/OCA/partner-contact.svg?branch=12.0)](https://travis-ci.org/OCA/partner-contact)
[![Coverage Status](https://coveralls.io/repos/OCA/partner-contact/badge.svg?branch=12.0)](https://coveralls.io/r/OCA/partner-contact?branch=12.0)
[![Build Status](https://travis-ci.org/OCA/partner-contact.svg?branch=7.0)](https://travis-ci.org/OCA/partner-contact)
[![Coverage Status](https://coveralls.io/repos/OCA/partner-contact/badge.svg?branch=7.0)](https://coveralls.io/r/OCA/partner-contact?branch=7.0)
OCA partner and contact management modules for Odoo
===================================================
OCA partner and contact management modules for OpenERP
======================================================
This project is meant to gather all community extensions about partner and contact management for Odoo.
This project is meant to gather all community extensions about partner and contact management for OpenERP.
Here you should find community modules that:
Here you should find all community modules that:
* Enable isolated contact management.
* Add first name, birth name, street number and other extensions for the partners.
@ -18,7 +17,7 @@ Here you should find community modules that:
Translation Status
------------------
[![Transifex Status](https://www.transifex.com/projects/p/OCA-partner-contact-12-0/chart/image_png)](https://www.transifex.com/projects/p/OCA-partner-contact-12-0)
[![Transifex Status](https://www.transifex.com/projects/p/OCA-partner-contact-7-0/chart/image_png)](https://www.transifex.com/projects/p/OCA-partner-contact-7-0)
----

23
__unported__/base_address_category/__init__.py

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2010-2013 Camptocamp SA (http://www.camptocamp.com)
# All Right Reserved
#
# Author : Nicolas Bessi (Camptocamp), Joel Grand-Guillaume
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import base_address

52
__unported__/base_address_category/__openerp__.py

@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2010-2013 Camptocamp SA (http://www.camptocamp.com)
# All Right Reserved
#
# Author : Nicolas Bessi (Camptocamp), Joel Grand-Guillaume
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{
"name": "Partner Address Category",
"description": """\
res.partner.address.category
----------------------------
This module is deprecated as of OpenERP 7.0, because that version
deprecated res.partner.address, and res.partner already has multi
category support (visible as Tags in the user interface).
The port of this module to OpenERP 7 keeps the model definitions, but
removes the views (for which the base views are no longer
available). The migration process should ensure that the
res.partner.address.category records are migrated to
res.partner.category records.
""",
"version": "1.2",
"author": "Camptocamp,Odoo Community Association (OCA)",
"category": "Generic Modules/Base",
"website": "http://www.camptocamp.com",
"depends": [
"base",
],
"data": [
"security/security.xml"
'security/ir.model.access.csv',
],
"installable": False,
}

94
__unported__/base_address_category/base_address.py

@ -0,0 +1,94 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2010-2013 Camptocamp SA (http://www.camptocamp.com)
# All Right Reserved
#
# Author : Nicolas Bessi (Camptocamp), Joel Grand-Guillaume
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#
##############################################################################
from openerp.osv import fields, orm
class ResPartnerAdressCategory(orm.Model):
def name_get(self, cr, uid, ids, context=None):
if not len(ids):
return []
reads = self.read(cr, uid, ids, ['name', 'parent_id'], context)
res = []
for record in reads:
name = record['name']
if record['parent_id']:
name = '%s / %s ' % (record['parent_id'][1], name)
res.append((record['id'], name))
return res
def _name_get_fnc(self, cr, uid, ids, prop, unknow_none, unknow_dict):
res = self.name_get(cr, uid, ids)
return dict(res)
def _check_recursion(self, cr, uid, ids):
level = 100
while ids:
cr.execute('select distinct parent_id '
'from res_partner_address_category '
'where id in %s', ids)
ids = [parent_id for (parent_id,) in cr.fetchall() if parent_id]
if not level:
return False
level -= 1
return True
_name = 'res.partner.address.category'
_description = 'Partner address Categories'
_columns = {
'name': fields.char('Category Name', required=True, size=64),
'parent_id': fields.many2one('res.partner.address.category',
'Parent Category',
select=True),
'complete_name': fields.function(_name_get_fnc,
type="char",
string='Name'),
'child_ids': fields.one2many('res.partner.address.category',
'parent_id',
'Children Category'),
'active': fields.boolean('Active'),
}
_constraints = [
(_check_recursion,
'Error: you can not create recursive categories.',
['parent_id'])
]
_defaults = {
'active': lambda *a: 1,
}
_order = 'parent_id,name'
class ResPartnerAddress(orm.Model):
_inherit = "res.partner.address"
_columns = {
'category_id': fields.many2many('res.partner.address.category',
'res_partner_address_category_rel',
'adress_id',
'category_id',
'Address categories'),
}

4
__unported__/base_address_category/base_address_view.xml

@ -0,0 +1,4 @@
<openerp>
<data>
</data>
</openerp>

3
__unported__/base_address_category/security/ir.model.access.csv

@ -0,0 +1,3 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"access_res_partner_address_category_group_user","res.partner.address.category","model_res_partner_address_category",base.group_user,1,0,0,0
"access_res_partner_address_category_partner_manager","res.partner.address.category","model_res_partner_address_category","base.group_partner_manager",1,1,1,1

4
__unported__/base_address_category/security/security.xml

@ -0,0 +1,4 @@
<openerp>
<data>
</data>
</openerp>

39
__unported__/partner_address_ldap/__init__.py

@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2010 Camptocamp SA (http://www.camptocamp.com)
# All Right Reserved
#
# Author : Nicolas Bessi (Camptocamp),
# Thanks to Laurent Lauden for his code adaptation
# Contribution : Joel Grand-Guillaume
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from . import (
address,
partner,
company,
wizard,
)

74
__unported__/partner_address_ldap/__openerp__.py

@ -0,0 +1,74 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2010 Camptocamp SA (http://www.camptocamp.com)
# All Right Reserved
#
# Author : Nicolas Bessi (Camptocamp),
# Thanks to Laurent Lauden for his code adaptation
# Active directory Donor: M. Benadiba (Informatique Assistances.fr)
# Contribution : Joel Grand-Guillaume
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
{
"name": "Partner synchronization from OpenERP to ldap",
"version": "1.2",
"author": "Camptocamp,Odoo Community Association (OCA)",
"depends": ["base"],
"category": "Generic Modules/Misc",
"website": "http://www.camptocamp.com",
"description": """
Live partner address synchronization through a LDAP module (inetOrgPerson).
OpenERP becomes the master of the LDAP. Each time an address is deleted,
created or updated the same is done in the ldap (a new record is pushed).
The LDAP configuration is done in the company view. There can be one different
LDAP per company. Do not forget to activate
the LDAP link in the configuration.
The used LDAP depends on the current user company.
This module does not allows bulk batching synchronisation into the LDAP and is
thus not suitable for an instant use with an existing LDAP.
In order to use it with an existing LDAP you have to alter the uid of contact
in your LDAP. The uid should be terp_ plus the OpenERP
contact id (for example terp_10).
N.B:
The module requires the python-ldap library
Unicode support --> As python ldap does not support unicode we try to decode
string if it fails we transliterate values.
Active Directory Support for Windows server 2003, try 2008 at your own risk
(AD support not tested for Version 6 of OpenERP looking for active dir test
infra)
""",
"data": [
"security/security.xml"
'company_view.xml',
'address_view.xml',
"wizard.xml",
],
"installable": False
}

545
__unported__/partner_address_ldap/address.py

@ -0,0 +1,545 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2010-2011 Camptocamp SA (http://www.camptocamp.com)
# All Right Reserved
#
# Author : Nicolas Bessi (Camptocamp),
# Thanks to Laurent Lauden for his code adaptation
# Active directory Donor: M. Benadiba (Informatique Assistances.fr)
# Contribution : Joel Grand-Guillaume
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly advised to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
# TODO Find why company parameter are cached
import re
import unicodedata
from openerp import netsvc
try:
import ldap
import ldap.modlist
except:
print('python ldap not installed please install it in order to use this '
'module')
from openerp.osv import orm, fields
from openerp.tools.translate import _
logger = netsvc.Logger()
class LdapConnMApper(object):
"""LdapConnMApper: push specific fields from the Terp
Partner_contacts to the LDAP schema inetOrgPerson. Ldap bind options
are stored in company.
"""
def __init__(self, cursor, uid, osv_obj, context=None):
"""Initialize connexion to ldap by using parameter set in the
current user compagny
"""
logger.notifyChannel("MY TOPIC", netsvc.LOG_DEBUG,
_('Initalize LDAP CONN'))
self.USER_DN = ''
self.CONTACT_DN = ''
self.LDAP_SERVER = ''
self.PASS = ''
self.OU = ''
self.connexion = ''
self.ACTIVDIR = False
# Reading ldap pref
user = osv_obj.pool.get('res.users').browse(
cursor, uid, uid, context=context
)
company = user.company_id
self.USER_DN = company.base_dn
self.CONTACT_DN = company.contact_dn
self.LDAP_SERVER = company.ldap_server
self.PASS = company.passwd
self.PORT = company.ldap_port
self.OU = company.ounit
self.ACTIVDIR = company.is_activedir
mand = (
self.USER_DN,
self.CONTACT_DN,
self.LDAP_SERVER,
self.PASS, self.OU
)
if company.ldap_active:
for param in mand:
if not param:
raise orm.except_orm(
_('Warning !'),
_('An LDAP parameter is missing for company %s') %
(company.name, )
)
def get_connexion(self):
"""create a new ldap connexion"""
logger.notifyChannel(
"LDAP Address",
netsvc.LOG_DEBUG,
_('connecting to server ldap %s') % (self.LDAP_SERVER, )
)
if self.PORT:
self.connexion = ldap.open(self.LDAP_SERVER, self.PORT)
else:
self.connexion = ldap.open(self.LDAP_SERVER)
self.connexion.simple_bind_s(self.USER_DN, self.PASS)
return self.connexion
class LDAPAddress(orm.Model):
"""Override the CRUD of the objet in order to dynamically bind to ldap"""
_inherit = 'res.partner.address'
ldapMapper = None
def init(self, cr):
logger = netsvc.Logger()
try:
logger.notifyChannel(
_('LDAP address init'),
netsvc.LOG_INFO,
_('try to ALTER TABLE res_partner_address RENAME '
'column name to lastname ;'))
cr.execute(
'ALTER TABLE res_partner_address'
'RENAME column name to lastname ;'
)
except Exception:
cr.rollback()
logger.notifyChannel(
_('LDAP address init'),
netsvc.LOG_INFO,
_('Warning ! impossible to rename column name into lastname, '
'this is probably already done or does not exist')
)
def _compute_name(self, firstname, lastname):
firstname = firstname or u''
lastname = lastname or u''
firstname = (u' ' + firstname).rstrip()
return u"%s%s" % (lastname, firstname)
def _name_get_fnc(self, cursor, uid, ids, name, arg, context=None):
"""Get the name (lastname + firstname), otherwise ''"""
if not ids:
return []
reads = self.read(cursor, uid, ids, ['lastname', 'firstname'])
res = []
for record in reads:
# We want to have " firstname" or ""
name = self._compute_name(record['firstname'], record['lastname'])
res.append((record['id'], name))
return dict(res)
# TODO get the new version of name search not vulnerable to sql injections
# def name_search(
# self, cursor, user, name, args=None, operator='ilike',
# context=None, limit=100):
# if not context: context = {}
# prep_name = '.*%s.*' %(name)
# cursor.execute(("""
# select id from res_partner_address where
# (to_ascii(convert( lastname, 'UTF8', 'LATIN1'),'LATIN-1') ~* '%s'
# or to_ascii(convert( firstname, 'UTF8', 'LATIN1'),'LATIN-1') ~* '%s')
# limit %s""") % (prep_name, prep_name, limit))
# res = cursor.fetchall()
# if res:
# res = [x[0] for x in res]
# else:
# res = []
# search in partner name to know if we are searching partner...
# partner_obj=self.pool.get('res.partner')
# part_len = len(res)-limit
# if part_len > 0:
# partner_res = partner_obj.search(
# cursor, user, [('name', 'ilike', name)],
# limit=part_len, context=context
# )
# for p in partner_res:
# addresses = partner_obj.browse(cursor, user, p).address
# Take each contact and add it to
# for add in addresses:
# res.append(add.id)
# return self.name_get(cursor, user, res, context)
_columns = {
'firstname': fields.char('First name', size=256),
'lastname': fields.char('Last name', size=256),
'name': fields.function(
_name_get_fnc,
method=True,
type="char",
size=512,
store=True,
string='Contact Name',
help='Name generated from the first name and last name',
nodrop=True
),
'private_phone': fields.char('Private phone', size=128),
}
def create(self, cursor, uid, vals, context=None):
if context is None:
context = {}
self.getconn(cursor, uid, {})
ids = None
self.validate_entries(vals, cursor, uid, ids)
tmp_id = super(LDAPAddress, self).create(cursor, uid,
vals, context)
if self.ldaplinkactive(cursor, uid, context):
self.saveLdapContact(tmp_id, vals, cursor, uid, context)
return tmp_id
def write(self, cursor, uid, ids, vals, context=None):
context = context or {}
self.getconn(cursor, uid, {})
if not isinstance(ids, list):
ids = [ids]
if ids:
self.validate_entries(vals, cursor, uid, ids)
if context.get('init_mode'):
success = True
else:
success = super(LDAPAddress, self).write(cursor, uid, ids,
vals, context)
if self.ldaplinkactive(cursor, uid, context):
for address_id in ids:
self.updateLdapContact(address_id, vals, cursor, uid, context)
return success
def unlink(self, cursor, uid, ids, context=None):
if context is None:
context = {}
if ids:
self.getconn(cursor, uid, {})
if not isinstance(ids, list):
ids = [ids]
if self.ldaplinkactive(cursor, uid, context):
for id in ids:
self.removeLdapContact(id, cursor, uid)
return super(LDAPAddress, self).unlink(cursor, uid, ids)
def validate_entries(self, vals, cursor, uid, ids):
"""Validate data of an address based on the inetOrgPerson schema"""
for val in vals:
try:
if isinstance(vals[val], basestring):
vals[val] = unicode(vals[val].decode('utf8'))
except UnicodeError:
logger.notifyChannel('LDAP encode', netsvc.LOG_DEBUG,
'cannot unicode ' + vals[val])
if ids is not None:
if isinstance(ids, (int, long)):
ids = [ids]
if len(ids) == 1:
self.addNeededFields(ids[0], vals, cursor, uid)
email = vals.get('email', False)
phone = vals.get('phone', False)
fax = vals.get('fax', False)
mobile = vals.get('mobile', False)
private_phone = vals.get('private_phone', False)
if email:
if re.match("^.+\\@(\\[?)[a-zA-Z0-9\\-\\.]+\\."
"([a-zA-Z]{2,3}|[0-9]{1,3})(\\]?)$",
email) is None:
raise orm.except_orm(_('Warning !'),
_('Please enter a valid e-mail'))
phones = (('phone', phone), ('fax', fax), ('mobile', mobile),
('private_phone', private_phone))
for phone_tuple in phones:
phone_number = phone_tuple[1]
if phone_number:
if not phone_number.startswith('+'):
raise orm.except_orm(
_('Warning !'),
_('Please enter a valid phone number in %s'
' international format (i.e. leading +)')
% phone_tuple[0])
def getVals(self, att_name, key, vals, dico, uid, ids, cr, context=None):
"""map to values to dict"""
if not context:
context = {}
# We explicitly test False value
if vals.get(key, False) is not False:
dico[att_name] = vals[key]
else:
if context.get('init_mode'):
return False
tmp = self.read(cr, uid, ids, [key], context={})
if tmp.get(key, False):
dico[att_name] = tmp[key]
def _un_unicodize_buf(self, in_buf):
if isinstance(in_buf, unicode):
try:
return in_buf.encode()
except Exception:
return unicodedata.normalize("NFKD", in_buf).encode(
'ascii', 'ignore'
)
return in_buf
def unUnicodize(self, indict):
"""remove unicode data of modlist as unicode is not supported
by python-ldap library till version 2.7
"""
for key in indict:
if not isinstance(indict[key], list):
indict[key] = self._un_unicodize_buf(indict[key])
else:
nonutfArray = []
for val in indict[key]:
nonutfArray.append(self._un_unicodize_buf(val))
indict[key] = nonutfArray
def addNeededFields(self, id, vals, cursor, uid):
previousvalue = self.browse(cursor, uid, [id])[0]
if not vals.get('partner_id'):
vals['partner_id'] = previousvalue.partner_id.id
values_to_check = ('email', 'phone', 'fax', 'mobile', 'firstname',
'lastname', 'private_phone', 'street', 'street2')
for val in values_to_check:
if not vals.get(val):
vals[val] = previousvalue[val]
def mappLdapObject(self, id, vals, cr, uid, context):
"""Mapp ResPArtner adress to moddlist"""
self.addNeededFields(id, vals, cr, uid)
conn = self.getconn(cr, uid, {})
partner_obj = self.pool.get('res.partner')
part_name = partner_obj.browse(cr, uid, vals['partner_id']).name
vals['partner'] = part_name
name = self._compute_name(vals.get('firstname'), vals.get('lastname'))
if name:
cn = name
else:
cn = part_name
if not vals.get('lastname'):
vals['lastname'] = part_name
contact_obj = {'objectclass': ['inetOrgPerson'],
'uid': ['terp_' + str(id)],
'ou': [conn.OU],
'cn': [cn],
'sn': [vals['lastname']]}
if not vals.get('street'):
vals['street'] = u''
if not vals.get('street2'):
vals['street2'] = u''
street_key = 'street'
if self.getconn(cr, uid, {}).ACTIVDIR:
# ENTERING THE M$ Realm and it is weird
# We manage the address
street_key = 'streetAddress'
contact_obj[street_key] = vals['street'] + "\r\n" + vals['street2']
# we modifiy the class
contact_obj['objectclass'] = [
'top',
'person',
'organizationalPerson',
'inetOrgPerson',
'user',
]
# we handle the country
if vals.get('country_id'):
country = self.browse(cr, uid, id, context=context).country_id
if country:
vals['country_id'] = country.name
vals['c'] = country.code
else:
vals['country_id'] = False
vals['c'] = False
if vals.get('country_id', False):
self.getVals(
'co', 'country_id', vals, contact_obj, uid, id, cr,
context
)
self.getVals('c', 'c', vals, contact_obj, uid, id, cr, context)
# we compute the display name
vals['display'] = '%s %s' % (vals['partner'], contact_obj['cn'][0])
# we get the title
if self.browse(cr, uid, id).function:
contact_obj['description'] = self.browse(
cr, uid, id).function.name
# we replace carriage return
if vals.get('comment', False):
vals['comment'] = vals['comment'].replace("\n", "\r\n")
# Active directory specific fields
self.getVals(
'company', 'partner', vals, contact_obj, uid, id, cr, context
)
self.getVals(
'info', 'comment', vals, contact_obj, uid, id, cr, context
)
self.getVals(
'displayName', 'partner', vals, contact_obj, uid, id, cr,
context
)
# Web site management
if self.browse(cr, uid, id).partner_id.website:
vals['website'] = self.browse(cr, uid, id).partner_id.website
self.getVals(
'wWWHomePage', 'website', vals, contact_obj, uid, id, cr,
context
)
del(vals['website'])
self.getVals(
'title', 'title', vals, contact_obj, uid, id, cr, context
)
else:
contact_obj[street_key] = vals['street'] + u"\n" + vals['street2']
self.getVals(
'o', 'partner', vals, contact_obj, uid, id, cr, context
)
# Common attributes
self.getVals(
'givenName', 'firstname', vals, contact_obj, uid, id, cr, context
)
self.getVals('mail', 'email', vals, contact_obj, uid, id, cr, context)
self.getVals(
'telephoneNumber', 'phone', vals, contact_obj, uid, id, cr, context
)
self.getVals('l', 'city', vals, contact_obj, uid, id, cr, context)
self.getVals(
'facsimileTelephoneNumber', 'fax', vals, contact_obj, uid, id, cr,
context
)
self.getVals(
'mobile', 'mobile', vals, contact_obj, uid, id, cr, context
)
self.getVals(
'homePhone', 'private_phone', vals, contact_obj, uid, id, cr,
context
)
self.getVals(
'postalCode', 'zip', vals, contact_obj, uid, id, cr, context
)
self.unUnicodize(contact_obj)
return contact_obj
def saveLdapContact(self, id, vals, cursor, uid, context=None):
"""save openerp adress to ldap"""
contact_obj = self.mappLdapObject(id, vals, cursor, uid, context)
conn = self.connectToLdap(cursor, uid, context=context)
try:
if self.getconn(cursor, uid, context).ACTIVDIR:
conn.connexion.add_s(
"CN=%s,OU=%s,%s" % (contact_obj['cn'][0],
conn.OU,
conn.CONTACT_DN),
ldap.modlist.addModlist(contact_obj)
)
else:
conn.connexion.add_s(
"uid=terp_%s,OU=%s,%s" % (str(id),
conn.OU,
conn.CONTACT_DN),
ldap.modlist.addModlist(contact_obj))
except Exception:
raise
conn.connexion.unbind_s()
def updateLdapContact(self, id, vals, cursor, uid, context):
"""update an existing contact with the data of OpenERP"""
conn = self.connectToLdap(cursor, uid, context={})
try:
old_contatc_obj = self.getLdapContact(conn, id)
except ldap.NO_SUCH_OBJECT:
self.saveLdapContact(id, vals, cursor, uid, context)
return
contact_obj = self.mappLdapObject(id, vals, cursor, uid, context)
if conn.ACTIVDIR:
modlist = []
for key, val in contact_obj.items():
if key in ('cn', 'uid', 'objectclass'):
continue
if isinstance(val, list):
val = val[0]
modlist.append((ldap.MOD_REPLACE, key, val))
else:
modlist = ldap.modlist.modifyModlist(
old_contatc_obj[1],
contact_obj
)
try:
conn.connexion.modify_s(old_contatc_obj[0], modlist)
conn.connexion.unbind_s()
except Exception:
raise
def removeLdapContact(self, id, cursor, uid):
"""Remove a contact from ldap"""
conn = self.connectToLdap(cursor, uid, context={})
to_delete = None
try:
to_delete = self.getLdapContact(conn, id)
except ldap.NO_SUCH_OBJECT:
logger.notifyChannel("Warning", netsvc.LOG_INFO,
_("'no object to delete in ldap' %s") % (id))
except Exception:
raise
try:
if to_delete:
conn.connexion.delete_s(to_delete[0])
conn.connexion.unbind_s()
except Exception:
raise
def getLdapContact(self, conn, id):
result = conn.connexion.search_ext_s(
"ou=%s, %s" % (conn.OU, conn.CONTACT_DN),
ldap.SCOPE_SUBTREE,
"(&(objectclass=*)(uid=terp_" + str(id) + "))"
)
if not result:
raise ldap.NO_SUCH_OBJECT
return result[0]
def ldaplinkactive(self, cursor, uid, context=None):
"""Check if ldap is activated for this company"""
users_pool = self.pool['res.users']
user = users_pool.browse(cursor, uid, uid, context=context)
return user.company_id.ldap_active
def getconn(self, cursor, uid, context=None):
"""LdapConnMApper"""
if not self.ldapMapper:
self.ldapMapper = LdapConnMApper(cursor, uid, self)
return self.ldapMapper
def connectToLdap(self, cursor, uid, context=None):
"""Reinitialize ldap connection"""
# getting ldap pref
if not self.ldapMapper:
self.getconn(cursor, uid, context)
self.ldapMapper.get_connexion()
return self.ldapMapper

115
__unported__/partner_address_ldap/address_view.xml

@ -0,0 +1,115 @@
<openerp>
<data>
#---------------------------------------------------------------------------------------------------------
# Partner form->contact and tree view of address
#---------------------------------------------------------------------------------------------------------
<record model="ir.ui.view" id="base.view_partner_address_tree">
<field name="name">res.partner.address.tree</field>
<field name="model">res.partner.address</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Partner contacts">
<field name="partner_id" />
<field name="firstname" />
<field name="lastname" />
<field name="phone"/>
<field name="email"/>
<field name="zip"/>
<field name="city"/>
<field name="country_id"/>
<field name="type"/>
</tree>
</field>
</record>
<record model="ir.ui.view" id="view_partner_address_form1_inherit_ldap2">
<field name="name">res.partner.address.form1.c2c_partner_adress</field>
<field name="model">res.partner.address</field>
<field name="type">form</field>
<field name="inherit_id" ref="base.view_partner_address_form1" />
<field name="priority" eval="16" />
<field name="arch" type="xml">
<field name="name" position="replace">
<field name="name" invisible="1" select="2"/>
<field name="firstname"/>
<field name="lastname"/>
<newline/>
</field>
</field>
</record>
<record model="ir.ui.view" id="view_partner_address_form1_inherit_ldap">
<field name="name">res.partner.address.form1.c2c_partner_adress</field>
<field name="model">res.partner.address</field>
<field name="type">form</field>
<field name="inherit_id" ref="base.view_partner_address_form1" />
<field name="priority" eval="17" />
<field name="arch" type="xml">
<field name="mobile" position="after">
<field name="private_phone"/>
</field>
</field>
</record>
<!--
=========================================
the short form used in the partner form
=========================================
-->
<record model="ir.ui.view" id="view_partner_address_form_inerit_ldap">
<field name="name">res.partner.address.form2_c2c_partner_address</field>
<field name="model">res.partner.address</field>
<field name="type">form</field>
<field name="priority" eval="16" />
<field name="inherit_id" ref="base.view_partner_address_form2" />
<field name="arch" type="xml">
<field name="name" select="1" position="replace">
<field name="name" invisible="1" select="2"/>
<field name="firstname"/>
<field name="lastname"/>
<newline/>
</field>
</field>
</record>
<record model="ir.ui.view" id="view_partner_address_form2_inerit_ldap">
<field name="name">res.partner.address.form2_c2c_partner_address</field>
<field name="model">res.partner.address</field>
<field name="type">form</field>
<field name="inherit_id" ref="base.view_partner_address_form2" />
<field name="priority" eval="17" />
<field name="arch" type="xml">
<field name="mobile" position="after">
<field name="private_phone"/>
</field>
</field>
</record>
<!--
=========================================
the short form used in the res_partner standard form
=========================================
-->
<record model="ir.ui.view" id="view_partner_form_inherite_contact_ldap">
<field name="name">res.partner.form2_partner_address</field>
<field name="model">res.partner</field>
<field name="type">form</field>
<field name="inherit_id" ref="base.view_partner_form" />
<field name="arch" type="xml">
<xpath expr="/form/notebook/page/field[@name='address']/form/group/field[@name='mobile']" position="after">
<field name="private_phone"/>
</xpath>
<xpath expr="/form/notebook/page/field[@name='address']/form/group/field[@name='name']" position="replace">
<field name="firstname"/>
<field name="lastname"/>
<newline/>
</xpath>
</field>
</record>
</data>
</openerp>

81
__unported__/partner_address_ldap/company.py

@ -0,0 +1,81 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2010 Camptocamp SA (http://www.camptocamp.com)
# All Right Reserved
#
# Author : Nicolas Bessi (Camptocamp),
# Thanks to Laurent Lauden for his code adaptation
# Active directory Donor: M. Benadiba (Informatique Assistances.fr)
# Contribution : Joel Grand-Guillaume
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from openerp.osv import orm, fields
class Res_company(orm.Model):
"""Define ldap connexion parameters"""
_inherit = 'res.company'
_columns = {
'base_dn': fields.char(
'User dn',
size=128,
help="Example: cn=contacts_admin,dc=ldap,dc=dcc2c"
),
'contact_dn': fields.char(
'Bind dn',
size=128,
help="Example: dc=ldap,dc=dcc2c -- watch out "
"the OU will be automatically included inside"
),
'ounit': fields.char(
'Contact Organizational unit of the contacts',
size=128,
help="Example: Contacts"
),
'ldap_server': fields.char(
'Server address',
size=128,
help="Example: ldap.camptocamp.com"
),
'passwd': fields.char(
'ldap password',
size=128,
help="Example: Mypassword1234"
),
'ldap_active': fields.boolean(
'Activate ldap link for this company',
help='If not check nothing will be reported into the ldap'
),
'is_activedir': fields.boolean(
'Active Directory ?',
help='The ldap is part of an Active Directory'
),
'ldap_port': fields.integer(
'LDAP Port',
help="If not specified, the default port(389), will be used"
),
}

25
__unported__/partner_address_ldap/company_view.xml

@ -0,0 +1,25 @@
<?xml version="1.0"?>
<openerp>
<data>
<record model="ir.ui.view" id="view_company_for_ldap">
<field name="name">res.company.form</field>
<field name="model">res.company</field>
<field name="inherit_id" ref="base.view_company_form"/>
<field name="type">form</field>
<field name="arch" type="xml">
<page string="Configuration" position="after">
<page string="LDAP">
<field name="ldap_active" />
<field name="is_activedir" attrs="{'readonly':[('ldap_active', '=', False)]}"/>
<field name="ldap_server" attrs="{'readonly':[('ldap_active', '=', False)]}"/>
<field name="ldap_port" attrs="{'readonly':[('ldap_active', '=', False)]}"/>
<field name="base_dn" attrs="{'readonly':[('ldap_active', '=', False)]}"/>
<field name="contact_dn" attrs="{'readonly':[('ldap_active', '=', False)]}"/>
<field name="ounit" attrs="{'readonly':[('ldap_active', '=', False)]}"/>
<field name="passwd" attrs="{'readonly':[('ldap_active', '=', False)]}"/>
</page>
</page>
</field>
</record>
</data>
</openerp>

82
__unported__/partner_address_ldap/i18n/fr_FR.po

@ -0,0 +1,82 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * c2c_contact_to_ldap
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 5.0.7\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2010-02-01 15:06:40+0000\n"
"PO-Revision-Date: 2010-02-01 15:06:40+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: c2c_contact_to_ldap
#: field:res.company,is_activedir:0
msgid "Active Directory ?"
msgstr ""
#. module: c2c_contact_to_ldap
#: model:ir.actions.wizard,name:c2c_contact_to_ldap.ldap_import_adresses
msgid "Export addresses to company LDAP"
msgstr "Exporte les adresses de OpenERP vers LDAP"
#. module: c2c_contact_to_ldap
#: wizard_field:ldap.import_adresses,importadd,errors:0
msgid "Error report"
msgstr "Rapport d'erreur"
#. module: c2c_contact_to_ldap
#: constraint:ir.ui.view:0
msgid "Invalid XML for View Architecture!"
msgstr "XML non valide pour l'architecture de la vue"
#. module: c2c_contact_to_ldap
#: wizard_button:ldap.import_adresses,init,importadd:0
msgid "Export adresses into company LDAP"
msgstr "Exportation des adresses vers le ldap spécifié dans la société"
#. module: c2c_contact_to_ldap
#: wizard_view:ldap.import_adresses,importadd:0
msgid "Export log"
msgstr "Log d'exportation"
#. module: c2c_contact_to_ldap
#: wizard_view:ldap.import_adresses,importadd:0
msgid "Clic on 'Save as' to save the log file :"
msgstr "Cliquez sur save as pour sauvegarder le fichier"
#. module: c2c_contact_to_ldap
#: help:res.company,is_activedir:0
msgid "The ldap is part of an Active Directory"
msgstr "Le ldap est un ldap Active Directory"
#. module: c2c_contact_to_ldap
#: wizard_button:ldap.import_adresses,importadd,end:0
msgid "OK"
msgstr ""
#. module: c2c_contact_to_ldap
#: wizard_view:ldap.import_adresses,init:0
msgid "Export adresses to ldap"
msgstr "Exporter les adresses dans le ldap"
#. module: c2c_contact_to_ldap
#: wizard_button:ldap.import_adresses,init,end:0
msgid "Cancel"
msgstr "Annuler"
#. module: c2c_contact_to_ldap
#: field:res.company,ounit:0
msgid "Contact Organizational unit of the contacts"
msgstr "OU (organizational unit des adresses)"
#. module: c2c_contact_to_ldap
#: help:res.company,ounit:0
msgid "Exemple: Contacts"
msgstr ""

50
__unported__/partner_address_ldap/partner.py

@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2010 Camptocamp SA (http://www.camptocamp.com)
# All Right Reserved
#
# Author : Nicolas Bessi (Camptocamp),
# Thanks to Laurent Lauden for his code adaptation
# Active directory Donor: M. Benadiba (Informatique Assistances.fr)
# Contribution : Joel Grand-Guillaume
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from openerp.osv import orm
class LdapPartner(orm.Model):
"""Ensure that when deleting a partner unlink function is called on all
related addresses"""
_inherit = 'res.partner'
def unlink(self, cr, uid, ids, context=None):
context = context or {}
addr_obj = self.pool.get('res.partner.address')
if not isinstance(ids, list):
ids = [ids]
addr_ids = addr_obj.search(cr, uid, [('partner_id', 'in', ids)])
addr_obj.unlink(cr, uid, addr_ids, context=context)
return super(LdapPartner, self).unlink(cr, uid, ids, context=context)

4
__unported__/partner_address_ldap/security/security.xml

@ -0,0 +1,4 @@
<openerp>
<data>
</data>
</openerp>

10
__unported__/partner_address_ldap/wizard.xml

@ -0,0 +1,10 @@
<?xml version="1.0"?>
<openerp>
<data>
<wizard
string="Export addresses to company LDAP"
model="res.company"
name="ldap.import_adresses"
id="ldap_import_adresses"/>
</data>
</openerp>

32
__unported__/partner_address_ldap/wizard/__init__.py

@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2010 Camptocamp SA (http://www.camptocamp.com)
# All Right Reserved
#
# Author : Vincent Renaville
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from . import wiz_import_adresses

174
__unported__/partner_address_ldap/wizard/wiz_import_adresses.py

@ -0,0 +1,174 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2010 Camptocamp SA (http://www.camptocamp.com)
# All Right Reserved
#
# Author : Vincent Renaville
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
# init_import.py
#
# Created by Nicolas Bessi on 28.04.09.
# Copyright (c) 2009 CamptoCamp. All rights reserved.
#
import base64
import unicodedata
import re
import wizard
from openerp import pooler
from openerp import netsvc
from openerp.tools.translate import _
from openerp.tools import ustr
_FORM = '''<?xml version="1.0"?>
<form string="Export addresses to ldap">
</form>'''
_FORM1 = """<?xml version="1.0"?>
<form string="Export log">
<separator colspan="4" string="Clic on 'Save as' to save the log file :" />
<field name="errors"/>
</form>
"""
_FIELDS = {
'errors': {
'string': 'Error report',
'type': 'binary',
'readonly': True,
},
}
# As this is a bulk batch wizard the performance process was not really
# taken in account ###
# The ideal way of doing would be to modify the connexion settings in
# order to have a connexion singleton in the file partner.py it will
# avoid connexion renegotiation for each partner.
def _action_import_addresses(self, cr, uid, data, context):
""" This function create or update each addresses present in the database.
It will also generate an error report"""
logger = netsvc.Logger()
error_report = [u'Error report']
add_obj = pooler.get_pool(cr.dbname).get('res.partner.address')
add_ids = add_obj.search(cr, uid, [])
addresses = add_obj.browse(cr, uid, add_ids)
phone_fields = ['phone', 'fax', 'mobile', 'private_phone']
for add in addresses:
vals = {
'partner_id': add.partner_id.id,
'email': add.email,
'phone': add.phone,
'fax': add.fax,
'mobile': add.mobile,
'firstname': add.firstname,
'lastname': add.lastname,
'private_phone': add.private_phone,
'street': add.street,
'street2': add.street2,
'city': add.city,
}
# Validating the mail
if add.email:
if re.match(
"^.+\\@(\\[?)[a-zA-Z0-9\\-\\.]+\\."
"([a-zA-Z]{2,3}|[0-9]{1,3})(\\]?)$", add.email) is None or\
re.search(u"[éèàêöüäï&]", add.email) is not None:
msg = _(
'Address %s for partner %s has email that is invalid %s'
) % (
ustr(vals['firstname']) + ' ' + ustr(vals['lastname']),
add.partner_id.name,
ustr(add.email)
)
logger.notifyChannel('ldap export', netsvc.LOG_INFO, msg)
error_report.append(msg)
vals['email'] = False
# Validating the Phone
for key in phone_fields:
if (not ustr(vals[key]).startswith('+')
or ustr(vals[key]).find("\n") != -1
or re.search(u"[éèàêöüä#&]", ustr(vals[key])) is not None):
vals[key] = False
msg = _(
'Address %s for partner %s has %s that is invalid '
) % (
ustr(vals['firstname']) + ' ' + ustr(vals['lastname']),
add.partner_id.name,
key
)
logger.notifyChannel('ldap export', netsvc.LOG_INFO, msg)
error_report.append(msg)
# Validating the CN
if not add.lastname and add.firstname:
msg = (_('!!! Address %s for partner %s has no last name and '
'first name that is valid partner name was used')
% (ustr(add.id), add.partner_id.name))
logger.notifyChannel('ldap export', netsvc.LOG_INFO, msg)
error_report.append(msg)
# We save to LDAP
add.write(vals, {'init_mode': True})
# we by pass the encoding errors
map(lambda x: unicodedata.normalize("NFKD", x).encode('ascii', 'ignore'),
error_report)
error_report = "\n".join(error_report)
logger.notifyChannel("MY TOPIC", netsvc.LOG_ERROR, error_report)
try:
data = base64.encodestring(error_report.encode())
except:
data = base64.encodestring("Could not generate report file. "
"Please look in the log for details")
return {'errors': data}
class Wiz_import_addresses(wizard.interface):
states = {
'init': {
'actions': [],
'result': {
'type': 'form',
'arch': _FORM,
'fields': {},
'state': [
('end', 'Cancel'),
('importadd', 'Export adresses into company LDAP')
]
}
},
'importadd': {
'actions': [_action_import_addresses],
'result': {
'state': [('end', 'OK', 'gtk-ok', True)],
'arch': _FORM1,
'fields': _FIELDS,
'type': 'form'
}
}
}
Wiz_import_addresses('ldap.import_adresses')

22
account_partner_merge/__init__.py

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Yannick Vaucher
# Copyright 2013 Camptocamp SA
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import partner_merge

34
account_partner_merge/__openerp__.py

@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Yannick Vaucher
# Copyright 2013 Camptocamp SA
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{'name': 'Account Partner Merge',
'version': '1.0',
'category': 'Hidden',
'description': """Update invoice commercial_partner_id""",
'author': "Camptocamp,Odoo Community Association (OCA)",
'maintainer': 'Camptocamp',
'website': 'http://www.camptocamp.com/',
'depends': ['account_report_company', 'base_partner_merge'],
'data': ['account_partner_merge_view.xml'],
'test': [],
'installable': True,
'auto_install': True,
'application': False,
}

17
account_partner_merge/account_partner_merge_view.xml

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<openerp>
<data>
<record model='ir.ui.view' id='base_partner_merge_automatic_wizard_form'>
<field name='name'>account.partner.merge.automatic.wizard.form</field>
<field name='model'>base.partner.merge.automatic.wizard</field>
<field name='inherit_id' ref='base_partner_merge.base_partner_merge_automatic_wizard_form'/>
<field name='arch' type='xml'>
<xpath expr="//field[@name='partner_ids']/tree/field[@name='name']" position="replace">
<field name="display_name" />
</xpath>
</field>
</record>
</data>
</openerp>

40
account_partner_merge/partner_merge.py

@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Yannick Vaucher
# Copyright 2013 Camptocamp SA
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import orm
class MergePartnerAutomatic(orm.TransientModel):
_inherit = 'base.partner.merge.automatic.wizard'
def _update_values(self, cr, uid, src_partners, dst_partner, context=None):
"""Make sure we don't forget to update the stored value of
invoice field commercial_partner_id
"""
super(MergePartnerAutomatic, self)._update_values(
cr, uid, src_partners, dst_partner, context=context
)
invoice_obj = self.pool.get('account.invoice')
invoice_ids = invoice_obj.search(
cr, uid, [('partner_id', '=', dst_partner.id)], context=context
)
# call write to refresh stored value
invoice_obj.write(cr, uid, invoice_ids, {}, context=context)

90
animal/README.rst

@ -1,90 +0,0 @@
======
Animal
======
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fpartner--contact-lightgray.png?logo=github
:target: https://github.com/OCA/partner-contact/tree/12.0/animal
:alt: OCA/partner-contact
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/partner-contact-12-0/partner-contact-12-0-animal
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
:target: https://runbot.odoo-community.org/runbot/134/12.0
:alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5|
This module allows you to store animal information.
**Table of contents**
.. contents::
:local:
Usage
=====
* Go to Animals
* Create an animal by entering his name and selecting his gender, species, breed and
color.
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/partner-contact/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
`feedback <https://github.com/OCA/partner-contact/issues/new?body=module:%20animal%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Authors
~~~~~~~
* Open Source Integrators
Contributors
~~~~~~~~~~~~
* Open Source Integrators <https://www.opensourceintegrators.com>
* Maxime Chambreuil <mchambreuil@opensourceintegrators.com>
Maintainers
~~~~~~~~~~~
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
.. |maintainer-max3903| image:: https://github.com/max3903.png?size=40px
:target: https://github.com/max3903
:alt: max3903
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
|maintainer-max3903|
This module is part of the `OCA/partner-contact <https://github.com/OCA/partner-contact/tree/12.0/animal>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

3
animal/__init__.py

@ -1,3 +0,0 @@
# Copyright (C) 2020 Open Source Integrators
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import models

29
animal/__manifest__.py

@ -1,29 +0,0 @@
# Copyright (C) 2020 Open Source Integrators
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "Animal",
"version": "12.0.1.0.0",
"license": "AGPL-3",
"summary": "Manage animals information",
"author": "Open Source Integrators, Odoo Community Association (OCA)",
"maintainer": "Open Source Integrators",
"website": "https://github.com/OCA/partner-contact",
"depends": ["mail"],
"data": [
"data/ir.module.category.csv",
"data/animal.species.csv",
"data/animal.breed.csv",
"data/animal.color.csv",
"security/res_groups.xml",
"security/ir.model.access.csv",
"views/animal_color.xml",
"views/animal_breed.xml",
"views/animal_species.xml",
"views/animal.xml",
"views/menu.xml",
],
"demo": [],
"application": True,
"development_status": "Beta",
"maintainers": ["max3903"],
}

25
animal/data/animal.breed.csv

@ -1,25 +0,0 @@
id,name,species_id/id
beagle,Beagle,animal.dog
boxer,Boxer,animal.dog
bull_terrier,Bull Terrier,animal.dog
bulldog,Bulldog,animal.dog
chihuahua,Chihuahua,animal.dog
cocker,Cocker,animal.dog
collie,Collie,animal.dog
dalmatian,Dalmatian,animal.dog
dachshund,Dachshund,animal.dog
doberman,Doberman,animal.dog
english_cocker_spaniel,English Cocker Spaniel,animal.dog
french_bulldog,French Bulldog,animal.dog
german_shepard,German Shepard,animal.dog
golden_retriever,Golden Retriever,animal.dog
great_dane,Great Dane,animal.dog
jack_russell,Jack Russell,animal.dog
labrador,Labrador,animal.dog
pomeranian,Pomeranian,animal.dog
pug,Pug,animal.dog
rottweiler,Rottweiler,animal.dog
schnauzer,Schnauzer,animal.dog
shih_tzu,Shih Tzu,animal.dog
siberian_husky,Siberian Husky,animal.dog
yorkshire_terrier,Yorkshire Terrier,animal.dog

2
animal/data/animal.color.csv

@ -1,2 +0,0 @@
id,name,breed_id/id
salt,Salt and pepper,animal.schnauzer

4
animal/data/animal.species.csv

@ -1,4 +0,0 @@
id,name
bird,Bird
cat,Cat
dog,Dog

2
animal/data/ir.module.category.csv

@ -1,2 +0,0 @@
id,name,sequence
animal,Animal,30

565
animal/i18n/animal.pot

@ -1,565 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * animal
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 12.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__message_needaction
msgid "Action Needed"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__active
msgid "Active"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__activity_ids
msgid "Activities"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__activity_state
msgid "Activity State"
msgstr ""
#. module: animal
#: model:ir.model,name:animal.model_animal
#: model:ir.module.category,name:animal.animal
#: model_terms:ir.ui.view,arch_db:animal.view_animal_form
msgid "Animal"
msgstr ""
#. module: animal
#: model:ir.model,name:animal.model_animal_breed
msgid "Animal Breeds"
msgstr ""
#. module: animal
#: model:ir.model,name:animal.model_animal_color
msgid "Animal Colors"
msgstr ""
#. module: animal
#: model:ir.model,name:animal.model_animal_species
msgid "Animal Species"
msgstr ""
#. module: animal
#: model:ir.actions.act_window,name:animal.action_animal
#: model:ir.ui.menu,name:animal.root
#: model_terms:ir.ui.view,arch_db:animal.view_animal_search
#: model_terms:ir.ui.view,arch_db:animal.view_animal_tree
msgid "Animals"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__message_attachment_count
msgid "Attachment Count"
msgstr ""
#. module: animal
#: model_terms:ir.ui.view,arch_db:animal.view_animal_kanban
msgid "Avatar"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.beagle
msgid "Beagle"
msgstr ""
#. module: animal
#: model:animal.species,name:animal.bird
msgid "Bird"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__birth_date
msgid "Birth Date"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.boxer
msgid "Boxer"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__breed_id
#: model:ir.model.fields,field_description:animal.field_animal_color__breed_id
#: model_terms:ir.ui.view,arch_db:animal.view_animal_breed_form
#: model_terms:ir.ui.view,arch_db:animal.view_animal_search
msgid "Breed"
msgstr ""
#. module: animal
#: model:ir.actions.act_window,name:animal.action_animal_breed
#: model:ir.model.fields,field_description:animal.field_animal_species__breed_ids
#: model:ir.ui.menu,name:animal.breed
#: model_terms:ir.ui.view,arch_db:animal.view_animal_breed_tree
#: model_terms:ir.ui.view,arch_db:animal.view_animal_species_form
msgid "Breeds"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.bull_terrier
msgid "Bull Terrier"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.bulldog
msgid "Bulldog"
msgstr ""
#. module: animal
#: model:animal.species,name:animal.cat
msgid "Cat"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.chihuahua
msgid "Chihuahua"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.cocker
msgid "Cocker"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.collie
msgid "Collie"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__color_id
#: model_terms:ir.ui.view,arch_db:animal.view_animal_color_form
#: model_terms:ir.ui.view,arch_db:animal.view_animal_search
msgid "Color"
msgstr ""
#. module: animal
#: model:ir.actions.act_window,name:animal.action_animal_color
#: model:ir.ui.menu,name:animal.color
msgid "Colors"
msgstr ""
#. module: animal
#: model:ir.ui.menu,name:animal.config
msgid "Configuration"
msgstr ""
#. module: animal
#: model_terms:ir.actions.act_window,help:animal.action_animal_breed
msgid "Create a breed."
msgstr ""
#. module: animal
#: model_terms:ir.actions.act_window,help:animal.action_animal_color
msgid "Create a color."
msgstr ""
#. module: animal
#: model_terms:ir.actions.act_window,help:animal.action_animal_species
msgid "Create a species."
msgstr ""
#. module: animal
#: model_terms:ir.actions.act_window,help:animal.action_animal
msgid "Create an animal."
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__create_uid
#: model:ir.model.fields,field_description:animal.field_animal_breed__create_uid
#: model:ir.model.fields,field_description:animal.field_animal_color__create_uid
#: model:ir.model.fields,field_description:animal.field_animal_species__create_uid
msgid "Created by"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__create_date
#: model:ir.model.fields,field_description:animal.field_animal_breed__create_date
#: model:ir.model.fields,field_description:animal.field_animal_color__create_date
#: model:ir.model.fields,field_description:animal.field_animal_species__create_date
msgid "Created on"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.dachshund
msgid "Dachshund"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.dalmatian
msgid "Dalmatian"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__display_name
#: model:ir.model.fields,field_description:animal.field_animal_breed__display_name
#: model:ir.model.fields,field_description:animal.field_animal_color__display_name
#: model:ir.model.fields,field_description:animal.field_animal_species__display_name
msgid "Display Name"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.doberman
msgid "Doberman"
msgstr ""
#. module: animal
#: model:animal.species,name:animal.dog
msgid "Dog"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.english_cocker_spaniel
msgid "English Cocker Spaniel"
msgstr ""
#. module: animal
#: selection:animal,gender:0
#: model_terms:ir.ui.view,arch_db:animal.view_animal_search
msgid "Female"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__message_follower_ids
msgid "Followers"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__message_channel_ids
msgid "Followers (Channels)"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__message_partner_ids
msgid "Followers (Partners)"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.french_bulldog
msgid "French Bulldog"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__gender
#: model_terms:ir.ui.view,arch_db:animal.view_animal_search
msgid "Gender"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.german_shepard
msgid "German Shepard"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.golden_retriever
msgid "Golden Retriever"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.great_dane
msgid "Great Dane"
msgstr ""
#. module: animal
#: model_terms:ir.ui.view,arch_db:animal.view_animal_search
msgid "Group By"
msgstr ""
#. module: animal
#: selection:animal,gender:0
#: model_terms:ir.ui.view,arch_db:animal.view_animal_search
msgid "Hermaphrodite"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__id
#: model:ir.model.fields,field_description:animal.field_animal_breed__id
#: model:ir.model.fields,field_description:animal.field_animal_color__id
#: model:ir.model.fields,field_description:animal.field_animal_species__id
msgid "ID"
msgstr ""
#. module: animal
#: model:ir.model.fields,help:animal.field_animal__message_unread
msgid "If checked new messages require your attention."
msgstr ""
#. module: animal
#: model:ir.model.fields,help:animal.field_animal__message_needaction
msgid "If checked, new messages require your attention."
msgstr ""
#. module: animal
#: model:ir.model.fields,help:animal.field_animal__message_has_error
msgid "If checked, some messages have a delivery error."
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__image
msgid "Image"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__message_is_follower
msgid "Is Follower"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.jack_russell
msgid "Jack Russell"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.labrador
msgid "Labrador"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal____last_update
#: model:ir.model.fields,field_description:animal.field_animal_breed____last_update
#: model:ir.model.fields,field_description:animal.field_animal_color____last_update
#: model:ir.model.fields,field_description:animal.field_animal_species____last_update
msgid "Last Modified on"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__write_uid
#: model:ir.model.fields,field_description:animal.field_animal_breed__write_uid
#: model:ir.model.fields,field_description:animal.field_animal_color__write_uid
#: model:ir.model.fields,field_description:animal.field_animal_species__write_uid
msgid "Last Updated by"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__write_date
#: model:ir.model.fields,field_description:animal.field_animal_breed__write_date
#: model:ir.model.fields,field_description:animal.field_animal_color__write_date
#: model:ir.model.fields,field_description:animal.field_animal_species__write_date
msgid "Last Updated on"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__message_main_attachment_id
msgid "Main Attachment"
msgstr ""
#. module: animal
#: selection:animal,gender:0
#: model_terms:ir.ui.view,arch_db:animal.view_animal_search
msgid "Male"
msgstr ""
#. module: animal
#: model:res.groups,name:animal.group_animal_manager
msgid "Manager"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__message_has_error
msgid "Message Delivery error"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__message_ids
msgid "Messages"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__name
#: model:ir.model.fields,field_description:animal.field_animal_breed__name
#: model:ir.model.fields,field_description:animal.field_animal_color__name
#: model:ir.model.fields,field_description:animal.field_animal_species__name
#: model_terms:ir.ui.view,arch_db:animal.view_animal_search
msgid "Name"
msgstr ""
#. module: animal
#: selection:animal,gender:0
#: model_terms:ir.ui.view,arch_db:animal.view_animal_search
msgid "Neutered"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__activity_date_deadline
msgid "Next Activity Deadline"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__activity_summary
msgid "Next Activity Summary"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__activity_type_id
msgid "Next Activity Type"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__message_needaction_counter
msgid "Number of Actions"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__message_has_error_counter
msgid "Number of error"
msgstr ""
#. module: animal
#: model:ir.model.fields,help:animal.field_animal__message_needaction_counter
msgid "Number of messages which requires an action"
msgstr ""
#. module: animal
#: model:ir.model.fields,help:animal.field_animal__message_has_error_counter
msgid "Number of messages with delivery error"
msgstr ""
#. module: animal
#: model:ir.model.fields,help:animal.field_animal__message_unread_counter
msgid "Number of unread messages"
msgstr ""
#. module: animal
#: selection:animal,activity_state:0
msgid "Overdue"
msgstr ""
#. module: animal
#: selection:animal,activity_state:0
msgid "Planned"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.pomeranian
msgid "Pomeranian"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.pug
msgid "Pug"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__ref
msgid "Reference"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__activity_user_id
msgid "Responsible User"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.rottweiler
msgid "Rottweiler"
msgstr ""
#. module: animal
#: model:animal.color,name:animal.salt
msgid "Salt and pepper"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.schnauzer
msgid "Schnauzer"
msgstr ""
#. module: animal
#: model:ir.ui.menu,name:animal.settings
msgid "Settings"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.shih_tzu
msgid "Shih Tzu"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.siberian_husky
msgid "Siberian Husky"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__size
msgid "Size"
msgstr ""
#. module: animal
#: model:ir.actions.act_window,name:animal.action_animal_species
#: model:ir.model.fields,field_description:animal.field_animal__species_id
#: model:ir.model.fields,field_description:animal.field_animal_breed__species_id
#: model:ir.model.fields,field_description:animal.field_animal_color__species_id
#: model:ir.ui.menu,name:animal.species
#: model_terms:ir.ui.view,arch_db:animal.view_animal_search
#: model_terms:ir.ui.view,arch_db:animal.view_animal_species_form
#: model_terms:ir.ui.view,arch_db:animal.view_animal_species_tree
msgid "Species"
msgstr ""
#. module: animal
#: model:ir.model.fields,help:animal.field_animal__activity_state
msgid "Status based on activities\n"
"Overdue: Due date is already passed\n"
"Today: Activity date is today\n"
"Planned: Future activities."
msgstr ""
#. module: animal
#: model:ir.model.fields,help:animal.field_animal__image
msgid "This field holds the photo of the animal."
msgstr ""
#. module: animal
#: selection:animal,activity_state:0
msgid "Today"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__message_unread
msgid "Unread Messages"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__message_unread_counter
msgid "Unread Messages Counter"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__website_message_ids
msgid "Website Messages"
msgstr ""
#. module: animal
#: model:ir.model.fields,help:animal.field_animal__website_message_ids
msgid "Website communication history"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__weight
msgid "Weight (in kg)"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.yorkshire_terrier
msgid "Yorkshire Terrier"
msgstr ""
#. module: animal
#: model_terms:ir.ui.view,arch_db:animal.view_animal_color_tree
msgid "colors"
msgstr ""

567
animal/i18n/es.po

@ -1,567 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * animal
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 12.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2020-10-19 17:08+0000\n"
"Last-Translator: Maxime Chambreuil <mchambreuil@opensourceintegrators.com>\n"
"Language-Team: none\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 3.10\n"
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__message_needaction
msgid "Action Needed"
msgstr "Acción requerida"
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__active
msgid "Active"
msgstr "Activo"
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__activity_ids
msgid "Activities"
msgstr "Actividades"
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__activity_state
msgid "Activity State"
msgstr "Estado de la actividad"
#. module: animal
#: model:ir.model,name:animal.model_animal
#: model:ir.module.category,name:animal.animal
#: model_terms:ir.ui.view,arch_db:animal.view_animal_form
msgid "Animal"
msgstr ""
#. module: animal
#: model:ir.model,name:animal.model_animal_breed
msgid "Animal Breeds"
msgstr "Razas"
#. module: animal
#: model:ir.model,name:animal.model_animal_color
msgid "Animal Colors"
msgstr "Colores"
#. module: animal
#: model:ir.model,name:animal.model_animal_species
msgid "Animal Species"
msgstr "Especies"
#. module: animal
#: model:ir.actions.act_window,name:animal.action_animal
#: model:ir.ui.menu,name:animal.root
#: model_terms:ir.ui.view,arch_db:animal.view_animal_search
#: model_terms:ir.ui.view,arch_db:animal.view_animal_tree
msgid "Animals"
msgstr "Animales"
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__message_attachment_count
msgid "Attachment Count"
msgstr "Numero de archivos"
#. module: animal
#: model_terms:ir.ui.view,arch_db:animal.view_animal_kanban
msgid "Avatar"
msgstr "Avatar"
#. module: animal
#: model:animal.breed,name:animal.beagle
msgid "Beagle"
msgstr ""
#. module: animal
#: model:animal.species,name:animal.bird
msgid "Bird"
msgstr "Pájaro"
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__birth_date
msgid "Birth Date"
msgstr "Fecha de nacimiento"
#. module: animal
#: model:animal.breed,name:animal.boxer
msgid "Boxer"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__breed_id
#: model:ir.model.fields,field_description:animal.field_animal_color__breed_id
#: model_terms:ir.ui.view,arch_db:animal.view_animal_breed_form
#: model_terms:ir.ui.view,arch_db:animal.view_animal_search
msgid "Breed"
msgstr "Raza"
#. module: animal
#: model:ir.actions.act_window,name:animal.action_animal_breed
#: model:ir.model.fields,field_description:animal.field_animal_species__breed_ids
#: model:ir.ui.menu,name:animal.breed
#: model_terms:ir.ui.view,arch_db:animal.view_animal_breed_tree
#: model_terms:ir.ui.view,arch_db:animal.view_animal_species_form
msgid "Breeds"
msgstr "Razas"
#. module: animal
#: model:animal.breed,name:animal.bull_terrier
msgid "Bull Terrier"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.bulldog
msgid "Bulldog"
msgstr ""
#. module: animal
#: model:animal.species,name:animal.cat
msgid "Cat"
msgstr "Gato"
#. module: animal
#: model:animal.breed,name:animal.chihuahua
msgid "Chihuahua"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.cocker
msgid "Cocker"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.collie
msgid "Collie"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__color_id
#: model_terms:ir.ui.view,arch_db:animal.view_animal_color_form
#: model_terms:ir.ui.view,arch_db:animal.view_animal_search
msgid "Color"
msgstr ""
#. module: animal
#: model:ir.actions.act_window,name:animal.action_animal_color
#: model:ir.ui.menu,name:animal.color
msgid "Colors"
msgstr "Colores"
#. module: animal
#: model:ir.ui.menu,name:animal.config
msgid "Configuration"
msgstr "Configuración"
#. module: animal
#: model_terms:ir.actions.act_window,help:animal.action_animal_breed
msgid "Create a breed."
msgstr "Crear una raza."
#. module: animal
#: model_terms:ir.actions.act_window,help:animal.action_animal_color
msgid "Create a color."
msgstr "Crear un color."
#. module: animal
#: model_terms:ir.actions.act_window,help:animal.action_animal_species
msgid "Create a species."
msgstr "Crear un especie."
#. module: animal
#: model_terms:ir.actions.act_window,help:animal.action_animal
msgid "Create an animal."
msgstr "Crear un animal."
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__create_uid
#: model:ir.model.fields,field_description:animal.field_animal_breed__create_uid
#: model:ir.model.fields,field_description:animal.field_animal_color__create_uid
#: model:ir.model.fields,field_description:animal.field_animal_species__create_uid
msgid "Created by"
msgstr "Creado por"
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__create_date
#: model:ir.model.fields,field_description:animal.field_animal_breed__create_date
#: model:ir.model.fields,field_description:animal.field_animal_color__create_date
#: model:ir.model.fields,field_description:animal.field_animal_species__create_date
msgid "Created on"
msgstr "Creado el"
#. module: animal
#: model:animal.breed,name:animal.dachshund
msgid "Dachshund"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.dalmatian
msgid "Dalmatian"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__display_name
#: model:ir.model.fields,field_description:animal.field_animal_breed__display_name
#: model:ir.model.fields,field_description:animal.field_animal_color__display_name
#: model:ir.model.fields,field_description:animal.field_animal_species__display_name
msgid "Display Name"
msgstr "Nombre"
#. module: animal
#: model:animal.breed,name:animal.doberman
msgid "Doberman"
msgstr ""
#. module: animal
#: model:animal.species,name:animal.dog
msgid "Dog"
msgstr "Perro"
#. module: animal
#: model:animal.breed,name:animal.english_cocker_spaniel
msgid "English Cocker Spaniel"
msgstr ""
#. module: animal
#: selection:animal,gender:0
#: model_terms:ir.ui.view,arch_db:animal.view_animal_search
msgid "Female"
msgstr "Hembra"
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__message_follower_ids
msgid "Followers"
msgstr "Seguidores"
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__message_channel_ids
msgid "Followers (Channels)"
msgstr "Seguidores (Canales)"
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__message_partner_ids
msgid "Followers (Partners)"
msgstr "Seguidores (Contactos)"
#. module: animal
#: model:animal.breed,name:animal.french_bulldog
msgid "French Bulldog"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__gender
#: model_terms:ir.ui.view,arch_db:animal.view_animal_search
msgid "Gender"
msgstr "Género"
#. module: animal
#: model:animal.breed,name:animal.german_shepard
msgid "German Shepard"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.golden_retriever
msgid "Golden Retriever"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.great_dane
msgid "Great Dane"
msgstr ""
#. module: animal
#: model_terms:ir.ui.view,arch_db:animal.view_animal_search
msgid "Group By"
msgstr "Agrupar por"
#. module: animal
#: selection:animal,gender:0
#: model_terms:ir.ui.view,arch_db:animal.view_animal_search
msgid "Hermaphrodite"
msgstr "Hermafrodita"
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__id
#: model:ir.model.fields,field_description:animal.field_animal_breed__id
#: model:ir.model.fields,field_description:animal.field_animal_color__id
#: model:ir.model.fields,field_description:animal.field_animal_species__id
msgid "ID"
msgstr "Id"
#. module: animal
#: model:ir.model.fields,help:animal.field_animal__message_unread
msgid "If checked new messages require your attention."
msgstr ""
#. module: animal
#: model:ir.model.fields,help:animal.field_animal__message_needaction
msgid "If checked, new messages require your attention."
msgstr ""
#. module: animal
#: model:ir.model.fields,help:animal.field_animal__message_has_error
msgid "If checked, some messages have a delivery error."
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__image
msgid "Image"
msgstr "Imagen"
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__message_is_follower
msgid "Is Follower"
msgstr "Es un seguidor"
#. module: animal
#: model:animal.breed,name:animal.jack_russell
msgid "Jack Russell"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.labrador
msgid "Labrador"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal____last_update
#: model:ir.model.fields,field_description:animal.field_animal_breed____last_update
#: model:ir.model.fields,field_description:animal.field_animal_color____last_update
#: model:ir.model.fields,field_description:animal.field_animal_species____last_update
msgid "Last Modified on"
msgstr "Ultima modificación el"
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__write_uid
#: model:ir.model.fields,field_description:animal.field_animal_breed__write_uid
#: model:ir.model.fields,field_description:animal.field_animal_color__write_uid
#: model:ir.model.fields,field_description:animal.field_animal_species__write_uid
msgid "Last Updated by"
msgstr "Ultima modificación por"
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__write_date
#: model:ir.model.fields,field_description:animal.field_animal_breed__write_date
#: model:ir.model.fields,field_description:animal.field_animal_color__write_date
#: model:ir.model.fields,field_description:animal.field_animal_species__write_date
msgid "Last Updated on"
msgstr "Ultima modificación el"
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__message_main_attachment_id
msgid "Main Attachment"
msgstr ""
#. module: animal
#: selection:animal,gender:0
#: model_terms:ir.ui.view,arch_db:animal.view_animal_search
msgid "Male"
msgstr "Macho"
#. module: animal
#: model:res.groups,name:animal.group_animal_manager
msgid "Manager"
msgstr "Gerente"
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__message_has_error
msgid "Message Delivery error"
msgstr "Error de entrega"
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__message_ids
msgid "Messages"
msgstr "Mensajes"
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__name
#: model:ir.model.fields,field_description:animal.field_animal_breed__name
#: model:ir.model.fields,field_description:animal.field_animal_color__name
#: model:ir.model.fields,field_description:animal.field_animal_species__name
#: model_terms:ir.ui.view,arch_db:animal.view_animal_search
msgid "Name"
msgstr "Nombre"
#. module: animal
#: selection:animal,gender:0
#: model_terms:ir.ui.view,arch_db:animal.view_animal_search
msgid "Neutered"
msgstr "Castrado/a"
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__activity_date_deadline
msgid "Next Activity Deadline"
msgstr "Fecha limite de la próxima actividad"
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__activity_summary
msgid "Next Activity Summary"
msgstr "Resumen de la próxima actividad"
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__activity_type_id
msgid "Next Activity Type"
msgstr "Tipo de la próxima actividad"
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__message_needaction_counter
msgid "Number of Actions"
msgstr "Numero de acciones"
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__message_has_error_counter
msgid "Number of error"
msgstr "Numero de errores"
#. module: animal
#: model:ir.model.fields,help:animal.field_animal__message_needaction_counter
msgid "Number of messages which requires an action"
msgstr ""
#. module: animal
#: model:ir.model.fields,help:animal.field_animal__message_has_error_counter
msgid "Number of messages with delivery error"
msgstr ""
#. module: animal
#: model:ir.model.fields,help:animal.field_animal__message_unread_counter
msgid "Number of unread messages"
msgstr ""
#. module: animal
#: selection:animal,activity_state:0
msgid "Overdue"
msgstr "Vencido"
#. module: animal
#: selection:animal,activity_state:0
msgid "Planned"
msgstr "Planeado"
#. module: animal
#: model:animal.breed,name:animal.pomeranian
msgid "Pomeranian"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.pug
msgid "Pug"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__ref
msgid "Reference"
msgstr "Referencia"
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__activity_user_id
msgid "Responsible User"
msgstr "Usuario responsable"
#. module: animal
#: model:animal.breed,name:animal.rottweiler
msgid "Rottweiler"
msgstr ""
#. module: animal
#: model:animal.color,name:animal.salt
msgid "Salt and pepper"
msgstr "Sal y pimienta"
#. module: animal
#: model:animal.breed,name:animal.schnauzer
msgid "Schnauzer"
msgstr ""
#. module: animal
#: model:ir.ui.menu,name:animal.settings
msgid "Settings"
msgstr "Ajustes"
#. module: animal
#: model:animal.breed,name:animal.shih_tzu
msgid "Shih Tzu"
msgstr ""
#. module: animal
#: model:animal.breed,name:animal.siberian_husky
msgid "Siberian Husky"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__size
msgid "Size"
msgstr "Tamaño"
#. module: animal
#: model:ir.actions.act_window,name:animal.action_animal_species
#: model:ir.model.fields,field_description:animal.field_animal__species_id
#: model:ir.model.fields,field_description:animal.field_animal_breed__species_id
#: model:ir.model.fields,field_description:animal.field_animal_color__species_id
#: model:ir.ui.menu,name:animal.species
#: model_terms:ir.ui.view,arch_db:animal.view_animal_search
#: model_terms:ir.ui.view,arch_db:animal.view_animal_species_form
#: model_terms:ir.ui.view,arch_db:animal.view_animal_species_tree
msgid "Species"
msgstr "Especies"
#. module: animal
#: model:ir.model.fields,help:animal.field_animal__activity_state
msgid "Status based on activities\n"
"Overdue: Due date is already passed\n"
"Today: Activity date is today\n"
"Planned: Future activities."
msgstr ""
#. module: animal
#: model:ir.model.fields,help:animal.field_animal__image
msgid "This field holds the photo of the animal."
msgstr "Foto del animal."
#. module: animal
#: selection:animal,activity_state:0
msgid "Today"
msgstr "Hoy"
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__message_unread
msgid "Unread Messages"
msgstr "Nuevos mensajes"
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__message_unread_counter
msgid "Unread Messages Counter"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__website_message_ids
msgid "Website Messages"
msgstr ""
#. module: animal
#: model:ir.model.fields,help:animal.field_animal__website_message_ids
msgid "Website communication history"
msgstr ""
#. module: animal
#: model:ir.model.fields,field_description:animal.field_animal__weight
msgid "Weight (in kg)"
msgstr "Peso (en kg)"
#. module: animal
#: model:animal.breed,name:animal.yorkshire_terrier
msgid "Yorkshire Terrier"
msgstr ""
#. module: animal
#: model_terms:ir.ui.view,arch_db:animal.view_animal_color_tree
msgid "colors"
msgstr "colores"

8
animal/models/__init__.py

@ -1,8 +0,0 @@
# Copyright (C) 2020 Open Source Integrators
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import (
animal_species,
animal_breed,
animal_color,
animal,
)

42
animal/models/animal.py

@ -1,42 +0,0 @@
# Copyright (C) 2020 Open Source Integrators
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models
class Animal(models.Model):
_name = "animal"
_description = "Animal"
_inherit = ["mail.thread", "mail.activity.mixin"]
_order = "name"
name = fields.Char(string="Name")
ref = fields.Char(string="Reference")
species_id = fields.Many2one("animal.species", string="Species", required=True)
breed_id = fields.Many2one("animal.breed", string="Breed", required=True)
color_id = fields.Many2one("animal.color", string="Color")
size = fields.Char(string="Size")
weight = fields.Float(string="Weight (in kg)")
birth_date = fields.Date(string="Birth Date")
gender = fields.Selection(
string="Gender",
selection=[
("female", "Female"),
("male", "Male"),
("hermaphrodite", "Hermaphrodite"),
("neutered", "Neutered"),
],
default="female",
required=True,
)
active = fields.Boolean(default=True)
image = fields.Binary(
"Image", attachment=True, help="This field holds the photo of the animal."
)
@api.onchange("species_id")
def onchange_species(self):
self.breed_id = False
@api.onchange("breed_id")
def onchange_breed(self):
self.color_id = False

12
animal/models/animal_breed.py

@ -1,12 +0,0 @@
# Copyright (C) 2020 Open Source Integrators
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models
class AnimalBreed(models.Model):
_name = "animal.breed"
_description = "Animal Breeds"
_order = "name"
name = fields.Char(string="Name", translate=True)
species_id = fields.Many2one("animal.species", string="Species", required=True)

14
animal/models/animal_color.py

@ -1,14 +0,0 @@
# Copyright (C) 2020 Open Source Integrators
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models
class AnimalColor(models.Model):
_name = "animal.color"
_description = "Animal Colors"
name = fields.Char(string="Name", translate=True)
breed_id = fields.Many2one("animal.breed", string="Breed", required=True)
species_id = fields.Many2one(
"animal.species", string="Species", related="breed_id.species_id", readonly=True
)

12
animal/models/animal_species.py

@ -1,12 +0,0 @@
# Copyright (C) 2020 Open Source Integrators
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models
class AnimalSpecies(models.Model):
_name = "animal.species"
_description = "Animal Species"
_order = "name"
name = fields.Char(string="Name", translate=True)
breed_ids = fields.One2many("animal.breed", "species_id", string="Breeds")

3
animal/readme/CONTRIBUTORS.rst

@ -1,3 +0,0 @@
* Open Source Integrators <https://www.opensourceintegrators.com>
* Maxime Chambreuil <mchambreuil@opensourceintegrators.com>

1
animal/readme/DESCRIPTION.rst

@ -1 +0,0 @@
This module allows you to store animal information.

3
animal/readme/USAGE.rst

@ -1,3 +0,0 @@
* Go to Animals
* Create an animal by entering his name and selecting his gender, species, breed and
color.

8
animal/security/ir.model.access.csv

@ -1,8 +0,0 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_animal_user,animal.user,model_animal,base.group_user,1,1,1,1
access_animal_color_user,animal.color.user,model_animal_color,base.group_user,1,0,0,0
access_animal_color_manager,animal.color.manaager,model_animal_color,animal.group_animal_manager,1,1,1,1
access_animal_breed_user,animal.breed.user,model_animal_breed,base.group_user,1,0,0,0
access_animal_breed_manager,animal.breed.manager,model_animal_breed,animal.group_animal_manager,1,1,1,1
access_animal_species_user,animal.species.user,model_animal_species,base.group_user,1,0,0,0
access_animal_species_manager,animal.species.manager,model_animal_species,animal.group_animal_manager,1,1,1,0

10
animal/security/res_groups.xml

@ -1,10 +0,0 @@
<odoo>
<!-- Animal groups -->
<record id="group_animal_manager" model="res.groups">
<field name="name">Manager</field>
<field name="category_id" ref="animal"/>
<field name="users" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
</record>
</odoo>

BIN
animal/static/description/icon.png

Before

Width: 591  |  Height: 592  |  Size: 37 KiB

433
animal/static/description/index.html

@ -1,433 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.15.1: http://docutils.sourceforge.net/" />
<title>Animal</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: grey; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="animal">
<h1 class="title">Animal</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/partner-contact/tree/12.0/animal"><img alt="OCA/partner-contact" src="https://img.shields.io/badge/github-OCA%2Fpartner--contact-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/partner-contact-12-0/partner-contact-12-0-animal"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/134/12.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
<p>This module allows you to store animal information.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#usage" id="id1">Usage</a></li>
<li><a class="reference internal" href="#bug-tracker" id="id2">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id3">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id4">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id5">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="id6">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#id1">Usage</a></h1>
<ul class="simple">
<li>Go to Animals</li>
<li>Create an animal by entering his name and selecting his gender, species, breed and
color.</li>
</ul>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#id2">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/partner-contact/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/partner-contact/issues/new?body=module:%20animal%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h1><a class="toc-backref" href="#id3">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#id4">Authors</a></h2>
<ul class="simple">
<li>Open Source Integrators</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#id5">Contributors</a></h2>
<ul class="simple">
<li>Open Source Integrators &lt;<a class="reference external" href="https://www.opensourceintegrators.com">https://www.opensourceintegrators.com</a>&gt;<ul>
<li>Maxime Chambreuil &lt;<a class="reference external" href="mailto:mchambreuil&#64;opensourceintegrators.com">mchambreuil&#64;opensourceintegrators.com</a>&gt;</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#id6">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>
<p>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainer</a>:</p>
<p><a class="reference external" href="https://github.com/max3903"><img alt="max3903" src="https://github.com/max3903.png?size=40px" /></a></p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/partner-contact/tree/12.0/animal">OCA/partner-contact</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</body>
</html>

BIN
animal/static/img/avatar.png

Before

Width: 2130  |  Height: 2130  |  Size: 86 KiB

156
animal/views/animal.xml

@ -1,156 +0,0 @@
<odoo>
<!-- Animal Tree View -->
<record id="view_animal_tree" model="ir.ui.view">
<field name="name">animal.tree</field>
<field name="model">animal</field>
<field name="arch" type="xml">
<tree string="Animals">
<field name="ref"/>
<field name="name"/>
<field name="gender"/>
<field name="species_id"/>
<field name="breed_id"/>
<field name="color_id"/>
</tree>
</field>
</record>
<!-- Animal Form View -->
<record id="view_animal_form" model="ir.ui.view">
<field name="name">animal.form</field>
<field name="model">animal</field>
<field name="arch" type="xml">
<form string="Animal">
<header/>
<sheet>
<div class="oe_button_box" name="button_box">
<button name="toggle_active" type="object"
class="oe_stat_button" icon="fa-archive">
<field name="active" widget="boolean_button"
options='{"terminology": "archive"}'/>
</button>
</div>
<field name="image" widget='image' class="oe_avatar"/>
<div class="oe_title">
<h1>
<field name="name"/>
</h1>
<field name="gender" widget="radio"
options="{'horizontal': true}"/>
</div>
<group id="main">
<group id="left">
<field name="species_id"/>
<field name="breed_id" domain="[('species_id', '=', species_id)]"/>
<field name="color_id" domain="[('breed_id', '=', breed_id)]"/>
<field name="size"/>
</group>
<group id="right">
<field name="ref"/>
<field name="weight"/>
<field name="birth_date"/>
</group>
</group>
<notebook/>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"
widget="mail_followers"/>
<field name="activity_ids" widget="mail_activity"/>
<field name="message_ids" widget="mail_thread"
options="{'post_refresh': 'recipients'}"/>
</div>
</form>
</field>
</record>
<!-- Animal Kanban View -->
<record id="view_animal_kanban" model="ir.ui.view">
<field name="name">animal.kanban</field>
<field name="model">animal</field>
<field name="arch" type="xml">
<kanban class="o_res_partner_kanban">
<field name="id"/>
<field name="image"/>
<field name="name"/>
<templates>
<t t-name="kanban-box">
<div class="oe_kanban_global_click o_kanban_record_has_image_fill o_res_partner_kanban">
<t t-if="record.image.raw_value">
<img class="o_kanban_image" t-att-src="kanban_image('animal', 'image', record.id.raw_value)" t-att-alt="record.name"/>
</t>
<t t-else="">
<img class="o_kanban_image" alt="Avatar" t-att-src='_s + "/animal/static/img/avatar.png"'/>
</t>
<div class="oe_kanban_details">
<strong class="o_kanban_record_title oe_partner_heading"><field name="display_name"/></strong>
<div class="o_kanban_tags_section oe_kanban_partner_categories"/>
<ul>
<li t-if="record.gender.raw_value"><field name="gender"/></li>
<li t-if="record.species_id.raw_value and record.breed_id.raw_value"><field name="breed_id"/>, <field name="species_id"/></li>
</ul>
<div class="oe_kanban_partner_links"/>
</div>
</div>
</t>
</templates>
</kanban>
</field>
</record>
<!-- Search for animal -->
<record id="view_animal_search" model="ir.ui.view">
<field name="name">animal.search</field>
<field name="model">animal</field>
<field name="arch" type="xml">
<search string="Animals">
<field name="name"
filter_domain="['|', ('name', 'ilike', self), ('ref', 'ilike', self)]"
string="Name"/>
<field name="species_id"/>
<field name="breed_id"/>
<field name="color_id"/>
<field name="size"/>
<separator/>
<filter string="Female"
domain="[('gender', '=', 'female')]"
name="female"/>
<filter string="Male"
domain="[('gender', '=', 'male')]"
name="male"/>
<filter string="Hermaphrodite"
domain="[('gender', '=', 'hermaphrodite')]"
name="hermaphrodite"/>
<filter string="Neutered"
domain="[('gender', '=', 'neutered')]"
name="neutered"/>
<separator/>
<group expand="0" string="Group By">
<filter name="gender" string="Gender" domain=""
context="{'group_by': 'gender'}"/>
<filter name="species_id" string="Species" domain=""
context="{'group_by': 'species_id'}"/>
<filter name="breed_id" string="Breed" domain=""
context="{'group_by': 'breed_id'}"/>
<filter name="color_id" string="Color" domain=""
context="{'group_by': 'color_id'}"/>
</group>
</search>
</field>
</record>
<record id="action_animal" model="ir.actions.act_window">
<field name="name">Animals</field>
<field name="res_model">animal</field>
<field name="view_type">form</field>
<field name="view_mode">kanban,tree,form</field>
<field name="search_view_id" ref="view_animal_search"/>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create an animal.
</p>
</field>
</record>
</odoo>

53
animal/views/animal_breed.xml

@ -1,53 +0,0 @@
<odoo>
<!-- Animal breed Tree View -->
<record id="view_animal_breed_tree" model="ir.ui.view">
<field name="name">view.animal.breed.tree</field>
<field name="model">animal.breed</field>
<field name="arch" type="xml">
<tree string="Breeds">
<field name="name"/>
<field name="species_id"/>
</tree>
</field>
</record>
<!-- Animal breed Form View -->
<record id="view_animal_breed_form" model="ir.ui.view">
<field name="name">view.animal.breed.form</field>
<field name="model">animal.breed</field>
<field name="arch" type="xml">
<form string="Breed">
<header/>
<sheet>
<div class="oe_button_box" name="button_box"/>
<div class="oe_title">
<h1>
<field name="name"/>
</h1>
</div>
<group id="main">
<group id="left">
<field name="species_id"/>
</group>
<group id="right"/>
</group>
<notebook/>
</sheet>
</form>
</field>
</record>
<record id="action_animal_breed" model="ir.actions.act_window">
<field name="name">Breeds</field>
<field name="res_model">animal.breed</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a breed.
</p>
</field>
</record>
</odoo>

55
animal/views/animal_color.xml

@ -1,55 +0,0 @@
<odoo>
<!-- Animal color Tree View -->
<record id="view_animal_color_tree" model="ir.ui.view">
<field name="name">view.animal.color.tree</field>
<field name="model">animal.color</field>
<field name="arch" type="xml">
<tree string="colors">
<field name="name"/>
<field name="breed_id"/>
<field name="species_id"/>
</tree>
</field>
</record>
<!-- Animal Color Form View -->
<record id="view_animal_color_form" model="ir.ui.view">
<field name="name">view.animal.color.form</field>
<field name="model">animal.color</field>
<field name="arch" type="xml">
<form string="Color">
<header/>
<sheet>
<div class="oe_button_box" name="button_box"/>
<div class="oe_title">
<h1>
<field name="name"/>
</h1>
</div>
<group id="main">
<group id="left">
<field name="breed_id"/>
<field name="species_id"/>
</group>
<group id="right"/>
</group>
<notebook/>
</sheet>
</form>
</field>
</record>
<record id="action_animal_color" model="ir.actions.act_window">
<field name="name">Colors</field>
<field name="res_model">animal.color</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a color.
</p>
</field>
</record>
</odoo>

54
animal/views/animal_species.xml

@ -1,54 +0,0 @@
<odoo>
<!-- Animal Species Tree View -->
<record id="view_animal_species_tree" model="ir.ui.view">
<field name="name">view.animal.species.tree</field>
<field name="model">animal.species</field>
<field name="arch" type="xml">
<tree string="Species">
<field name="name"/>
</tree>
</field>
</record>
<!-- Animal Species Form View -->
<record id="view_animal_species_form" model="ir.ui.view">
<field name="name">view.animal.species.form</field>
<field name="model">animal.species</field>
<field name="arch" type="xml">
<form string="Species">
<header/>
<sheet>
<div class="oe_button_box" name="button_box"/>
<div class="oe_title">
<h1>
<field name="name"/>
</h1>
</div>
<group id="main">
<group id="left"/>
<group id="right"/>
</group>
<notebook>
<page string="Breeds" id="breeds">
<field name="breed_ids"/>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="action_animal_species" model="ir.actions.act_window">
<field name="name">Species</field>
<field name="res_model">animal.species</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a species.
</p>
</field>
</record>
</odoo>

41
animal/views/menu.xml

@ -1,41 +0,0 @@
<odoo>
<!-- Main Menu -->
<menuitem id="root"
name="Animals"
action="action_animal"
sequence="10"
web_icon="animal,static/description/icon.png"/>
<menuitem id="animal"
name="Animals"
action="action_animal"
sequence="10"
parent="root"/>
<menuitem id="config"
name="Configuration"
sequence="100"
parent="root"
groups="group_animal_manager"/>
<!-- Configuration -->
<menuitem id="settings"
name="Settings"
sequence="10"
parent="config"/>
<menuitem id="species"
name="Species"
action="action_animal_species"
sequence="20"
parent="config"/>
<menuitem id="breed"
name="Breeds"
action="action_animal_breed"
sequence="30"
parent="config"/>
<menuitem id="color"
name="Colors"
action="action_animal_color"
sequence="40"
parent="config"/>
</odoo>

90
animal_owner/README.rst

@ -1,90 +0,0 @@
============
Animal Owner
============
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fpartner--contact-lightgray.png?logo=github
:target: https://github.com/OCA/partner-contact/tree/12.0/animal_owner
:alt: OCA/partner-contact
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/partner-contact-12-0/partner-contact-12-0-animal_owner
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
:target: https://runbot.odoo-community.org/runbot/134/12.0
:alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5|
This module allows you to store the owner of a pet.
**Table of contents**
.. contents::
:local:
Usage
=====
* Go to Animals
* Create or select an animal
* Select his owner
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/partner-contact/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
`feedback <https://github.com/OCA/partner-contact/issues/new?body=module:%20animal_owner%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Authors
~~~~~~~
* Open Source Integrators
Contributors
~~~~~~~~~~~~
* Open Source Integrators <https://www.opensourceintegrators.com>
* Maxime Chambreuil <mchambreuil@opensourceintegrators.com>
Maintainers
~~~~~~~~~~~
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
.. |maintainer-max3903| image:: https://github.com/max3903.png?size=40px
:target: https://github.com/max3903
:alt: max3903
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
|maintainer-max3903|
This module is part of the `OCA/partner-contact <https://github.com/OCA/partner-contact/tree/12.0/animal_owner>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

3
animal_owner/__init__.py

@ -1,3 +0,0 @@
# Copyright (C) 2020 Open Source Integrators
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import models

16
animal_owner/__manifest__.py

@ -1,16 +0,0 @@
# Copyright (C) 2020 Open Source Integrators
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "Animal Owner",
"version": "12.0.1.0.0",
"license": "AGPL-3",
"summary": "Add owner to the animal",
"author": "Open Source Integrators, Odoo Community Association (OCA)",
"maintainer": "Open Source Integrators",
"website": "https://github.com/OCA/partner-contact",
"depends": ["animal"],
"data": ["views/animal.xml", "views/res_partner.xml"],
"application": False,
"development_status": "Beta",
"maintainers": ["max3903"],
}

48
animal_owner/i18n/animal_owner.pot

@ -1,48 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * animal_owner
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 12.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: animal_owner
#: model_terms:ir.ui.view,arch_db:animal_owner.view_partner_animal_owner_form
msgid "<span class=\"o_stat_text\"> Animals</span>"
msgstr ""
#. module: animal_owner
#: model:ir.model,name:animal_owner.model_animal
msgid "Animal"
msgstr ""
#. module: animal_owner
#: model:ir.model.fields,field_description:animal_owner.field_res_partner__animal_ids
#: model:ir.model.fields,field_description:animal_owner.field_res_users__animal_ids
msgid "Animals"
msgstr ""
#. module: animal_owner
#: model:ir.model,name:animal_owner.model_res_partner
msgid "Contact"
msgstr ""
#. module: animal_owner
#: model:ir.model.fields,field_description:animal_owner.field_res_partner__animal_count
#: model:ir.model.fields,field_description:animal_owner.field_res_users__animal_count
msgid "Number of Animals"
msgstr ""
#. module: animal_owner
#: model:ir.model.fields,field_description:animal_owner.field_animal__partner_id
#: model_terms:ir.ui.view,arch_db:animal_owner.view_animal_owner_search
msgid "Owner"
msgstr ""

50
animal_owner/i18n/es.po

@ -1,50 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * animal_owner
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 12.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2020-10-20 04:08+0000\n"
"Last-Translator: Maxime Chambreuil <mchambreuil@opensourceintegrators.com>\n"
"Language-Team: none\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 3.10\n"
#. module: animal_owner
#: model_terms:ir.ui.view,arch_db:animal_owner.view_partner_animal_owner_form
msgid "<span class=\"o_stat_text\"> Animals</span>"
msgstr "<span class=\"o_stat_text\"> Animales</span>"
#. module: animal_owner
#: model:ir.model,name:animal_owner.model_animal
msgid "Animal"
msgstr ""
#. module: animal_owner
#: model:ir.model.fields,field_description:animal_owner.field_res_partner__animal_ids
#: model:ir.model.fields,field_description:animal_owner.field_res_users__animal_ids
msgid "Animals"
msgstr "Animales"
#. module: animal_owner
#: model:ir.model,name:animal_owner.model_res_partner
msgid "Contact"
msgstr "Contacto"
#. module: animal_owner
#: model:ir.model.fields,field_description:animal_owner.field_res_partner__animal_count
#: model:ir.model.fields,field_description:animal_owner.field_res_users__animal_count
msgid "Number of Animals"
msgstr "Numero de animales"
#. module: animal_owner
#: model:ir.model.fields,field_description:animal_owner.field_animal__partner_id
#: model_terms:ir.ui.view,arch_db:animal_owner.view_animal_owner_search
msgid "Owner"
msgstr "Dueño"

3
animal_owner/models/__init__.py

@ -1,3 +0,0 @@
# Copyright (C) 2020 Open Source Integrators
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import animal, res_partner

11
animal_owner/models/animal.py

@ -1,11 +0,0 @@
# Copyright (C) 2020 Open Source Integrators
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models
class Animal(models.Model):
_inherit = "animal"
partner_id = fields.Many2one(
"res.partner", string="Owner", index=True, track_visibility="onchange"
)

27
animal_owner/models/res_partner.py

@ -1,27 +0,0 @@
# Copyright (C) 2020 Open Source Integrators
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models
class ResPartner(models.Model):
_inherit = "res.partner"
@api.depends("animal_ids")
def _compute_animal_count(self):
for rec in self:
rec.animal_count = len(rec.animal_ids)
animal_ids = fields.One2many("animal", "partner_id", string="Animals")
animal_count = fields.Integer(
compute=_compute_animal_count, string="Number of Animals", store=True
)
@api.multi
def action_view_animals(self):
action = self.env.ref("animal.action_animal").read()[0]
if self.animal_count > 1:
action["domain"] = [("id", "in", self.animal_ids.ids)]
else:
action["views"] = [(self.env.ref("animal.view_animal_form").id, "form")]
action["res_id"] = self.animal_ids and self.animal_ids.ids[0] or False
return action

3
animal_owner/readme/CONTRIBUTORS.rst

@ -1,3 +0,0 @@
* Open Source Integrators <https://www.opensourceintegrators.com>
* Maxime Chambreuil <mchambreuil@opensourceintegrators.com>

1
animal_owner/readme/DESCRIPTION.rst

@ -1 +0,0 @@
This module allows you to store the owner of a pet.

3
animal_owner/readme/USAGE.rst

@ -1,3 +0,0 @@
* Go to Animals
* Create or select an animal
* Select his owner

BIN
animal_owner/static/description/icon.png

Before

Width: 460  |  Height: 460  |  Size: 28 KiB

433
animal_owner/static/description/index.html

@ -1,433 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.15.1: http://docutils.sourceforge.net/" />
<title>Animal Owner</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: grey; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="animal-owner">
<h1 class="title">Animal Owner</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/partner-contact/tree/12.0/animal_owner"><img alt="OCA/partner-contact" src="https://img.shields.io/badge/github-OCA%2Fpartner--contact-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/partner-contact-12-0/partner-contact-12-0-animal_owner"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/134/12.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
<p>This module allows you to store the owner of a pet.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#usage" id="id1">Usage</a></li>
<li><a class="reference internal" href="#bug-tracker" id="id2">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id3">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id4">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id5">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="id6">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#id1">Usage</a></h1>
<ul class="simple">
<li>Go to Animals</li>
<li>Create or select an animal</li>
<li>Select his owner</li>
</ul>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#id2">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/partner-contact/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/partner-contact/issues/new?body=module:%20animal_owner%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h1><a class="toc-backref" href="#id3">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#id4">Authors</a></h2>
<ul class="simple">
<li>Open Source Integrators</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#id5">Contributors</a></h2>
<ul class="simple">
<li>Open Source Integrators &lt;<a class="reference external" href="https://www.opensourceintegrators.com">https://www.opensourceintegrators.com</a>&gt;<ul>
<li>Maxime Chambreuil &lt;<a class="reference external" href="mailto:mchambreuil&#64;opensourceintegrators.com">mchambreuil&#64;opensourceintegrators.com</a>&gt;</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#id6">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>
<p>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainer</a>:</p>
<p><a class="reference external" href="https://github.com/max3903"><img alt="max3903" src="https://github.com/max3903.png?size=40px" /></a></p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/partner-contact/tree/12.0/animal_owner">OCA/partner-contact</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</body>
</html>

58
animal_owner/views/animal.xml

@ -1,58 +0,0 @@
<odoo>
<!-- Animal Tree View -->
<record id="view_animal_owner_tree" model="ir.ui.view">
<field name="name">view.animal.owner.tree</field>
<field name="model">animal</field>
<field name="inherit_id" ref="animal.view_animal_tree"/>
<field name="arch" type="xml">
<field name="color_id" position="after">
<field name="partner_id"/>
</field>
</field>
</record>
<!-- Animal Form View -->
<record id="view_animal_owner_form" model="ir.ui.view">
<field name="name">view.animal.owner.form</field>
<field name="model">animal</field>
<field name="inherit_id" ref="animal.view_animal_form"/>
<field name="arch" type="xml">
<field name="birth_date" position="after">
<field name="partner_id"/>
</field>
</field>
</record>
<!-- Animal Kanban View -->
<record id="view_animal_owner_kanban" model="ir.ui.view">
<field name="name">view.animal.owner.kanban</field>
<field name="model">animal</field>
<field name="inherit_id" ref="animal.view_animal_kanban"/>
<field name="arch" type="xml">
<field name="name" position="after">
<field name="partner_id"/>
</field>
<xpath expr="//div[hasclass('oe_kanban_details')]/ul" position="inside">
<li t-if="record.partner_id.raw_value"><field name="partner_id"/></li>
</xpath>
</field>
</record>
<!-- Animal Search View -->
<record id="view_animal_owner_search" model="ir.ui.view">
<field name="name">view.animal.owner.search</field>
<field name="model">animal</field>
<field name="inherit_id" ref="animal.view_animal_search"/>
<field name="arch" type="xml">
<field name="name" position="after">
<field name="partner_id"/>
</field>
<filter name="gender" position="before">
<filter name="partner_id" string="Owner" domain=""
context="{'group_by': 'partner_id'}"/>
</filter>
</field>
</record>
</odoo>

22
animal_owner/views/res_partner.xml

@ -1,22 +0,0 @@
<odoo>
<!-- Partner Form View -->
<record id="view_partner_animal_owner_form" model="ir.ui.view">
<field name="name">view.partner.animal.owner.form</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<button name="toggle_active" position="before">
<button class="oe_stat_button" type="object"
name="action_view_animals" icon="fa-pencil-square-o"
context="{'default_partner_id': id}">
<div class="o_stat_info">
<field name="animal_count" class="o_stat_value"/>
<span class="o_stat_text"> Animals</span>
</div>
</button>
</button>
</field>
</record>
</odoo>

22
base_contact/__init__.py

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2013-TODAY OpenERP SA (<http://www.openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import base_contact

56
base_contact/__openerp__.py

@ -0,0 +1,56 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (C) 2013-TODAY OpenERP S.A. (<http://openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'name': 'Contacts Management',
'version': '1.0',
'author': "OpenERP SA,Odoo Community Association (OCA)",
'website': 'http://www.openerp.com',
'category': 'Customer Relationship Management',
'complexity': "expert",
'description': """
This module allows you to manage your contacts
==============================================
It lets you define groups of contacts sharing some common information, like:
* Birthdate
* Nationality
* Native Language
""",
'depends': [
'base',
'process',
'contacts'
],
'external_dependencies': {},
'data': [
'base_contact_view.xml',
],
'demo': [
'base_contact_demo.xml',
],
'test': [],
'installable': True,
'auto_install': False,
'images': [],
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

239
base_contact/base_contact.py

@ -0,0 +1,239 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2013-TODAY OpenERP SA (<http://www.openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import fields, orm, expression
from openerp.tools.translate import _
class res_partner(orm.Model):
_inherit = 'res.partner'
def _type_selection(self, cr, uid, context=None):
return [
('standalone', _('Standalone Contact')),
('attached', _('Attached to existing Contact')),
]
def _get_contact_type(self, cr, uid, ids, field_name, args, context=None):
result = dict.fromkeys(ids, 'standalone')
for partner in self.browse(cr, uid, ids, context=context):
if partner.contact_id:
result[partner.id] = 'attached'
return result
_columns = {
'contact_type': fields.function(
_get_contact_type,
type='selection',
selection=lambda self, *a, **kw: self._type_selection(*a, **kw),
string='Contact Type',
required=True,
select=1,
store=True,
),
'contact_id': fields.many2one(
'res.partner',
'Main Contact',
domain=[
('is_company', '=', False),
('contact_type', '=', 'standalone'),
],
),
'other_contact_ids': fields.one2many(
'res.partner',
'contact_id',
'Others Positions',
),
# Person specific fields
# add a 'birthdate' as date field, i.e different from char
# 'birthdate' introduced v6.1!
'birthdate_date': fields.date('Birthdate'),
'nationality_id': fields.many2one('res.country', 'Nationality'),
}
_defaults = {
'contact_type': 'standalone',
}
def _basecontact_check_context(self, cr, user, mode, context=None):
""" Remove 'search_show_all_positions' for non-search mode.
Keeping it in context can result in unexpected behaviour (ex: reading
one2many might return wrong result - i.e with "attached contact"
removed even if it's directly linked to a company).
"""
if context is None:
context = {}
if mode != 'search':
context.pop('search_show_all_positions', None)
return context
def search(
self, cr, user, args, offset=0, limit=None, order=None,
context=None, count=False):
""" Display only standalone contact matching ``args`` or having
attached contact matching ``args`` """
if context is None:
context = {}
if context.get('search_show_all_positions') is False:
args = expression.normalize_domain(args)
attached_contact_args = expression.AND(
(args, [('contact_type', '=', 'attached')])
)
attached_contact_ids = super(res_partner, self).search(
cr, user, attached_contact_args, context=context
)
args = expression.OR((
expression.AND(([('contact_type', '=', 'standalone')], args)),
[('other_contact_ids', 'in', attached_contact_ids)],
))
return super(res_partner, self).search(
cr, user, args, offset=offset, limit=limit, order=order,
context=context, count=count
)
def create(self, cr, user, vals, context=None):
context = self._basecontact_check_context(cr, user, 'create', context)
if not vals.get('name') and vals.get('contact_id'):
vals['name'] = self.browse(
cr, user, vals['contact_id'], context=context).name
return super(res_partner, self).create(cr, user, vals, context=context)
def read(
self, cr, user, ids, fields=None, context=None,
load='_classic_read'):
context = self._basecontact_check_context(cr, user, 'read', context)
return super(res_partner, self).read(
cr, user, ids, fields=fields, context=context, load=load)
def write(self, cr, user, ids, vals, context=None):
context = self._basecontact_check_context(cr, user, 'write', context)
return super(
res_partner, self).write(cr, user, ids, vals, context=context)
def unlink(self, cr, user, ids, context=None):
context = self._basecontact_check_context(cr, user, 'unlink', context)
return super(res_partner, self).unlink(cr, user, ids, context=context)
def _commercial_partner_compute(
self, cr, uid, ids, name, args, context=None):
""" Returns the partner that is considered the commercial
entity of this partner. The commercial entity holds the master data
for all commercial fields (see :py:meth:`~_commercial_fields`) """
result = super(res_partner, self)._commercial_partner_compute(
cr, uid, ids, name, args, context=context)
for partner in self.browse(cr, uid, ids, context=context):
if partner.contact_type == 'attached' and not partner.parent_id:
result[partner.id] = partner.contact_id.id
return result
def _contact_fields(self, cr, uid, context=None):
""" Returns the list of contact fields that are synced from the parent
when a partner is attached to him. """
return ['name', 'title']
def _contact_sync_from_parent(self, cr, uid, partner, context=None):
""" Handle sync of contact fields when a new parent contact entity
is set, as if they were related fields
"""
if partner.contact_id:
contact_fields = self._contact_fields(cr, uid, context=context)
sync_vals = self._update_fields_values(
cr, uid, partner.contact_id, contact_fields, context=context
)
partner.write(sync_vals)
def update_contact(self, cr, uid, ids, vals, context=None):
if context is None:
context = {}
if context.get('__update_contact_lock'):
return
contact_fields = self._contact_fields(cr, uid, context=context)
contact_vals = dict(
(field, vals[field]) for field in contact_fields if field in vals
)
if contact_vals:
ctx = dict(context, __update_contact_lock=True)
self.write(cr, uid, ids, contact_vals, context=ctx)
def _fields_sync(self, cr, uid, partner, update_values, context=None):
"""Sync commercial fields and address fields from company and to
children, contact fields from contact and to attached contact
after create/update, just as if those were all modeled as
fields.related to the parent
"""
super(res_partner, self)._fields_sync(
cr, uid, partner, update_values, context=context
)
contact_fields = self._contact_fields(cr, uid, context=context)
# 1. From UPSTREAM: sync from parent contact
if update_values.get('contact_id'):
self._contact_sync_from_parent(cr, uid, partner, context=context)
# 2. To DOWNSTREAM: sync contact fields to parent or related
elif any(field in contact_fields for field in update_values):
update_ids = [
c.id for c in partner.other_contact_ids if not c.is_company
]
if partner.contact_id:
update_ids.append(partner.contact_id.id)
self.update_contact(
cr, uid, update_ids, update_values, context=context
)
def onchange_contact_id(self, cr, uid, ids, contact_id, context=None):
values = {}
if contact_id:
values['name'] = self.browse(
cr, uid, contact_id, context=context).name
return {'value': values}
def onchange_contact_type(self, cr, uid, ids, contact_type, context=None):
values = {}
if contact_type == 'standalone':
values['contact_id'] = False
return {'value': values}
class ir_actions_window(orm.Model):
_inherit = 'ir.actions.act_window'
def read(
self, cr, user, ids, fields=None, context=None,
load='_classic_read'):
action_ids = ids
if isinstance(ids, (int, long)):
action_ids = [ids]
actions = super(ir_actions_window, self).read(
cr, user, action_ids, fields=fields, context=context, load=load
)
for action in actions:
if action.get('res_model', '') == 'res.partner':
# By default, only show standalone contact
action_context = action.get('context', '{}') or '{}'
if 'search_show_all_positions' not in action_context:
action['context'] = action_context.replace(
'{', "{'search_show_all_positions': False,", 1
)
if isinstance(ids, (int, long)):
if actions:
return actions[0]
return False
return actions

29
base_contact/base_contact_demo.xml

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<openerp>
<data>
<record id="res_partner_main2_position_consultant" model="res.partner">
<field name="name">Roger Scott</field>
<field name="function">Consultant</field>
<field name="parent_id" ref="base.res_partner_11"/>
<field name="contact_id" ref="base.res_partner_main2"/>
<field name="use_parent_address" eval="True"/>
</record>
<record id="res_partner_contact1" model="res.partner">
<field name="name">Bob Egnops</field>
<field name="birthdate_date">1984-01-01</field>
<field name="email">bob@hillenburg-oceaninstitute.com</field>
</record>
<record id="res_partner_contact1_work_position1" model="res.partner">
<field name="name">Bob Egnops</field>
<field name="function">Technician</field>
<field name="email">bob@yourcompany.com</field>
<field name="parent_id" ref="base.main_partner"/>
<field name="contact_id" ref="res_partner_contact1"/>
<field name="use_parent_address" eval="True"/>
</record>
</data>
</openerp>

204
base_contact/base_contact_view.xml

@ -0,0 +1,204 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_res_partner_filter_contact" model="ir.ui.view">
<field name="name">res.partner.select.contact</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_res_partner_filter"/>
<field name="arch" type="xml">
<filter name="type_company" position="after">
<separator/>
<filter string="All positions" name="type_otherpositions"
context="{'search_show_all_positions': True}"
help="All partner positions"/>
</filter>
<xpath expr="/search/group/filter[@string='Company']" position="before">
<filter string="Person" name="group_person" context="{'group_by': 'contact_id'}"/>
</xpath>
</field>
</record>
<record id="view_res_partner_tree_contact" model="ir.ui.view">
<field name="name">res.partner.tree.contact</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_tree"/>
<field name="arch" type="xml">
<field name="parent_id" position="after">
<field name="contact_id" invisible="1"/>
</field>
</field>
</record>
<record model="ir.ui.view" id="view_partner_form_inherit">
<field name="name">res.partner.form.contact</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<field name="is_company" position="after">
<field name="contact_type" invisible="1"/>
</field>
<page string="Contacts" position="after">
<page string="Other Positions" attrs="{'invisible': ['|',('is_company','=',True),('contact_id','!=',False)]}">
<field name="other_contact_ids" context="{'default_contact_id': active_id, 'default_name': name, 'default_street': street, 'default_street2': street2, 'default_city': city, 'default_state_id': state_id, 'default_zip': zip, 'default_country_id': country_id, 'default_supplier': supplier}}" mode="kanban">
<kanban>
<field name="color"/>
<field name="name"/>
<field name="title"/>
<field name="email"/>
<field name="parent_id"/>
<field name="is_company"/>
<field name="function"/>
<field name="phone"/>
<field name="street"/>
<field name="street2"/>
<field name="zip"/>
<field name="city"/>
<field name="country_id"/>
<field name="mobile"/>
<field name="fax"/>
<field name="state_id"/>
<field name="has_image"/>
<templates>
<t t-name="kanban-box">
<t t-set="color" t-value="kanban_color(record.color.raw_value)"/>
<div t-att-class="color + (record.title.raw_value == 1 ? ' oe_kanban_color_alert' : '')" style="position: relative">
<a t-if="! read_only_mode" type="delete" style="position: absolute; right: 0; padding: 4px; diplay: inline-block">X</a>
<div class="oe_module_vignette">
<a type="open">
<t t-if="record.has_image.raw_value === true">
<img t-att-src="kanban_image('res.partner', 'image', record.id.value, {'preview_image': 'image_small'})" class="oe_avatar oe_kanban_avatar_smallbox"/>
</t>
<t t-if="record.image and record.image.raw_value !== false">
<img t-att-src="'data:image/png;base64,'+record.image.raw_value" class="oe_avatar oe_kanban_avatar_smallbox"/>
</t>
<t t-if="record.has_image.raw_value === false and (!record.image or record.image.raw_value === false)">
<t t-if="record.is_company.raw_value === true">
<img t-att-src='_s + "/base/static/src/img/company_image.png"' class="oe_kanban_image oe_kanban_avatar_smallbox"/>
</t>
<t t-if="record.is_company.raw_value === false">
<img t-att-src='_s + "/base/static/src/img/avatar.png"' class="oe_kanban_image oe_kanban_avatar_smallbox"/>
</t>
</t>
</a>
<div class="oe_module_desc">
<div class="oe_kanban_box_content oe_kanban_color_bglight oe_kanban_color_border">
<table class="oe_kanban_table">
<tr>
<td class="oe_kanban_title1" align="left" valign="middle">
<h4><a type="open"><field name="name"/></a></h4>
<i>
<t t-if="record.parent_id.raw_value and !record.function.raw_value"><field name="parent_id"/></t>
<t t-if="!record.parent_id.raw_value and record.function.raw_value"><field name="function"/></t>
<t t-if="record.parent_id.raw_value and record.function.raw_value"><field name="function"/> at <field name="parent_id"/></t>
</i>
<div><a t-if="record.email.raw_value" title="Mail" t-att-href="'mailto:'+record.email.value">
<field name="email"/>
</a></div>
<div t-if="record.phone.raw_value">Phone: <field name="phone"/></div>
<div t-if="record.mobile.raw_value">Mobile: <field name="mobile"/></div>
<div t-if="record.fax.raw_value">Fax: <field name="fax"/></div>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</t>
</templates>
</kanban>
<form string="Contact" version="7.0">
<sheet>
<field name="image" widget='image' class="oe_avatar oe_left" options='{"preview_image": "image_medium"}'/>
<div class="oe_title">
<label for="name" class="oe_edit_only"/>
<h1><field name="name" style="width: 70%%"/></h1>
</div>
<group>
<!-- inherited part -->
<field name="category_id" widget="many2many_tags" placeholder="Tags..." style="width: 70%%"/>
<field name="parent_id" placeholder="Company" domain="[('is_company','=',True)]"/>
<!-- inherited part end -->
<field name="function" placeholder="e.g. Sales Director"/>
<field name="email"/>
<field name="phone"/>
<field name="mobile"/>
</group>
<div>
<field name="use_parent_address"/><label for="use_parent_address"/>
</div>
<group>
<label for="type"/>
<div name="div_type">
<field class="oe_inline" name="type"/>
</div>
<label for="street" string="Address" attrs="{'invisible': [('use_parent_address','=', True)]}"/>
<div attrs="{'invisible': [('use_parent_address','=', True)]}" name="div_address">
<field name="street" placeholder="Street..."/>
<field name="street2"/>
<div class="address_format">
<field name="city" placeholder="City" style="width: 40%%"/>
<field name="state_id" class="oe_no_button" placeholder="State" style="width: 37%%" options='{"no_open": True}' on_change="onchange_state(state_id)"/>
<field name="zip" placeholder="ZIP" style="width: 20%%"/>
</div>
<field name="country_id" placeholder="Country" class="oe_no_button" options='{"no_open": True}'/>
</div>
</group>
<field name="supplier" invisible="True"/>
</sheet>
</form>
</field>
</page>
<page name="personal-info" string="Personal Information" attrs="{'invisible': ['|',('is_company','=',True)]}">
<p attrs="{'invisible': [('contact_id','=',False)]}">
To see personal information about this contact, please go to to the his person form: <field name="contact_id" class="oe_inline" domain="[('contact_type','!=','attached')]" context="{'show_address': 1}"
on_change="onchange_contact_id(contact_id)" options="{'always_reload': True}"/>
</p>
<group attrs="{'invisible': [('contact_id','!=',False)]}">
<field name="birthdate_date"/>
<field name="nationality_id"/>
</group>
</page>
</page>
<xpath expr="//form[@string='Contact']/sheet//field[@name='category_id']" position="before">
<group>
<label for="contact_type" class="oe_edit_only"/>
<field name="contact_type" readonly="0" on_change="onchange_contact_type(contact_type)" nolabel="1"/>
</group>
</xpath>
<xpath expr="//field[@name='child_ids']/form//field[@name='name']" position="after">
<field name="contact_id" on_change="onchange_contact_id(contact_id)" string="Contact"
attrs="{'invisible': [('contact_type','!=','attached')], 'required': [('contact_type','=','attached')]}"/>
</xpath>
<xpath expr="//field[@name='child_ids']/form//field[@name='name']" position="attributes">
<attribute name="attrs">{'invisible': [('contact_type','=','attached')]}</attribute>
</xpath>
</field>
</record>
<record model="ir.ui.view" id="view_res_partner_kanban_contact">
<field name="name">res.partner.kanban.contact</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.res_partner_kanban_view"/>
<field name="arch" type="xml">
<field name="is_company" position="after">
<field name="other_contact_ids">
<tree>
<field name="parent_id"/>
<field name="function"/>
</tree>
</field>
</field>
<xpath expr="//t[@t-name='kanban-box']//div[@class='oe_kanban_details']/ul/li[3]" position="after">
<t t-if="record.other_contact_ids.raw_value.length &gt; 0">
<li>+<t t-esc="record.other_contact_ids.raw_value.length"/>
<t t-if="record.other_contact_ids.raw_value.length == 1">other position</t>
<t t-if="record.other_contact_ids.raw_value.length &gt; 1">other positions</t></li>
</t>
</xpath>
</field>
</record>
</data>
</openerp>

191
base_contact/i18n/base_contact.pot

@ -0,0 +1,191 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * base_contact
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 7.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-09-19 22:10+0000\n"
"PO-Revision-Date: 2014-09-19 18:10-0500\n"
"Last-Translator: EL Hadji DEM <elhadji.dem@savoirfairelinux.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: \n"
"X-Generator: Poedit 1.5.4\n"
#. module: base_contact
#: view:res.partner:0
msgid "City"
msgstr ""
#. module: base_contact
#: view:res.partner:0
msgid "other position"
msgstr ""
#. module: base_contact
#: view:res.partner:0
msgid "Contacts"
msgstr ""
#. module: base_contact
#: view:res.partner:0
msgid ""
"To see personal information about this contact, please go to to the his "
"person form:"
msgstr ""
#. module: base_contact
#: view:res.partner:0
msgid "Street..."
msgstr ""
#. module: base_contact
#: view:res.partner:0
msgid "State"
msgstr ""
#. module: base_contact
#: view:res.partner:0
msgid "at"
msgstr ""
#. module: base_contact
#: view:res.partner:0
msgid "Tags..."
msgstr ""
#. module: base_contact
#: view:res.partner:0
msgid "Other Positions"
msgstr ""
#. module: base_contact
#: view:res.partner:0
msgid "Phone:"
msgstr ""
#. module: base_contact
#: view:res.partner:0
msgid "Company"
msgstr ""
#. module: base_contact
#: field:res.partner,contact_id:0
msgid "Main Contact"
msgstr ""
#. module: base_contact
#: view:res.partner:0
msgid "Fax:"
msgstr ""
#. module: base_contact
#: code:addons/base_contact/base_contact.py:31
#, python-format
msgid "Standalone Contact"
msgstr ""
#. module: base_contact
#: help:res.partner,nationality_id:0
msgid "Nationality."
msgstr ""
#. module: base_contact
#: view:res.partner:0
msgid "Address"
msgstr ""
#. module: base_contact
#: field:res.partner,nationality_id:0
msgid "Nationality"
msgstr ""
#. module: base_contact
#: code:addons/base_contact/base_contact.py:32
#, python-format
msgid "Attached to existing Contact"
msgstr ""
#. module: base_contact
#: view:res.partner:0
msgid "ZIP"
msgstr ""
#. module: base_contact
#: view:res.partner:0
msgid "Country"
msgstr ""
#. module: base_contact
#: field:res.partner,birthdate_date:0
msgid "Birthdate"
msgstr ""
#. module: base_contact
#: view:res.partner:0
msgid "Person"
msgstr ""
#. module: base_contact
#: view:res.partner:0
msgid "Contact"
msgstr ""
#. module: base_contact
#: view:res.partner:0
msgid "Mobile:"
msgstr ""
#. module: base_contact
#: view:res.partner:0
msgid "All partner positions"
msgstr ""
#. module: base_contact
#: view:res.partner:0
msgid "{'invisible': [('contact_type','=','attached')]}"
msgstr ""
#. module: base_contact
#: view:res.partner:0
msgid "All positions"
msgstr ""
#. module: base_contact
#: model:ir.model,name:base_contact.model_ir_actions_act_window
msgid "ir.actions.act_window"
msgstr ""
#. module: base_contact
#: view:res.partner:0
msgid "other positions"
msgstr ""
#. module: base_contact
#: field:res.partner,contact_type:0
msgid "Contact Type"
msgstr ""
#. module: base_contact
#: model:ir.model,name:base_contact.model_res_partner
msgid "Partner"
msgstr ""
#. module: base_contact
#: field:res.partner,other_contact_ids:0
msgid "Others Positions"
msgstr ""
#. module: base_contact
#: view:res.partner:0
msgid "Personal Information"
msgstr ""
#. module: base_contact
#: view:res.partner:0
msgid "e.g. Sales Director"
msgstr ""

188
base_contact/i18n/de.po

@ -0,0 +1,188 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * base_contact
#
# Translators:
# Rudolf Schnapka <schnapkar@golive-saar.de>, 2014
msgid ""
msgstr ""
"Project-Id-Version: partner-contact (7.0)\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-09-30 13:27+0000\n"
"PO-Revision-Date: 2015-05-22 13:07+0000\n"
"Last-Translator: Maxime Chambreuil <maxime.chambreuil@gmail.com>\n"
"Language-Team: German (http://www.transifex.com/oca/OCA-partner-contact-7-0/language/de/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: de\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: base_contact
#: view:res.partner:0
msgid "City"
msgstr "Stadt"
#. module: base_contact
#: view:res.partner:0
msgid "other position"
msgstr "andere Position"
#. module: base_contact
#: view:res.partner:0
msgid "Contacts"
msgstr "Kontakte"
#. module: base_contact
#: view:res.partner:0
msgid ""
"To see personal information about this contact, please go to to the his "
"person form:"
msgstr "Um persönliche Auskunft zu diesem Kontakt zu erhalten, gehen Sie bitte auf die Seite persönlich:"
#. module: base_contact
#: view:res.partner:0
msgid "Street..."
msgstr "Straße..."
#. module: base_contact
#: view:res.partner:0
msgid "State"
msgstr "Bundesland"
#. module: base_contact
#: view:res.partner:0
msgid "at"
msgstr "bei"
#. module: base_contact
#: view:res.partner:0
msgid "Tags..."
msgstr "Schlagworte..."
#. module: base_contact
#: view:res.partner:0
msgid "Other Positions"
msgstr "Andere Positionen"
#. module: base_contact
#: view:res.partner:0
msgid "Phone:"
msgstr "Telefon:"
#. module: base_contact
#: view:res.partner:0
msgid "Company"
msgstr "Unternehmen"
#. module: base_contact
#: field:res.partner,contact_id:0
msgid "Main Contact"
msgstr "Hauptkontakt"
#. module: base_contact
#: view:res.partner:0
msgid "Fax:"
msgstr "Fax:"
#. module: base_contact
#: code:addons/base_contact/base_contact.py:31
#, python-format
msgid "Standalone Contact"
msgstr "Alleinstehender Kontakt"
#. module: base_contact
#: view:res.partner:0
msgid "Address"
msgstr "Anschrift"
#. module: base_contact
#: field:res.partner,nationality_id:0
msgid "Nationality"
msgstr "Nationalität"
#. module: base_contact
#: code:addons/base_contact/base_contact.py:32
#, python-format
msgid "Attached to existing Contact"
msgstr "Mit bestehendem Kontakt verknüpft"
#. module: base_contact
#: view:res.partner:0
msgid "ZIP"
msgstr "PLZ"
#. module: base_contact
#: view:res.partner:0
msgid "Country"
msgstr "Land"
#. module: base_contact
#: field:res.partner,birthdate_date:0
msgid "Birthdate"
msgstr "Geburtsdatum"
#. module: base_contact
#: view:res.partner:0
msgid "Person"
msgstr "Person"
#. module: base_contact
#: view:res.partner:0
msgid "Contact"
msgstr "Kontakt"
#. module: base_contact
#: view:res.partner:0
msgid "Mobile:"
msgstr "Mobil:"
#. module: base_contact
#: view:res.partner:0
msgid "All partner positions"
msgstr "Alle Positionen des Partners"
#. module: base_contact
#: view:res.partner:0
msgid "{'invisible': [('contact_type','=','attached')]}"
msgstr ""
#. module: base_contact
#: view:res.partner:0
msgid "All positions"
msgstr "Alle Positionen"
#. module: base_contact
#: model:ir.model,name:base_contact.model_ir_actions_act_window
msgid "ir.actions.act_window"
msgstr ""
#. module: base_contact
#: view:res.partner:0
msgid "other positions"
msgstr "andere Positionen"
#. module: base_contact
#: field:res.partner,contact_type:0
msgid "Contact Type"
msgstr "Kontaktart"
#. module: base_contact
#: model:ir.model,name:base_contact.model_res_partner
msgid "Partner"
msgstr "Partner"
#. module: base_contact
#: field:res.partner,other_contact_ids:0
msgid "Others Positions"
msgstr "Andere Positionen"
#. module: base_contact
#: view:res.partner:0
msgid "Personal Information"
msgstr "Persönliche Auskunft"
#. module: base_contact
#: view:res.partner:0
msgid "e.g. Sales Director"
msgstr "z. B. Verkaufsleiter"

187
base_contact/i18n/fr.po

@ -0,0 +1,187 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * base_contact
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: partner-contact (7.0)\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-09-30 13:27+0000\n"
"PO-Revision-Date: 2015-05-22 13:07+0000\n"
"Last-Translator: Maxime Chambreuil <maxime.chambreuil@gmail.com>\n"
"Language-Team: French (http://www.transifex.com/oca/OCA-partner-contact-7-0/language/fr/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: fr\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#. module: base_contact
#: view:res.partner:0
msgid "City"
msgstr "Ville"
#. module: base_contact
#: view:res.partner:0
msgid "other position"
msgstr "fonction"
#. module: base_contact
#: view:res.partner:0
msgid "Contacts"
msgstr "Contacts"
#. module: base_contact
#: view:res.partner:0
msgid ""
"To see personal information about this contact, please go to to the his "
"person form:"
msgstr "Pour voir des informations personnelles sur ce contact, s'il vous plaît aller au formulaire de la personne:"
#. module: base_contact
#: view:res.partner:0
msgid "Street..."
msgstr "Rue..."
#. module: base_contact
#: view:res.partner:0
msgid "State"
msgstr "État"
#. module: base_contact
#: view:res.partner:0
msgid "at"
msgstr "à"
#. module: base_contact
#: view:res.partner:0
msgid "Tags..."
msgstr "Étiquettes..."
#. module: base_contact
#: view:res.partner:0
msgid "Other Positions"
msgstr "Fonctions"
#. module: base_contact
#: view:res.partner:0
msgid "Phone:"
msgstr "Téléphone:"
#. module: base_contact
#: view:res.partner:0
msgid "Company"
msgstr "Organisme"
#. module: base_contact
#: field:res.partner,contact_id:0
msgid "Main Contact"
msgstr "Principal contact"
#. module: base_contact
#: view:res.partner:0
msgid "Fax:"
msgstr "Fax :"
#. module: base_contact
#: code:addons/base_contact/base_contact.py:31
#, python-format
msgid "Standalone Contact"
msgstr "Contact autonome"
#. module: base_contact
#: view:res.partner:0
msgid "Address"
msgstr "Adresse"
#. module: base_contact
#: field:res.partner,nationality_id:0
msgid "Nationality"
msgstr "Nationalité"
#. module: base_contact
#: code:addons/base_contact/base_contact.py:32
#, python-format
msgid "Attached to existing Contact"
msgstr "Contact rattaché à un contact existant"
#. module: base_contact
#: view:res.partner:0
msgid "ZIP"
msgstr "Code postal"
#. module: base_contact
#: view:res.partner:0
msgid "Country"
msgstr "Pays"
#. module: base_contact
#: field:res.partner,birthdate_date:0
msgid "Birthdate"
msgstr "Date de naissance"
#. module: base_contact
#: view:res.partner:0
msgid "Person"
msgstr "Personne"
#. module: base_contact
#: view:res.partner:0
msgid "Contact"
msgstr "Contact"
#. module: base_contact
#: view:res.partner:0
msgid "Mobile:"
msgstr "Portable :"
#. module: base_contact
#: view:res.partner:0
msgid "All partner positions"
msgstr "Tous les contacts"
#. module: base_contact
#: view:res.partner:0
msgid "{'invisible': [('contact_type','=','attached')]}"
msgstr "{'invisible': [('contact_type','=','attached')]}"
#. module: base_contact
#: view:res.partner:0
msgid "All positions"
msgstr "Tous les contacts"
#. module: base_contact
#: model:ir.model,name:base_contact.model_ir_actions_act_window
msgid "ir.actions.act_window"
msgstr "ir.actions.act_window"
#. module: base_contact
#: view:res.partner:0
msgid "other positions"
msgstr "fonctions"
#. module: base_contact
#: field:res.partner,contact_type:0
msgid "Contact Type"
msgstr "Type de contact"
#. module: base_contact
#: model:ir.model,name:base_contact.model_res_partner
msgid "Partner"
msgstr "Partenaire"
#. module: base_contact
#: field:res.partner,other_contact_ids:0
msgid "Others Positions"
msgstr "Fonctions"
#. module: base_contact
#: view:res.partner:0
msgid "Personal Information"
msgstr "Informations personnelles"
#. module: base_contact
#: view:res.partner:0
msgid "e.g. Sales Director"
msgstr "ex: Directeur des ventes"

26
base_contact/tests/__init__.py

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (C) 2013-TODAY OpenERP S.A. (<http://openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import test_base_contact
checks = [
test_base_contact,
]

201
base_contact/tests/test_base_contact.py

@ -0,0 +1,201 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (C) 2013-TODAY OpenERP S.A. (<http://openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.tests import common
class Test_Base_Contact(common.TransactionCase):
def setUp(self):
"""*****setUp*****"""
super(Test_Base_Contact, self).setUp()
cr, uid = self.cr, self.uid
ModelData = self.registry('ir.model.data')
self.partner = self.registry('res.partner')
# Get test records reference
for attr, module, name in [
('main_partner_id', 'base', 'main_partner'),
('bob_contact_id', 'base_contact', 'res_partner_contact1'),
('bob_job1_id', 'base_contact',
'res_partner_contact1_work_position1'),
('roger_contact_id', 'base', 'res_partner_main2'),
('roger_job2_id', 'base_contact',
'res_partner_main2_position_consultant')]:
r = ModelData.get_object_reference(cr, uid, module, name)
setattr(self, attr, r[1] if r else False)
def test_00_show_only_standalone_contact(self):
"""Check that only standalone contact are shown if context
explicitly state to not display all positions
"""
cr, uid = self.cr, self.uid
ctx = {'search_show_all_positions': False}
partner_ids = self.partner.search(cr, uid, [], context=ctx)
partner_ids.sort()
self.assertTrue(self.bob_job1_id not in partner_ids)
self.assertTrue(self.roger_job2_id not in partner_ids)
def test_01_show_all_positions(self):
"""Check that all contact are show if context is empty or
explicitly state to display all positions
"""
cr, uid = self.cr, self.uid
partner_ids = self.partner.search(cr, uid, [], context=None)
self.assertTrue(self.bob_job1_id in partner_ids)
self.assertTrue(self.roger_job2_id in partner_ids)
ctx = {'search_show_all_positions': True}
partner_ids = self.partner.search(cr, uid, [], context=ctx)
self.assertTrue(self.bob_job1_id in partner_ids)
self.assertTrue(self.roger_job2_id in partner_ids)
def test_02_reading_other_contact_one2many_show_all_positions(self):
"""Check that readonly partner's ``other_contact_ids`` return
all values whatever the context
"""
cr, uid = self.cr, self.uid
def read_other_contacts(pid, context=None):
return self.partner.read(
cr, uid, [pid], ['other_contact_ids'],
context=context)[0]['other_contact_ids']
def read_contacts(pid, context=None):
return self.partner.read(
cr, uid, [pid], ['child_ids'], context=context)[0]['child_ids']
ctx = None
self.assertEqual(
read_other_contacts(self.bob_contact_id, context=ctx),
[self.bob_job1_id],
)
ctx = {'search_show_all_positions': False}
self.assertEqual(read_other_contacts(
self.bob_contact_id, context=ctx),
[self.bob_job1_id],
)
ctx = {'search_show_all_positions': True}
self.assertEqual(
read_other_contacts(self.bob_contact_id, context=ctx),
[self.bob_job1_id],
)
ctx = None
self.assertIn(
self.bob_job1_id,
read_contacts(self.main_partner_id, context=ctx),
)
ctx = {'search_show_all_positions': False}
self.assertIn(
self.bob_job1_id,
read_contacts(self.main_partner_id, context=ctx),
)
ctx = {'search_show_all_positions': True}
self.assertIn(
self.bob_job1_id,
read_contacts(self.main_partner_id, context=ctx),
)
def test_03_search_match_attached_contacts(self):
"""Check that searching partner also return partners having
attached contacts matching search criteria
"""
cr, uid = self.cr, self.uid
# Bob's contact has one other position which is related to
# 'Your Company'
# so search for all contacts working for 'Your Company' should contain
# bob position.
partner_ids = self.partner.search(
cr, uid,
[('parent_id', 'ilike', 'Your Company')],
context=None
)
self.assertIn(self.bob_job1_id, partner_ids, )
# but when searching without 'all positions',
# we should get the position standalone contact instead.
ctx = {'search_show_all_positions': False}
partner_ids = self.partner.search(
cr, uid,
[('parent_id', 'ilike', 'Your Company')],
context=ctx
)
self.assertIn(self.bob_contact_id, partner_ids, )
def test_04_contact_creation(self):
"""Check that we're begin to create a contact"""
cr, uid = self.cr, self.uid
# Create a contact using only name
new_contact_id = self.partner.create(cr, uid, {'name': 'Bob Egnops'})
self.assertEqual(
self.partner.browse(cr, uid, new_contact_id).contact_type,
'standalone',
)
# Create a contact with only contact_id
new_contact_id = self.partner.create(
cr, uid, {'contact_id': self.bob_contact_id}
)
new_contact = self.partner.browse(cr, uid, new_contact_id)
self.assertEqual(new_contact.name, 'Bob Egnops')
self.assertEqual(new_contact.contact_type, 'attached')
# Create a contact with both contact_id and name;
# contact's name should override provided value in that case
new_contact_id = self.partner.create(
cr, uid, {'contact_id': self.bob_contact_id, 'name': 'Rob Egnops'}
)
self.assertEqual(
self.partner.browse(cr, uid, new_contact_id).name,
'Bob Egnops'
)
# Reset contact to standalone
self.partner.write(cr, uid, [new_contact_id], {'contact_id': False})
self.assertEqual(
self.partner.browse(cr, uid, new_contact_id).contact_type,
'standalone',
)
def test_05_contact_fields_sync(self):
"""Check that contact's fields are correctly synced between
parent contact or related contacts
"""
cr, uid = self.cr, self.uid
# Test DOWNSTREAM sync
self.partner.write(
cr, uid, [self.bob_contact_id], {'name': 'Rob Egnops'}
)
self.assertEqual(
self.partner.browse(cr, uid, self.bob_job1_id).name,
'Rob Egnops',
)
# Test UPSTREAM sync
self.partner.write(cr, uid, [self.bob_job1_id], {'name': 'Bob Egnops'})
self.assertEqual(
self.partner.browse(cr, uid, self.bob_contact_id).name,
'Bob Egnops',
)

28
base_contact_function/__init__.py

@ -0,0 +1,28 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2013 Savoir-faire Linux
# (<http://www.savoirfairelinux.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import (
res_partner,
res_partner_function,
res_partner_category,
res_partner_category_function,
)

73
base_contact_function/__openerp__.py

@ -0,0 +1,73 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2013 Savoir-faire Linux
# (<http://www.savoirfairelinux.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'name': 'Contact by Function',
'version': '1.0',
'author': "Savoir-faire Linux,Odoo Community Association (OCA)",
'maintainer': 'Savoir-faire Linux',
'website': 'http://www.savoirfairelinux.com',
'category': 'Customer Relationship Management',
'description': """
Contacts by Functions
=====================
This module allows you to manage contacts by functions.
A person can occupy many job positions in many organizations and you may need
to retrieve all your contacts for a given job position.
This module replaces the single "job position" field on a contact and gives
the possibility to declare many functions (i.e job positions) for a contact.
You can also manage the functions on the organization
(res.partner is_company=True) itself.
To retrieve contacts by functions, categories (tags) are used in order to group
similar functions in a single category. Within a category, you can declare
the sequence of functions so you could use this sequence to sort the contacts
of this category by their function.
When you have functions for your contacts and functions for the categories you
can tag the contacts (manually or automatically with the segmentation tool) and
search with the tag name.
E.g.:
You may need to have a category such as 'Head of State' to quickly identify
contacts that occupies the functions 'President', 'Prime minister' or 'King'.
For protocol reasons, you may want to have 'Kings' sorted first.
Contributors
------------
* El Hadji Dem (elhadji.dem@savoirfairelinux.com)
* Sandy Carter (sandy.carter@savoirfairelinux.com)
""",
'depends': [
'base_contact',
],
'data': [
'res_partner_function_view.xml',
'res_partner_category_view.xml',
'res_partner_view.xml',
'security/ir.model.access.csv',
],
'installable': True,
}

265
base_contact_function/i18n/base_contact_function.pot

@ -0,0 +1,265 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * base_contact_function
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 7.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-10-31 16:24+0000\n"
"PO-Revision-Date: 2014-10-31 16:24+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: base_contact_function
#: field:res.partner.category.function,category_id:0
msgid "Category"
msgstr ""
#. module: base_contact_function
#: view:res.partner:0
msgid "City"
msgstr ""
#. module: base_contact_function
#: model:ir.actions.act_window,name:base_contact_function.action_function_tree_list
#: view:res.partner:0
#: field:res.partner,function_ids:0
#: view:res.partner.category:0
#: field:res.partner.category,category_function_ids:0
#: field:res.partner.category.function,function_id:0
#: view:res.partner.function:0
#: field:res.partner.function,partner_ids:0
msgid "Functions"
msgstr ""
#. module: base_contact_function
#: model:ir.model,name:base_contact_function.model_res_partner_category_function
msgid "Partner Category Function"
msgstr ""
#. module: base_contact_function
#: model:ir.actions.act_window,name:base_contact_function.action_partner_contact_form
msgid "Contacts"
msgstr ""
#. module: base_contact_function
#: view:res.partner:0
msgid "Street..."
msgstr ""
#. module: base_contact_function
#: view:res.partner:0
msgid "State"
msgstr ""
#. module: base_contact_function
#: view:res.partner:0
msgid "e.g. Sales Director"
msgstr ""
#. module: base_contact_function
#: model:ir.model,name:base_contact_function.model_res_partner_function
msgid "Partner Function"
msgstr ""
#. module: base_contact_function
#: view:res.partner:0
msgid "Tags..."
msgstr ""
#. module: base_contact_function
#: field:res.partner,start_date:0
msgid "Start date"
msgstr ""
#. module: base_contact_function
#: view:res.partner:0
msgid "Parent Organism"
msgstr ""
#. module: base_contact_function
#: view:res.partner:0
msgid "Fax:"
msgstr ""
#. module: base_contact_function
#: view:res.partner:0
#: view:res.partner.category:0
#: view:res.partner.function:0
msgid "Function"
msgstr ""
#. module: base_contact_function
#: view:res.partner:0
msgid "Fax"
msgstr ""
#. module: base_contact_function
#: field:res.partner.function,acronym:0
msgid "Acronym"
msgstr ""
#. module: base_contact_function
#: field:res.partner,other_contact_history_ids:0
msgid "unknown"
msgstr ""
#. module: base_contact_function
#: model:ir.actions.act_window,name:base_contact_function.action_partner_customer_form
msgid "Company"
msgstr ""
#. module: base_contact_function
#: view:res.partner:0
msgid "History (Functions)"
msgstr ""
#. module: base_contact_function
#: view:res.partner:0
msgid "Tag"
msgstr ""
#. module: base_contact_function
#: view:res.partner:0
msgid "Address"
msgstr ""
#. module: base_contact_function
#: view:res.partner:0
msgid "Use Organisation Address"
msgstr ""
#. module: base_contact_function
#: view:res.partner:0
msgid "have the same form for contact and other postions"
msgstr ""
#. module: base_contact_function
#: view:res.partner:0
msgid "Phone:"
msgstr ""
#. module: base_contact_function
#: field:res.partner.function,name:0
msgid "Name"
msgstr ""
#. module: base_contact_function
#: view:res.partner:0
msgid "ZIP"
msgstr ""
#. module: base_contact_function
#: view:res.partner:0
msgid "Country"
msgstr ""
#. module: base_contact_function
#: view:res.partner:0
msgid "Select Functions for this Organisation"
msgstr ""
#. module: base_contact_function
#: view:res.partner:0
msgid "Is an Organism"
msgstr ""
#. module: base_contact_function
#: field:res.partner,function_id:0
msgid "Position Occupied"
msgstr ""
#. module: base_contact_function
#: view:res.partner:0
msgid "Contact"
msgstr ""
#. module: base_contact_function
#: view:res.partner:0
msgid "Parent Organisation"
msgstr ""
#. module: base_contact_function
#: view:res.partner:0
msgid "Mobile:"
msgstr ""
#. module: base_contact_function
#: model:ir.model,name:base_contact_function.model_res_partner_category
msgid "Partner Categories"
msgstr ""
#. module: base_contact_function
#: view:res.partner:0
msgid "Organism"
msgstr ""
#. module: base_contact_function
#: help:res.partner,naming:0
msgid "Naming."
msgstr ""
#. module: base_contact_function
#: view:res.partner:0
msgid "History"
msgstr ""
#. module: base_contact_function
#: field:res.partner,end_date:0
msgid "End date"
msgstr ""
#. module: base_contact_function
#: view:res.partner.category:0
msgid "Add function"
msgstr ""
#. module: base_contact_function
#: view:res.partner:0
msgid "Bank Details"
msgstr ""
#. module: base_contact_function
#: field:res.partner.category.function,sequence:0
msgid "Sequence"
msgstr ""
#. module: base_contact_function
#: view:res.partner:0
msgid "Bank Accounts"
msgstr ""
#. module: base_contact_function
#: view:res.partner:0
msgid "No, Street, Apartment/Office"
msgstr ""
#. module: base_contact_function
#: help:res.partner.category.function,sequence:0
msgid "Used to order"
msgstr ""
#. module: base_contact_function
#: help:res.partner.function,acronym:0
msgid "Acronym of function."
msgstr ""
#. module: base_contact_function
#: help:res.partner.function,name:0
msgid "Name of function."
msgstr ""
#. module: base_contact_function
#: model:ir.model,name:base_contact_function.model_res_partner
msgid "Partner"
msgstr ""
#. module: base_contact_function
#: field:res.partner,naming:0
msgid "Naming"
msgstr ""

288
base_contact_function/i18n/fr.po

@ -0,0 +1,288 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * base_contact_function
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: partner-contact (7.0)\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-09-30 13:27+0000\n"
"PO-Revision-Date: 2015-05-22 13:07+0000\n"
"Last-Translator: Maxime Chambreuil <maxime.chambreuil@gmail.com>\n"
"Language-Team: French (http://www.transifex.com/oca/OCA-partner-contact-7-0/language/fr/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: fr\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#. module: base_contact_function
#: field:res.partner.category.function,category_id:0
msgid "Category"
msgstr "Catégorie"
#. module: base_contact_function
#: view:res.partner:0
msgid "City"
msgstr "Ville"
#. module: base_contact_function
#: model:ir.actions.act_window,name:base_contact_function.action_function_tree_list
#: view:res.partner:0 field:res.partner,function_ids:0
#: view:res.partner.category:0
#: field:res.partner.category,category_function_ids:0
#: field:res.partner.category.function,function_id:0
#: view:res.partner.function:0 field:res.partner.function,partner_ids:0
msgid "Functions"
msgstr "Fonctions"
#. module: base_contact_function
#: model:ir.model,name:base_contact_function.model_res_partner_category_function
msgid "Partner Category Function"
msgstr "Fonction de la catégorie du partenaire"
#. module: base_contact_function
#: model:ir.actions.act_window,name:base_contact_function.action_partner_contact_form
msgid "Contacts"
msgstr "Contacts"
#. module: base_contact_function
#: view:res.partner:0
msgid "Street..."
msgstr "Rue..."
#. module: base_contact_function
#: view:res.partner:0
msgid "State"
msgstr "Etat"
#. module: base_contact_function
#: view:res.partner:0
msgid "e.g. Sales Director"
msgstr "ex: Directeur de vente"
#. module: base_contact_function
#: model:ir.model,name:base_contact_function.model_res_partner_function
msgid "Partner Function"
msgstr "Fonction du partenaire"
#. module: base_contact_function
#: view:res.partner:0
msgid "Tags..."
msgstr "Etiquettes..."
#. module: base_contact_function
#: field:res.partner,start_date:0
msgid "Start date"
msgstr "Date de début"
#. module: base_contact_function
#: view:res.partner:0
msgid "Parent Organism"
msgstr "Organisme parent"
#. module: base_contact_function
#: view:res.partner:0
msgid "Fax:"
msgstr "Télécopie :"
#. module: base_contact_function
#: view:res.partner:0 view:res.partner.category:0 view:res.partner.function:0
msgid "Function"
msgstr "Fonction"
#. module: base_contact_function
#: view:res.partner:0
msgid "Fax"
msgstr "Télécopie"
#. module: base_contact_function
#: field:res.partner.function,acronym:0
msgid "Acronym"
msgstr "Acronyme"
#. module: base_contact_function
#: field:res.partner,other_contact_history_ids:0
msgid "unknown"
msgstr "inconnu"
#. module: base_contact_function
#: model:ir.actions.act_window,name:base_contact_function.action_partner_customer_form
msgid "Company"
msgstr "Organisme"
#. module: base_contact_function
#: view:res.partner:0
msgid "History (Functions)"
msgstr "Historique (Fonctions)"
#. module: base_contact_function
#: view:res.partner:0
msgid "Tag"
msgstr "Etiquette"
#. module: base_contact_function
#: view:res.partner:0
msgid "Address"
msgstr "Adresse"
#. module: base_contact_function
#: view:res.partner:0
msgid "Use Organisation Address"
msgstr "Utiliser l'adresse de l'organisme"
#. module: base_contact_function
#: view:res.partner:0
msgid "have the same form for contact and other postions"
msgstr "Avoir le même formulaire de contact et d'autres postions"
#. module: base_contact_function
#: view:res.partner:0
msgid "Phone:"
msgstr "Téléphone:"
#. module: base_contact_function
#: field:res.partner.function,name:0
msgid "Name"
msgstr "Nom"
#. module: base_contact_function
#: view:res.partner:0
msgid "ZIP"
msgstr "Code postal"
#. module: base_contact_function
#: view:res.partner:0
msgid "Country"
msgstr "Pays"
#. module: base_contact_function
#: view:res.partner:0
msgid "Select Functions for this Organisation"
msgstr "Sélectionner les fonctions de cet organisme"
#. module: base_contact_function
#: model:ir.actions.act_window,name:base_contact_function.partner_category_action
#: view:res.partner.category:0
msgid "List"
msgstr ""
#. module: base_contact_function
#: view:res.partner:0
msgid "Is an Organism"
msgstr "Est un Organisme"
#. module: base_contact_function
#: field:res.partner,function_id:0
msgid "Position Occupied"
msgstr "Fonction Occupée"
#. module: base_contact_function
#: view:res.partner:0
msgid "Contact"
msgstr "Contact"
#. module: base_contact_function
#: view:res.partner:0
msgid "Parent Organisation"
msgstr "Organisme parent"
#. module: base_contact_function
#: view:res.partner:0
msgid "Mobile:"
msgstr "Portable :"
#. module: base_contact_function
#: model:ir.model,name:base_contact_function.model_res_partner_category
msgid "Partner Categories"
msgstr "Catégories de partenaires"
#. module: base_contact_function
#: view:res.partner:0
msgid "Organism"
msgstr "Organisme"
#. module: base_contact_function
#: help:res.partner,naming:0
msgid "Naming."
msgstr "Appellation"
#. module: base_contact_function
#: view:res.partner:0
msgid "History"
msgstr "Historique"
#. module: base_contact_function
#: field:res.partner,end_date:0
msgid "End date"
msgstr "Date de fin"
#. module: base_contact_function
#: view:res.partner.category:0
msgid "Add function"
msgstr "Ajouter fonction"
#. module: base_contact_function
#: view:res.partner:0
msgid "Bank Details"
msgstr "Coordonnées bancaires"
#. module: base_contact_function
#: field:res.partner.category.function,sequence:0
msgid "Sequence"
msgstr "Séquence"
#. module: base_contact_function
#: view:res.partner:0
msgid "Bank Accounts"
msgstr "Comptes bancaires"
#. module: base_contact_function
#: view:res.partner:0
msgid "No, Street, Apartment/Office"
msgstr "No, Rue, Appartement/Bureau"
#. module: base_contact_function
#: model:ir.ui.menu,name:base_contact_function.menu_partner_category
msgid "Category Lists Tree"
msgstr ""
#. module: base_contact_function
#: help:res.partner.category.function,sequence:0
msgid "Used to order"
msgstr "Permet d'ordonner"
#. module: base_contact_function
#: help:res.partner.function,acronym:0
msgid "Acronym of function."
msgstr "Acronyme de la fonction."
#. module: base_contact_function
#: help:res.partner.function,name:0
msgid "Name of function."
msgstr "Nom de la fonction."
#. module: base_contact_function
#: model:ir.model,name:base_contact_function.model_res_partner
msgid "Partner"
msgstr "Partenaire"
#. module: base_contact_function
#: field:res.partner,naming:0
msgid "Naming"
msgstr "Appellation"
#. module: base_contact_function
#: model:ir.ui.menu,name:base_contact_function.menu_partner_category_list_form
msgid "Category Lists"
msgstr ""
#. module: base_contact_function
#: model:ir.actions.act_window,help:base_contact_function.partner_category_action
msgid ""
"<p>\n"
" Here is a list of all your partners classified by category.\n"
" </p>\n"
" "
msgstr ""

97
base_contact_function/res_partner.py

@ -0,0 +1,97 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2013 Savoir-faire Linux
# (<http://www.savoirfairelinux.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import time
from openerp.osv import orm, fields
class res_partner(orm.Model):
"""
Inherits partner and adds function_ids : List of functions
"""
_inherit = 'res.partner'
def onchange_partner_function(self, cr, uid, ids, part, context=None):
partner_pool = self.pool['res.partner']
if not part:
return {'value': None}
partner_ids = partner_pool.search(cr, uid, [('id', '=', part)])
function_ids = partner_pool.browse(
cr, uid, partner_ids, context=context)[0].function_ids
result = []
if function_ids:
for line in function_ids:
result.append(line.id)
dom = {'function_id': [('id', 'in', result)]}
return {'domain': dom}
def _get_history_lines(self, cr, uid, ids, name, arg, context=None):
res = {}
res_partner_pool = self.pool.get('res.partner')
for record in self.browse(cr, uid, ids, context=context):
if record.is_company:
return res
contact_ids = res_partner_pool.search(
cr, uid, [
('contact_id', '=', record.id),
('active', '=', False),
], context=context
)
res[record.id] = [
x.id for x in res_partner_pool.browse(cr, uid, contact_ids)
if x.end_date and x.end_date <= time.strftime('%Y-%m-%d')
]
return res
_columns = {
'function_ids': fields.many2many(
'res.partner.function',
'function_partner_rel',
'partner_id',
'function_id',
'Functions',
),
'start_date': fields.date('Start date'),
'end_date': fields.date('End date'),
'naming': fields.char(
'Naming',
help="Naming.",
),
'function_id': fields.many2one(
'res.partner.function',
'Position Occupied',
),
'other_contact_history_ids': fields.function(
_get_history_lines,
relation="res.partner",
method=True,
type="one2many",
),
# Replace company by Organisation
'use_parent_address': fields.boolean(
'Use Organisation Address',
help="Select this if you want to set organisation's address "
"information for this contact",
),
}

59
base_contact_function/res_partner_category.py

@ -0,0 +1,59 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2013 Savoir-faire Linux
# (<http://www.savoirfairelinux.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import orm, fields
class res_partner_category(orm.Model):
"""
Inherits partner_category
"""
_inherit = 'res.partner.category'
_columns = {
'category_function_ids': fields.one2many(
'res.partner.category.function',
'category_id',
'Functions'
),
}
def name_search(self, cr, user, name='', args=None, operator='ilike',
context=None, limit=100):
if not args:
args = []
if not context:
context = {}
if name:
name = name.split(' / ')[-1]
ids = self.search(cr, user, [('name', operator, name)] + args,
limit=limit, context=context)
if ids:
child_ids = self.search(
cr, user, [('parent_id', 'child_of', ids)],
limit=limit, context=context)
if child_ids:
ids.extend(child_ids)
# Remove duplicates and respect limit
ids = list(set(ids))[:limit]
else:
ids = self.search(cr, user, args, limit=limit, context=context)
return self.name_get(cr, user, ids, context)

47
base_contact_function/res_partner_category_function.py

@ -0,0 +1,47 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2013 Savoir-faire Linux
# (<http://www.savoirfairelinux.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import orm, fields
class res_partner_category_function(orm.Model):
"""
Adds this class to link category and functions
"""
_description = 'Partner Category Function'
_name = 'res.partner.category.function'
_order = 'sequence asc'
_columns = {
'function_id': fields.many2one(
'res.partner.function',
'Functions',
required='True',
),
'sequence': fields.integer(
'Sequence',
help="Used to order",
),
'category_id': fields.many2one(
'res.partner.category',
'Category',
),
}

54
base_contact_function/res_partner_category_view.xml

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_partner_category_form_admin" model="ir.ui.view">
<field name="name">List Form</field>
<field name="model">res.partner.category</field>
<field name="arch" type="xml">
<tree string="List">
<field name="complete_name"/>
</tree>
</field>
</record>
<record id="view_partner_category_list_admin" model="ir.ui.view">
<field name="name">List Tree</field>
<field name="model">res.partner.category</field>
<field name="field_parent">child_ids</field>
<field name="arch" type="xml">
<tree string="List">
<field name="name"/>
</tree>
</field>
</record>
<record id="partner_category_action" model="ir.actions.act_window">
<field name="name">List</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">res.partner.category</field>
<field name="domain">[('parent_id','=',False)]</field>
<field name="view_type">tree</field>
<field name="view_mode">tree</field>
<field name="view_id" ref="view_partner_category_list_admin"/>
<field name="help" type="html">
<p>
Here is a list of all your partners classified by category.
</p>
</field>
</record>
<menuitem id="menu_partner_category_list_form"
name="Category Lists"
parent="mail.mail_my_stuff"
sequence="30"
action="base.action_partner_category_form"/>
<menuitem id="menu_partner_category"
name="Category Lists Tree"
action="partner_category_action"
parent="mail.mail_my_stuff"
sequence="40" />
</data>
</openerp>

51
base_contact_function/res_partner_function.py

@ -0,0 +1,51 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2013 Savoir-faire Linux
# (<http://www.savoirfairelinux.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import fields, orm
class res_partner_function(orm.Model):
_description = 'Partner Function'
_name = 'res.partner.function'
_columns = {
'name': fields.char(
'Name',
required=True,
select=True,
translate=True,
help='Name of function.',
),
'acronym': fields.char(
'Acronym',
size=50,
select=True,
translate=True,
help='Acronym of function.',
),
'partner_ids': fields.many2many(
'res.partner',
'function_partner_rel',
'function_id',
'partner_id',
'Functions',
),
}

43
base_contact_function/res_partner_function_view.xml

@ -0,0 +1,43 @@
<?xml version="1.0" ?>
<openerp>
<data>
<!-- Tree Views functions-->
<record id="function_tree_view" model="ir.ui.view">
<field name="name">Function Tree View</field>
<field name="model">res.partner.function</field>
<field name="arch" type="xml">
<tree string="Functions" version="7.0">
<field name="name"/>
<field name="acronym"/>
</tree>
</field>
</record>
<!-- Form Views functions-->
<record id="function_form_view" model="ir.ui.view">
<field name="name">Function Form View</field>
<field name="model">res.partner.function</field>
<field name="arch" type="xml">
<form string="Function" version="7.0" >
<group col="4">
<field name="name"/>
<field name="acronym"/>
</group>
</form>
</field>
</record>
<!-- Actions -->
<record id="action_function_tree_list" model="ir.actions.act_window">
<field name="name">Functions</field>
<field name="view_id" ref="function_tree_view"/>
<field name="res_model">res.partner.function</field>
<field name="view_mode">tree,form</field>
</record>
</data>
</openerp>

527
base_contact_function/res_partner_view.xml

@ -0,0 +1,527 @@
<?xml version = "1.0" encoding="utf-8"?>
<openerp>
<data>
<!--Add list of functions in partner view-->
<record id="view_contact_by_function_form" model="ir.ui.view">
<field name="name">contact.functions.form.inherit</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<field name="use_parent_address" position="replace">
<field name="use_parent_address"
class="oe_edit_only oe_inline"
on_change="onchange_address(use_parent_address, parent_id)"
attrs="{'invisible': [('parent_id','=', False),('use_parent_address','=',False)]}"
string="Use Organisation Address"/>
</field>
<label for="use_parent_address" position="replace">
<label for="use_parent_address"
class="oe_edit_only"
attrs="{'invisible': [('parent_id','=', False),('use_parent_address','=',False)]}"
string="Use Organisation Address"/>
</label>
<!--Add ')' at the end of 'Is an Organism?'-->
<label for="is_company" position="replace">
<label for="is_company" string="Is an Organism"/>)
</label>
<!--Replace Street placeholder by No, Street, Apartment/Office-->
<field name="street" position="attributes">
<attribute name="placeholder">No, Street, Apartment/Office</attribute>
</field>
<field name="fax" position="attributes">
<attribute name="string">Fax</attribute>
</field>
<notebook position="inside">
<page string="Functions"
attrs="{'invisible': [('is_company','=',False), ('child_ids', '=', [])]}"
autofocus="autofocus">
<separator string="Select Functions for this Organisation"/>
<field name="function_ids"/>
</page>
<page string="History (Functions)" attrs="{'invisible': ['|','|',('is_company','=',True),('contact_id','!=',False),('other_contact_history_ids','=',[])]}">
<separator string="History"/>
<field name="other_contact_history_ids" mode="kanban"
attrs="{'invisible': [('other_contact_history_ids','=',False)]}">
<kanban>
<field name="color"/>
<field name="name"/>
<field name="title"/>
<field name="email"/>
<field name="parent_id" string="Organism"/>
<field name="is_company"/>
<field name="function_id" string="Function"/>
<field name="phone"/>
<field name="street"/>
<field name="street2"/>
<field name="zip"/>
<field name="city"/>
<field name="country_id"/>
<field name="mobile"/>
<field name="fax"/>
<field name="state_id"/>
<field name="has_image"/>
<templates>
<t t-name="kanban-box">
<div class="oe_kanban_vignette oe_semantic_html_override">
<a type="open">
<t t-if="record.has_image.raw_value === true">
<img t-att-src="kanban_image('res.partner', 'image_small', record.id.value)"
class="oe_kanban_image"/>
</t>
<t t-if="record.has_image.raw_value === false">
<t t-if="record.is_company.raw_value === true">
<img t-att-src='_s + "/base/static/src/img/company_image.png"'
class="oe_kanban_image"/>
</t>
<t t-if="record.is_company.raw_value === false">
<img t-att-src='_s + "/base/static/src/img/avatar.png"'
class="oe_kanban_image"/>
</t>
</t>
</a>
<div class="oe_kanban_details">
<h4 class="oe_partner_heading">
<a type="open">
<field name="name"/>
</a>
</h4>
<div class="oe_kanban_partner_categories"/>
<div class="oe_kanban_partner_links"/>
<ul>
<li t-if="!record.parent_id.raw_value and record.function_id.raw_value">
<field name="function_id"/>
</li>
<li t-if="record.parent_id.raw_value and record.function_id.raw_value">
<field name="function_id"/>
,
<field name="parent_id"/>
</li>
<li t-if="record.city.raw_value and !record.country.raw_value">
<field name="city"/>
</li>
<li t-if="!record.city.raw_value and record.country.raw_value">
<field name="country"/>
</li>
<li t-if="record.city.raw_value and record.country.raw_value">
<field name="city"/>
,
<field name="country"/>
</li>
<li t-if="record.email.raw_value">
<a t-attf-href="mailto:#{record.email.raw_value}">
<field name="email"/>
</a>
</li>
</ul>
</div>
</div>
</t>
</templates>
</kanban>
<form string="Contact" version="7.0">
<sheet>
<field name="image" widget='image' class="oe_avatar oe_left"
options='{"preview_image": "image_medium"}'/>
<div class="oe_title">
<label for="name" class="oe_edit_only"/>
<h1>
<field name="name" style="width: 70%%"/>
</h1>
</div>
<group>
<!-- inherited part -->
<field name="category_id" widget="many2many_tags"
placeholder="Tags..." style="width: 70%%" string="Tag"/>
<field name="parent_id"
placeholder="Organism"
domain="[('is_company','=',True)]"
string="Parent Organism"/>
<!-- inherited part end -->
<field name="function_id" placeholder="e.g. Sales Director"/>
<group colspan="4">
<field name="start_date" />
<field name="end_date"/>
<field name="naming"/>
</group>
<field name="email"/>
<field name="phone"/>
<field name="mobile"/>
</group>
<div>
<field name="use_parent_address"/>
<label for="use_parent_address" string="Use Organisation Address"/>
</div>
<group>
<label for="type"/>
<div name="div_type">
<field class="oe_inline" name="type"/>
</div>
<label for="street" string="Address"
attrs="{'invisible': [('use_parent_address','=', True)]}"/>
<div attrs="{'invisible': [('use_parent_address','=', True)]}"
name="div_address">
<field name="street" placeholder="Street..."/>
<field name="street2"/>
<div class="address_format">
<field name="city" placeholder="City" style="width: 40%%"/>
<field name="state_id" class="oe_no_button"
placeholder="State" style="width: 37%%"
options='{"no_open": True}' on_change="onchange_state(state_id)"/>
<field name="zip" placeholder="ZIP" style="width: 20%%"/>
</div>
<field name="country_id" placeholder="Country"
class="oe_no_button" options='{"no_open": True}'/>
</div>
</group>
<field name="supplier" invisible="True"/>
<group string="Bank Accounts">
<field name="bank_ids" nolabel="1">
<tree string="Bank Details">
<field name="state" invisible="1"/>
<field name="sequence" invisible="1"/>
<field name="acc_number"/>
<field name="bank_name"/>
<field name="owner_name"/>
</tree>
</field>
</group>
</sheet>
</form>
</field>
</page>
</notebook>
<!--Replace function by function_id defined by Organisation-->
<field name="function" position="replace"/>
</field>
</record>
<!--Add start_date, end_date, naming and account bank in partner view-->
<record id="view_position_info_form" model="ir.ui.view">
<field name="name">position.info.form.inherit</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base_contact.view_partner_form_inherit"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='other_contact_ids']/form//field[@name='category_id']"
position="attributes">
<attribute name="string">Tag</attribute>
</xpath>
<xpath expr="//field[@name='other_contact_ids']/form//field[@name='function']"
position="after">
<group colspan="4">
<field name="start_date" />
<field name="end_date"/>
<field name="naming"/>
</group>
</xpath>
<xpath expr="//field[@name='other_contact_ids']/form//field[@name='supplier']"
position="after">
<group string="Bank Accounts">
<field name="bank_ids" nolabel="1"/>
</group>
</xpath>
<xpath expr="//field[@name='other_contact_ids']/form//field[@name='parent_id']"
position="replace">
<group colspan="4">
<field name="parent_id" placeholder="Organism"
domain="[('is_company','=',True)]"
on_change="onchange_partner_function(parent_id)"
string="Parent Organism"/>
</group>
</xpath>
<xpath expr="//field[@name='other_contact_ids']/form//field[@name='function']"
position="replace">
<group colspan="4">
<field name="function_id" string="Function"/>
</group>
</xpath>
<xpath expr="//field[@name='other_contact_ids']/form//label[@for='use_parent_address']"
position="replace">
<label for="use_parent_address"/>
</xpath>
<xpath expr="//field[@name='other_contact_ids']/kanban//field[@name='function']"
position="replace">
<group colspan="4">
<field name="function_id" string="Function"/>
</group>
</xpath>
<xpath expr="//field[@name='other_contact_ids']/kanban//field[@name='has_image']"
position="after">
<templates>
<t t-name="kanban-box">
<t t-set="color" t-value="kanban_color(record.color.raw_value)"/>
<div t-att-class="color + (record.title.raw_value == 1 ? ' oe_kanban_color_alert' : '')"
style="position: relative">
<a t-if="! read_only_mode" type="delete"
style="position: absolute; right: 0; padding: 4px; diplay: inline-block">
X
</a>
<div class="oe_module_vignette">
<a type="open">
<t t-if="record.has_image.raw_value === true">
<img t-att-src="kanban_image('res.partner', 'image',
record.id.value, {'preview_image': 'image_small'})"
class="oe_avatar oe_kanban_avatar_smallbox"/>
</t>
<t t-if="record.image and record.image.raw_value !== false">
<img t-att-src="'data:image/png;base64,'+record.image.raw_value"
class="oe_avatar oe_kanban_avatar_smallbox"/>
</t>
<t t-if="record.has_image.raw_value === false
and (!record.image or record.image.raw_value === false)">
<t t-if="record.is_company.raw_value === true">
<img t-att-src='_s + "/base/static/src/img/company_image.png"'
class="oe_kanban_image oe_kanban_avatar_smallbox"/>
</t>
<t t-if="record.is_company.raw_value === false">
<img t-att-src='_s + "/base/static/src/img/avatar.png"'
class="oe_kanban_image oe_kanban_avatar_smallbox"/>
</t>
</t>
</a>
<div class="oe_module_desc">
<div class="oe_kanban_box_content oe_kanban_color_bglight oe_kanban_color_border">
<table class="oe_kanban_table">
<tr>
<td class="oe_kanban_title1" align="left" valign="middle">
<h4>
<a type="open">
<field name="name"/>
</a>
</h4>
<i>
<t t-if="record.parent_id.raw_value and !record.function_id.raw_value">
<field name="parent_id"/>
</t>
<t t-if="!record.parent_id.raw_value and record.function_id.raw_value">
<field name="function_id"/>
</t>
<t t-if="record.parent_id.raw_value and record.function_id.raw_value">
<field name="function_id"/>
,
<field name="parent_id"/>
</t>
</i>
<div>
<a t-if="record.email.raw_value" title="Mail"
t-att-href="'mailto:'+record.email.value">
<field name="email"/>
</a>
</div>
<div t-if="record.phone.raw_value">
Phone: <field name="phone"/>
</div>
<div t-if="record.mobile.raw_value">
Mobile: <field name="mobile"/>
</div>
<div t-if="record.fax.raw_value">
Fax: <field name="fax"/>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</t>
</templates>
</xpath>
have the same form for contact and other postions
<field name="parent_id" position="replace">
<field name="parent_id" placeholder="Parent Organisation"
domain="[('is_company','=',True)]"
attrs="{'invisible': [('is_company','=', False)]}"/>
</field>
<xpath expr="//field[@name='child_ids']/form//field[@name='supplier']"
position="after">
<group string="Bank Accounts">
<field name="bank_ids" nolabel="1"/>
</group>
</xpath>
<xpath expr="//field[@name='child_ids']/form//field[@name='function']"
position="after">
<group colspan="4">
<field name="start_date" />
<field name="end_date"/>
<field name="naming"/>
</group>
</xpath>
<xpath expr="//field[@name='child_ids']/form//field[@name='function']"
position="replace">
<group colspan="4">
<field name="parent_id"
placeholder="Organism"
domain="[('is_company','=',True)]"
string="Parent Organisation" />
<field name="function_id"
readonly="False"
options="{'create': false, 'create_edit': false}"
domain="[('id', 'in', parent.function_ids[0][2])]"
string="Function"/>
</group>
</xpath>
<xpath expr="//field[@name='child_ids']/form//label[@for='use_parent_address']"
position="replace">
<label for="use_parent_address"/>
</xpath>
<xpath expr="//field[@name='child_ids']/kanban//field[@name='function']"
position="replace">
<group colspan="4">
<field name="function_id" string="Function"/>
</group>
</xpath>
<xpath expr="//field[@name='other_contact_ids']/kanban//field[@name='has_image']"
position="after">
<templates>
<t t-name="kanban-box">
<div class="oe_kanban_vignette oe_semantic_html_override">
<a type="open">
<t t-if="record.has_image.raw_value === true">
<img t-att-src="kanban_image('res.partner', 'image_small', record.id.value)"
class="oe_kanban_image"/>
</t>
<t t-if="record.has_image.raw_value === false">
<t t-if="record.is_company.raw_value === true">
<img t-att-src='_s + "/base/static/src/img/company_image.png"'
class="oe_kanban_image"/>
</t>
<t t-if="record.is_company.raw_value === false">
<img t-att-src='_s + "/base/static/src/img/avatar.png"'
class="oe_kanban_image"/>
</t>
</t>
</a>
<div class="oe_kanban_details">
<h4 class="oe_partner_heading">
<a type="open">
<field name="name"/>
</a>
</h4>
<div class="oe_kanban_partner_categories"/>
<div class="oe_kanban_partner_links"/>
<ul>
<li t-if="record.parent_id.raw_value and !record.function_id.raw_value">
<field name="parent_id"/>
</li>
<li t-if="!record.parent_id.raw_value and record.function_id.raw_value">
<field name="function_id"/>
</li>
<li t-if="record.parent_id.raw_value and record.function_id.raw_value">
<field name="function_id"/>
,
<field name="parent_id"/>
</li>
<li t-if="record.city.raw_value and !record.country.raw_value">
<field name="city"/>
</li>
<li t-if="!record.city.raw_value and record.country.raw_value">
<field name="country"/>
</li>
<li t-if="record.city.raw_value and record.country.raw_value">
<field name="city"/>
,
<field name="country"/>
</li>
<li t-if="record.email.raw_value">
<a t-attf-href="mailto:#{record.email.raw_value}">
<field name="email"/>
</a>
</li>
</ul>
</div>
</div>
</t>
</templates>
</xpath>
</field>
</record>
<!-- Actions for Organisation -->
<record id="action_partner_customer_form" model="ir.actions.act_window">
<field name="name">Company</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">res.partner</field>
<field name="view_type">form</field>
<field name="view_mode">kanban,tree,form</field>
<field name="domain">[('is_company','=',1)]</field>
<field name="context" eval="{'default_is_company': True}"/>
<field name="filter" eval="True"/>
</record>
<!-- Actions for Contacts -->
<record id="action_partner_contact_form" model="ir.actions.act_window">
<field name="name">Contacts</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">res.partner</field>
<field name="view_type">form</field>
<field name="view_mode">kanban,tree,form</field>
<field name="domain">[('is_company','=',0)]</field>
<field name="filter" eval="True"/>
</record>
<!--Add country,street fields in tree partner view-->
<record model="ir.ui.view" id="view_partner_tree_country_address">
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_tree"/>
<field name="arch" type="xml">
<field name="country_id" position="replace"/>
<field name="name" position="after">
<field name="country_id"/>
</field>
<field name="email" position="after">
<field name="street" string="Address"/>
</field>
</field>
</record>
<!-- Inherit Categories adding functions -->
<record id="view_partner_category_functions_form" model="ir.ui.view">
<field name="name">Partner Categories Functions</field>
<field name="model">res.partner.category</field>
<field name="inherit_id" ref="base.view_partner_category_form"/>
<field name="arch" type="xml">
<xpath expr="//form[@string='Partner Category']/group[1]"
position="after">
<notebook position="inside">
<page string="Functions">
<separator string="Add function"/>
<group col="2" colspan="4">
<field name="category_function_ids" nolabel="1">
<tree string="Functions">
<field name="sequence"/>
<field name="function_id"/>
</tree>
<form string="Function">
<field name="function_id"/>
<field name="sequence"/>
</form>
</field>
</group>
</page>
</notebook>
</xpath>
</field>
</record>
<!--Replace company by Organisation-->
<record id="view_partner_simple_form" model="ir.ui.view">
<field name="name">res.partner.simplified.form</field>
<field name="model">res.partner</field>
<field name="arch" type="xml">
<label for="is_company" position="replace">
<label for="is_company" string="Is an Organism"/>
</label>
<field name="parent_id" position="replace">
<field name="parent_id"
placeholder="Organism"
domain="[('is_company', '=', True)]"
context="{'default_is_company': True, 'default_supplier': supplier}"
attrs="{'invisible': [('is_company','=', True),('parent_id', '=', False)]}"
on_change="onchange_address(use_parent_address, parent_id)"/>
</field>
</field>
</record>
</data>
</openerp>

3
base_contact_function/security/ir.model.access.csv

@ -0,0 +1,3 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"access_res_partner_function","access_res_partner_function_user","model_res_partner_function","base.group_user",1,1,1,1
"access_res_partner_category_function","access_res_partner_category_function_user","model_res_partner_category_function","base.group_user",1,1,1,1

31
base_contact_function/tests/__init__.py

@ -0,0 +1,31 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2013 Savoir-faire Linux
# (<http://www.savoirfairelinux.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import (
test_functions,
test_partner_category,
)
checks = [
test_functions,
test_partner_category
]

105
base_contact_function/tests/test_functions.py

@ -0,0 +1,105 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2013 Savoir-faire Linux
# (<http://www.savoirfairelinux.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.tests.common import TransactionCase
from openerp.osv.orm import browse_record
from openerp.tools import ustr
class Base_Test_function(TransactionCase):
"""
Simple test creating a function
This is a base class for function test cases.
Inherit from this and setup values.
"""
def setUp(self, vals=None):
"""
Setting up function.
"""
if not vals:
vals = {}
# Default test values
self.vals = {
'name': u'This is a test function name with uniödé',
'acronym': u'This is a test function acronym with uniödé',
}
super(Base_Test_function, self).setUp()
self.vals = dict(self.vals.items() + vals.items())
# Create the function object; we will be testing this, so store in self
function_pool = self.registry('res.partner.function')
self.function_id = function_pool.create(
self.cr, self.uid, self.vals, context=None
)
def test_function(self):
"""
Checking the function creation.
"""
function_function = self.registry('res.partner.function')
function_obj = function_function.browse(
self.cr, self.uid, self.function_id, context=None
)
for field in self.vals:
val = function_obj[field]
if type(val) == browse_record:
self.assertEquals(self.vals[field], val.id,
"IDs for %s don't match: (%i != %i)" %
(field, self.vals[field], val.id))
else:
self.assertEquals(ustr(self.vals[field]), ustr(val),
"Values for %s don't match: (%s != %s)" %
(field, ustr(self.vals[field]), ustr(val)))
class Test_function_bad(Base_Test_function):
"""
Simple test creating a function, test against bad values
"""
def setUp(self):
"""
Setting up function, then changing the values to test against.
"""
super(Test_function_bad, self).setUp()
# Change vals to something wrong
self.vals = {'name': 'This is the wrong function name',
'acronym': 'This is the wrong function acronym',
}
def test_function(self):
"""
Checking the function creation, assertions should all be false.
"""
function_function = self.registry('res.partner.function')
function_obj = function_function.browse(
self.cr, self.uid, self.function_id, context=None
)
for field in self.vals:
val = function_obj[field]
if type(val) == browse_record:
self.assertNotEqual(self.vals[field], val.id,
"IDs for %s don't match: (%i != %i)" %
(field, self.vals[field], val.id))
else:
self.assertNotEqual(ustr(self.vals[field]), ustr(val),
"Values for %s don't match: (%s != %s)" %
(field, ustr(self.vals[field]), ustr(val)))

193
base_contact_function/tests/test_partner_category.py

@ -0,0 +1,193 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2013 Savoir-faire Linux
# (<http://www.savoirfairelinux.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.tests.common import TransactionCase
from openerp.osv.orm import browse_record
from openerp.tools import ustr
class Base_Test_partner_category(TransactionCase):
"""
Simple test creating a partner_category
This is a base class for partner_category test cases.
Inherit from this and setup values.
"""
def setUp(self, vals=None):
"""
Setting up partner_category.
"""
if not vals:
vals = {}
# Default test values
self.vals = {
'name': 'This is a test partner_category name with unicödé',
'active': True,
}
super(Base_Test_partner_category, self).setUp()
# Create the parent
partner_category_partner = self.registry('res.partner.category')
self.vals['parent_id'] = partner_category_partner.create(
self.cr, self.uid, {
'name': 'Test parent',
'active': True,
}, context=None
)
self.vals = dict(self.vals.items() + vals.items())
# Create the partner_category object; we will be testing this,
# so store in self
self.partner_category_id = partner_category_partner.create(
self.cr, self.uid, self.vals, context=None
)
def test_partner_category(self):
"""
Checking the partner_category creation.
"""
partner_category = self.registry('res.partner.category')
partner_category_obj = partner_category.browse(
self.cr, self.uid, self.partner_category_id, context=None
)
for field in self.vals:
val = partner_category_obj[field]
if type(val) == browse_record:
self.assertEquals(self.vals[field], val.id,
"IDs for %s don't match: (%i != %i)" %
(field, self.vals[field], val.id))
else:
self.assertEquals(ustr(self.vals[field]), ustr(val),
"Values for %s don't match: (%s != %s)" %
(field, ustr(self.vals[field]), ustr(val)))
class Test_partner_category_bad(Base_Test_partner_category):
"""
Simple test creating a partner_category, test against bad values
"""
def setUp(self):
"""
Setting up partner_category, then changing the values to test against.
"""
super(Test_partner_category_bad, self).setUp()
# Change vals to something wrong
self.vals = {'name': 'This is the wrong partner_category name',
'active': False,
}
def test_partner_category(self):
"""
Checking the partner_category creation, assertions should all be false.
"""
partner_category_partner_category = self.registry(
'res.partner.category'
)
partner_category_obj = partner_category_partner_category.browse(
self.cr, self.uid, self.partner_category_id, context=None
)
for field in self.vals:
val = partner_category_obj[field]
if type(val) == browse_record:
self.assertNotEqual(self.vals[field], val.id,
"IDs for %s don't match: (%i != %i)" %
(field, self.vals[field], val.id))
else:
self.assertNotEqual(ustr(self.vals[field]), ustr(val),
"Values for %s don't match: (%s != %s)" %
(field, ustr(self.vals[field]), ustr(val)))
class test_partner_category(TransactionCase):
def setUp(self):
super(test_partner_category, self).setUp()
# Clean up registries
self.registry('ir.model').clear_caches()
self.registry('ir.model.data').clear_caches()
# Get registries
self.user_model = self.registry("res.users")
self.partner_cat_model = self.registry("res.partner.category")
# Get context
self.context = self.user_model.context_get(self.cr, self.uid)
# Create parent partner category
self.test_parent_id = self.partner_cat_model.create(
self.cr, self.uid, {
'name': 'test_parent',
}, context=self.context)
self.test_child_id = self.partner_cat_model.create(
self.cr, self.uid, {
'name': 'test_child',
'parent_id': self.test_parent_id,
}, context=self.context)
self.test_grandchild_id = self.partner_cat_model.create(
self.cr, self.uid, {
'name': 'test_grandchild',
'parent_id': self.test_child_id,
}, context=self.context)
self.test_orphan_id = self.partner_cat_model.create(
self.cr, self.uid, {
'name': 'test_orphan',
}, context=self.context)
def test_name_search(self):
self.assertItemsEqual(
self.partner_cat_model.name_search(
self.cr, self.uid, name='test_parent', context=self.context
),
[
(self.test_child_id, 'test_parent / test_child'),
(self.test_grandchild_id,
'test_parent / test_child / test_grandchild'),
(self.test_parent_id, 'test_parent'),
]
)
self.assertItemsEqual(
self.partner_cat_model.name_search(
self.cr, self.uid, name='test_child', context=self.context
),
[
(self.test_child_id, 'test_parent / test_child'),
(self.test_grandchild_id,
'test_parent / test_child / test_grandchild'),
]
)
self.assertItemsEqual(
self.partner_cat_model.name_search(
self.cr, self.uid, name='test_grandchild', context=self.context
),
[
(self.test_grandchild_id,
'test_parent / test_child / test_grandchild'),
]
)
self.assertItemsEqual(
self.partner_cat_model.name_search(
self.cr, self.uid, name='test_orphan', context=self.context
),
[
(self.test_orphan_id, 'test_orphan'),
]
)

23
base_contact_function_partner_firstname/__init__.py

@ -0,0 +1,23 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2013 Savoir-faire Linux
# (<http://www.savoirfairelinux.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import res_partner

51
base_contact_function_partner_firstname/__openerp__.py

@ -0,0 +1,51 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2013 Savoir-faire Linux
# (<http://www.savoirfairelinux.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'name': 'Contacts by Functions - Partner Firstname Bindings',
'version': '0.1',
'category': 'Customer Relationship Management',
'summary': 'Contacts by Functions - Partner Firstname Bindings',
'description': """
Contacts by Functions - Partner Firstname Bindings
==================================================
Contributors
------------
* El Hadji Dem (elhadji.dem@savoirfairelinux.com)
""",
'author': "Savoir-faire Linux,Odoo Community Association (OCA)",
'website': 'www.savoirfairelinux.com',
'license': 'AGPL-3',
'depends': [
'base_contact_function',
'partner_firstname',
],
'data': [
'res_partner_view.xml',
],
'test': [],
'demo': [
],
'installable': True,
'auto_install': True,
}

27
base_contact_function_partner_firstname/i18n/base_contact_function_partner_firstname.pot

@ -0,0 +1,27 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * base_contact_function_partner_firstname
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 7.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-01-03 07:16+0000\n"
"PO-Revision-Date: 2014-01-03 02:17-0500\n"
"Last-Translator: EL Hadji DEM <elhadji.dem@savoirfairelinux.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: \n"
"X-Generator: Poedit 1.5.4\n"
#. module: base_contact_function_partner_firstname
#: view:res.partner:0
msgid "lastname"
msgstr ""
#. module: base_contact_function_partner_firstname
#: view:res.partner:0
msgid "Firstname"
msgstr ""

27
base_contact_function_partner_firstname/i18n/fr.po

@ -0,0 +1,27 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * base_contact_function_partner_firstname
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 7.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-01-03 07:17+0000\n"
"PO-Revision-Date: 2014-01-03 02:18-0500\n"
"Last-Translator: EL Hadji DEM <elhadji.dem@savoirfairelinux.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: \n"
"X-Generator: Poedit 1.5.4\n"
#. module: base_contact_function_partner_firstname
#: view:res.partner:0
msgid "lastname"
msgstr "Nom"
#. module: base_contact_function_partner_firstname
#: view:res.partner:0
msgid "Firstname"
msgstr "Prénom"

79
base_contact_function_partner_firstname/res_partner.py

@ -0,0 +1,79 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2013 Savoir-faire Linux
# (<http://www.savoirfairelinux.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import time
from openerp.osv import orm
class res_partner(orm.Model):
_inherit = 'res.partner'
def create(self, cr, user, vals, context=None):
context = self._basecontact_check_context(cr, user, 'create', context)
if not vals.get('name') and vals.get('contact_id'):
vals['name'] = self.browse(
cr, user, vals['contact_id'], context=context).name
if vals.get('end_date'):
if vals.get('end_date', "Z") <= time.strftime('%Y-%m-%d'):
vals['active'] = False
if vals.get('contact_type') == 'standalone':
contact_vals = dict(
filter(lambda (k, v): k != 'parent_id', vals.iteritems())
)
contact_vals['active'] = True
contact_vals['function_id'] = None
vals['contact_id'] = super(res_partner, self).create(
cr, user, contact_vals, context=context
)
self.write(cr, user, vals['contact_id'], {
'firstname': vals.get('firstname', ''),
'lastname': vals.get('lastname', ''),
}, context=context)
# Check if we create existing contact from company(ie: company is true)
# Check if we create another function from contact view
if vals.get('contact_type') == 'attached' or (
not vals.get('contact_type') and vals.get('contact_id')):
contact_info = self.browse(
cr, user, vals.get('contact_id'), context=context)
vals['firstname'] = contact_info.firstname
vals['title'] = contact_info.title.id or ''
res = super(res_partner, self).create(cr, user, vals, context=context)
return res
def write(self, cr, user, ids, vals, context=None):
context = self._basecontact_check_context(cr, user, 'write', context)
if vals.get('end_date'):
if vals.get('end_date', "Z") <= time.strftime('%Y-%m-%d'):
vals['active'] = False
if vals.get('contact_type') == 'standalone':
contact_vals = dict(
filter(lambda (k, v): k != 'parent_id', vals.iteritems())
)
contact_vals['active'] = True
contact_vals['function_id'] = None
vals['contact_id'] = super(res_partner, self).write(
cr, user, contact_vals, context=context
)
return super(res_partner, self).write(
cr, user, ids, vals, context=context
)

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save