[MERGE] trunk-partner-address-cleaning-sbh

bzr revid: rco@openerp.com-20120309112101-ta5tkay052j7s9xe
This commit is contained in:
Raphael Collet
2012-03-09 12:21:01 +01:00
70 changed files with 30531 additions and 4380 deletions
+33 -9
View File
@@ -2,17 +2,19 @@
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXOPTS = -q
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = build
BUILDDIR = _build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
@@ -29,6 +31,9 @@ help:
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
@@ -43,6 +48,7 @@ html:
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
sed -i '/-99999/d' _build/dirhtml/_static/flasky.css
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
@@ -72,17 +78,17 @@ qthelp:
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/OpenERPWeb.qhcp"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/OpenERPTechnicalDocumentation.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/OpenERPWeb.qhc"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/OpenERPTechnicalDocumentation.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/OpenERPWeb"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/OpenERPWeb"
@echo "# mkdir -p $$HOME/.local/share/devhelp/OpenERPTechnicalDocumentation"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/OpenERPTechnicalDocumentation"
@echo "# devhelp"
epub:
@@ -100,7 +106,7 @@ latex:
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
make -C $(BUILDDIR)/latex all-pdf
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
@@ -113,6 +119,24 @@ man:
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

+16
View File
@@ -0,0 +1,16 @@
<p class="logo"><a href="http://doc.openerp.com/">
<img class="logo" src="{{ pathto('_static/openerp.png', 1) }}" alt="Logo"/>
</a></p>
<h3>Other Docs</h3>
<ul>
<li><a href="http://doc.openerp.com/trunk/developers">OpenERP Developers Documentation</a></li>
<li><a href="http://doc.openerp.com/trunk/developers/web">OpenERP Web Developers Documentation</a></li>
<li><a href="http://doc.openerp.com/trunk/users">OpenERP Users Documentation</a></li>
</ul>
<h3>Useful Links</h3>
<ul>
<li><a href="http://www.openerp.com/">The OpenERP website</a></li>
<li><a href="http://python.org/">The Python programming language</a></li>
</ul>
+3
View File
@@ -0,0 +1,3 @@
<p class="logo"><a href="{{ pathto(master_doc) }}">
<img class="logo" src="{{ pathto('_static/openerp.png', 1) }}" alt="Logo"/>
</a></p>
+3
View File
@@ -0,0 +1,3 @@
*.pyc
*.pyo
.DS_Store
+37
View File
@@ -0,0 +1,37 @@
Copyright (c) 2010 by Armin Ronacher.
Some rights reserved.
Redistribution and use in source and binary forms of the theme, with or
without modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* The names of the contributors may not be used to endorse or
promote products derived from this software without specific
prior written permission.
We kindly ask you to only use these themes in an unmodified manner just
for Flask and Flask-related products, not for unrelated projects. If you
like the visual style and want to use it for your own projects, please
consider making some larger changes to the themes (such as changing
font faces, sizes, colors or margins).
THIS THEME IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS THEME, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
+31
View File
@@ -0,0 +1,31 @@
Flask Sphinx Styles
===================
This repository contains sphinx styles for Flask and Flask related
projects. To use this style in your Sphinx documentation, follow
this guide:
1. put this folder as _themes into your docs folder. Alternatively
you can also use git submodules to check out the contents there.
2. add this to your conf.py:
sys.path.append(os.path.abspath('_themes'))
html_theme_path = ['_themes']
html_theme = 'flask'
The following themes exist:
- 'flask' - the standard flask documentation theme for large
projects
- 'flask_small' - small one-page theme. Intended to be used by
very small addon libraries for flask.
The following options exist for the flask_small theme:
[options]
index_logo = '' filename of a picture in _static
to be used as replacement for the
h1 in the index.rst file.
index_logo_height = 120px height of the index logo
github_fork = '' repository name on github for the
"fork me" badge
+25
View File
@@ -0,0 +1,25 @@
{%- extends "basic/layout.html" %}
{%- block extrahead %}
{{ super() }}
{% if theme_touch_icon %}
<link rel="apple-touch-icon" href="{{ pathto('_static/' ~ theme_touch_icon, 1) }}" />
{% endif %}
<link media="only screen and (max-device-width: 480px)" href="{{
pathto('_static/small_flask.css', 1) }}" type= "text/css" rel="stylesheet" />
{% endblock %}
{%- block relbar2 %}{% endblock %}
{% block header %}
{{ super() }}
{% if pagename == 'index' %}
<div class=indexwrapper>
{% endif %}
{% endblock %}
{%- block footer %}
<div class="footer">
&copy; Copyright {{ copyright }}
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> and a modified <a href="https://github.com/mitsuhiko/flask-sphinx-themes">Flask theme</a>.
</div>
{% if pagename == 'index' %}
</div>
{% endif %}
{%- endblock %}
+19
View File
@@ -0,0 +1,19 @@
<h3>Related Topics</h3>
<ul>
<li><a href="{{ pathto(master_doc) }}">Documentation overview</a><ul>
{%- for parent in parents %}
<li><a href="{{ parent.link|e }}">{{ parent.title }}</a><ul>
{%- endfor %}
{%- if prev %}
<li>Previous: <a href="{{ prev.link|e }}" title="{{ _('previous chapter')
}}">{{ prev.title }}</a></li>
{%- endif %}
{%- if next %}
<li>Next: <a href="{{ next.link|e }}" title="{{ _('next chapter')
}}">{{ next.title }}</a></li>
{%- endif %}
{%- for parent in parents %}
</ul></li>
{%- endfor %}
</ul></li>
</ul>
+395
View File
@@ -0,0 +1,395 @@
/*
* flasky.css_t
* ~~~~~~~~~~~~
*
* :copyright: Copyright 2010 by Armin Ronacher.
* :license: Flask Design License, see LICENSE for details.
*/
{% set page_width = '80em' %}
{% set sidebar_width = '16em' %}
@import url("basic.css");
/* -- page layout ----------------------------------------------------------- */
body {
font-family: 'Georgia', serif;
font-size: 15px;
background-color: white;
color: #000;
margin: 0;
padding: 0;
}
div.document {
width: {{ page_width }};
margin: 30px auto 0 auto;
}
div.documentwrapper {
float: left;
width: 100%;
}
div.bodywrapper {
margin: 0 0 0 {{ sidebar_width }};
}
div.sphinxsidebar {
width: {{ sidebar_width }};
}
hr {
border: 1px solid #B1B4B6;
}
div.body {
background-color: #ffffff;
color: #3E4349;
padding: 0 0px 0 0px;
}
img.floatingflask {
padding: 0 0 10px 10px;
float: right;
}
div.footer {
width: {{ page_width }};
margin: 20px auto 30px auto;
font-size: 12px;
color: #888;
text-align: right;
}
div.footer a {
color: #888;
}
div.related {
display: none;
}
div.sphinxsidebar a {
color: #444;
text-decoration: none;
border-bottom: 1px dotted #999;
}
div.sphinxsidebar a:hover {
border-bottom: 1px solid #999;
}
div.sphinxsidebar {
font-size: 12px;
line-height: 1.5;
}
div.sphinxsidebarwrapper {
padding: 0px 10px;
}
div.sphinxsidebarwrapper p.logo {
padding: 0 0 20px 0;
margin: 0;
text-align: center;
}
div.sphinxsidebar h3,
div.sphinxsidebar h4 {
font-family: 'Garamond', 'Georgia', serif;
color: #444;
font-size: 22px;
font-weight: normal;
margin: 0 0 5px 0;
padding: 0;
}
div.sphinxsidebar h4 {
font-size: 18px;
}
div.sphinxsidebar h3 a {
color: #444;
}
div.sphinxsidebar p.logo a,
div.sphinxsidebar h3 a,
div.sphinxsidebar p.logo a:hover,
div.sphinxsidebar h3 a:hover {
border: none;
}
div.sphinxsidebar p {
color: #555;
margin: 10px 0;
}
div.sphinxsidebar ul {
margin: 10px 0;
padding: 0;
color: #000;
}
div.sphinxsidebar input {
border: 1px solid #ccc;
font-family: 'Georgia', serif;
font-size: 1em;
}
/* -- body styles ----------------------------------------------------------- */
a {
color: #004B6B;
text-decoration: underline;
}
a:hover {
color: #6D4100;
text-decoration: underline;
}
div.body h1,
div.body h2,
div.body h3,
div.body h4,
div.body h5,
div.body h6 {
font-family: 'Garamond', 'Georgia', serif;
font-weight: normal;
margin: 30px 0px 10px 0px;
padding: 0;
}
{% if theme_index_logo %}
div.indexwrapper h1 {
text-indent: -999999px;
background: url({{ theme_index_logo }}) no-repeat center center;
height: {{ theme_index_logo_height }};
}
{% endif %}
div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; }
div.body h2 { font-size: 180%; }
div.body h3 { font-size: 150%; }
div.body h4 { font-size: 130%; }
div.body h5 { font-size: 100%; }
div.body h6 { font-size: 100%; }
a.headerlink {
color: #ddd;
padding: 0 4px;
text-decoration: none;
}
a.headerlink:hover {
color: #444;
background: #eaeaea;
}
div.body p, div.body dd, div.body li {
line-height: 1.4em;
}
div.admonition {
background: #fafafa;
margin: 20px -30px;
padding: 10px 30px;
border-top: 1px solid #ccc;
border-bottom: 1px solid #ccc;
}
div.admonition tt.xref, div.admonition a tt {
border-bottom: 1px solid #fafafa;
}
dd div.admonition {
margin-left: -60px;
padding-left: 60px;
}
div.admonition p.admonition-title {
font-family: 'Garamond', 'Georgia', serif;
font-weight: normal;
font-size: 22px;
margin: 0 0 10px 0;
padding: 0;
line-height: 1;
}
div.admonition p.last {
margin-bottom: 0;
}
div.highlight {
background-color: white;
}
dt:target, .highlight {
background: #FAF3E8;
}
div.note {
background-color: #eee;
border: 1px solid #ccc;
}
div.seealso {
background-color: #ffc;
border: 1px solid #ff6;
}
div.topic {
background-color: #eee;
}
p.admonition-title {
display: inline;
}
p.admonition-title:after {
content: ":";
}
pre, tt {
font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
font-size: 0.9em;
}
img.screenshot {
}
tt.descname, tt.descclassname {
font-size: 0.95em;
}
tt.descname {
padding-right: 0.08em;
}
img.screenshot {
-moz-box-shadow: 2px 2px 4px #eee;
-webkit-box-shadow: 2px 2px 4px #eee;
box-shadow: 2px 2px 4px #eee;
}
table.docutils {
border: 1px solid #888;
-moz-box-shadow: 2px 2px 4px #eee;
-webkit-box-shadow: 2px 2px 4px #eee;
box-shadow: 2px 2px 4px #eee;
}
table.docutils td, table.docutils th {
border: 1px solid #888;
padding: 0.25em 0.7em;
}
table.field-list, table.footnote {
border: none;
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
}
table.footnote {
margin: 15px 0;
width: 100%;
border: 1px solid #eee;
background: #fdfdfd;
font-size: 0.9em;
}
table.footnote + table.footnote {
margin-top: -15px;
border-top: none;
}
table.field-list th {
padding: 0 0.8em 0 0;
}
table.field-list td {
padding: 0;
}
table.footnote td.label {
width: 0px;
padding: 0.3em 0 0.3em 0.5em;
}
table.footnote td {
padding: 0.3em 0.5em;
}
dl {
margin: 0;
padding: 0;
}
dl dd {
margin-left: 30px;
}
blockquote {
margin: 0 0 0 30px;
padding: 0;
}
ul, ol {
margin: 10px 0 10px 30px;
padding: 0;
}
pre {
background: #eee;
padding: 7px 30px;
margin: 15px -30px;
line-height: 1.3em;
}
dl pre, blockquote pre, li pre {
margin-left: -60px;
padding-left: 60px;
}
dl dl pre {
margin-left: -90px;
padding-left: 90px;
}
tt {
background-color: #ecf0f3;
color: #222;
/* padding: 1px 2px; */
}
tt.xref, a tt {
background-color: #FBFBFB;
border-bottom: 1px solid white;
}
a.reference {
text-decoration: none;
border-bottom: 1px dotted #004B6B;
}
a.reference:hover {
border-bottom: 1px solid #6D4100;
}
a.footnote-reference {
text-decoration: none;
font-size: 0.7em;
vertical-align: top;
border-bottom: 1px dotted #004B6B;
}
a.footnote-reference:hover {
border-bottom: 1px solid #6D4100;
}
a:hover tt {
background: #EEE;
}
+70
View File
@@ -0,0 +1,70 @@
/*
* small_flask.css_t
* ~~~~~~~~~~~~~~~~~
*
* :copyright: Copyright 2010 by Armin Ronacher.
* :license: Flask Design License, see LICENSE for details.
*/
body {
margin: 0;
padding: 20px 30px;
}
div.documentwrapper {
float: none;
background: white;
}
div.sphinxsidebar {
display: block;
float: none;
width: 102.5%;
margin: 50px -30px -20px -30px;
padding: 10px 20px;
background: #333;
color: white;
}
div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p,
div.sphinxsidebar h3 a {
color: white;
}
div.sphinxsidebar a {
color: #aaa;
}
div.sphinxsidebar p.logo {
display: none;
}
div.document {
width: 100%;
margin: 0;
}
div.related {
display: block;
margin: 0;
padding: 10px 0 20px 0;
}
div.related ul,
div.related ul li {
margin: 0;
padding: 0;
}
div.footer {
display: none;
}
div.bodywrapper {
margin: 0;
}
div.body {
min-height: 0;
padding: 0;
}
+9
View File
@@ -0,0 +1,9 @@
[theme]
inherit = basic
stylesheet = flasky.css
pygments_style = flask_theme_support.FlaskyStyle
[options]
index_logo = ''
index_logo_height = 120px
touch_icon =
+22
View File
@@ -0,0 +1,22 @@
{% extends "basic/layout.html" %}
{% block header %}
{{ super() }}
{% if pagename == 'index' %}
<div class=indexwrapper>
{% endif %}
{% endblock %}
{% block footer %}
{% if pagename == 'index' %}
</div>
{% endif %}
{% endblock %}
{# do not display relbars #}
{% block relbar1 %}{% endblock %}
{% block relbar2 %}
{% if theme_github_fork %}
<a href="http://github.com/{{ theme_github_fork }}"><img style="position: fixed; top: 0; right: 0; border: 0;"
src="http://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub" /></a>
{% endif %}
{% endblock %}
{% block sidebar1 %}{% endblock %}
{% block sidebar2 %}{% endblock %}
+287
View File
@@ -0,0 +1,287 @@
/*
* flasky.css_t
* ~~~~~~~~~~~~
*
* Sphinx stylesheet -- flasky theme based on nature theme.
*
* :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@import url("basic.css");
/* -- page layout ----------------------------------------------------------- */
body {
font-family: 'Georgia', serif;
font-size: 17px;
color: #000;
background: white;
margin: 0;
padding: 0;
}
div.documentwrapper {
float: left;
width: 100%;
}
div.bodywrapper {
margin: 40px auto 0 auto;
width: 700px;
}
hr {
border: 1px solid #B1B4B6;
}
div.body {
background-color: #ffffff;
color: #3E4349;
padding: 0 30px 30px 30px;
}
img.floatingflask {
padding: 0 0 10px 10px;
float: right;
}
div.footer {
text-align: right;
color: #888;
padding: 10px;
font-size: 14px;
width: 650px;
margin: 0 auto 40px auto;
}
div.footer a {
color: #888;
text-decoration: underline;
}
div.related {
line-height: 32px;
color: #888;
}
div.related ul {
padding: 0 0 0 10px;
}
div.related a {
color: #444;
}
/* -- body styles ----------------------------------------------------------- */
a {
color: #004B6B;
text-decoration: underline;
}
a:hover {
color: #6D4100;
text-decoration: underline;
}
div.body {
padding-bottom: 40px; /* saved for footer */
}
div.body h1,
div.body h2,
div.body h3,
div.body h4,
div.body h5,
div.body h6 {
font-family: 'Garamond', 'Georgia', serif;
font-weight: normal;
margin: 30px 0px 10px 0px;
padding: 0;
}
{% if theme_index_logo %}
div.indexwrapper h1 {
text-indent: -999999px;
background: url({{ theme_index_logo }}) no-repeat center center;
height: {{ theme_index_logo_height }};
}
{% endif %}
div.body h2 { font-size: 180%; }
div.body h3 { font-size: 150%; }
div.body h4 { font-size: 130%; }
div.body h5 { font-size: 100%; }
div.body h6 { font-size: 100%; }
a.headerlink {
color: white;
padding: 0 4px;
text-decoration: none;
}
a.headerlink:hover {
color: #444;
background: #eaeaea;
}
div.body p, div.body dd, div.body li {
line-height: 1.4em;
}
div.admonition {
background: #fafafa;
margin: 20px -30px;
padding: 10px 30px;
border-top: 1px solid #ccc;
border-bottom: 1px solid #ccc;
}
div.admonition p.admonition-title {
font-family: 'Garamond', 'Georgia', serif;
font-weight: normal;
font-size: 24px;
margin: 0 0 10px 0;
padding: 0;
line-height: 1;
}
div.admonition p.last {
margin-bottom: 0;
}
div.highlight{
background-color: white;
}
dt:target, .highlight {
background: #FAF3E8;
}
div.note {
background-color: #eee;
border: 1px solid #ccc;
}
div.seealso {
background-color: #ffc;
border: 1px solid #ff6;
}
div.topic {
background-color: #eee;
}
div.warning {
background-color: #ffe4e4;
border: 1px solid #f66;
}
p.admonition-title {
display: inline;
}
p.admonition-title:after {
content: ":";
}
pre, tt {
font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
font-size: 0.85em;
}
img.screenshot {
}
tt.descname, tt.descclassname {
font-size: 0.95em;
}
tt.descname {
padding-right: 0.08em;
}
img.screenshot {
-moz-box-shadow: 2px 2px 4px #eee;
-webkit-box-shadow: 2px 2px 4px #eee;
box-shadow: 2px 2px 4px #eee;
}
table.docutils {
border: 1px solid #888;
-moz-box-shadow: 2px 2px 4px #eee;
-webkit-box-shadow: 2px 2px 4px #eee;
box-shadow: 2px 2px 4px #eee;
}
table.docutils td, table.docutils th {
border: 1px solid #888;
padding: 0.25em 0.7em;
}
table.field-list, table.footnote {
border: none;
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
}
table.footnote {
margin: 15px 0;
width: 100%;
border: 1px solid #eee;
}
table.field-list th {
padding: 0 0.8em 0 0;
}
table.field-list td {
padding: 0;
}
table.footnote td {
padding: 0.5em;
}
dl {
margin: 0;
padding: 0;
}
dl dd {
margin-left: 30px;
}
pre {
padding: 0;
margin: 15px -30px;
padding: 8px;
line-height: 1.3em;
padding: 7px 30px;
background: #eee;
border-radius: 2px;
-moz-border-radius: 2px;
-webkit-border-radius: 2px;
}
dl pre {
margin-left: -60px;
padding-left: 60px;
}
tt {
background-color: #ecf0f3;
color: #222;
/* padding: 1px 2px; */
}
tt.xref, a tt {
background-color: #FBFBFB;
}
a:hover tt {
background: #EEE;
}
+10
View File
@@ -0,0 +1,10 @@
[theme]
inherit = basic
stylesheet = flasky.css
nosidebar = true
pygments_style = flask_theme_support.FlaskyStyle
[options]
index_logo = ''
index_logo_height = 120px
github_fork = ''
+86
View File
@@ -0,0 +1,86 @@
# flasky extensions. flasky pygments style based on tango style
from pygments.style import Style
from pygments.token import Keyword, Name, Comment, String, Error, \
Number, Operator, Generic, Whitespace, Punctuation, Other, Literal
class FlaskyStyle(Style):
background_color = "#f8f8f8"
default_style = ""
styles = {
# No corresponding class for the following:
#Text: "", # class: ''
Whitespace: "underline #f8f8f8", # class: 'w'
Error: "#a40000 border:#ef2929", # class: 'err'
Other: "#000000", # class 'x'
Comment: "italic #8f5902", # class: 'c'
Comment.Preproc: "noitalic", # class: 'cp'
Keyword: "bold #004461", # class: 'k'
Keyword.Constant: "bold #004461", # class: 'kc'
Keyword.Declaration: "bold #004461", # class: 'kd'
Keyword.Namespace: "bold #004461", # class: 'kn'
Keyword.Pseudo: "bold #004461", # class: 'kp'
Keyword.Reserved: "bold #004461", # class: 'kr'
Keyword.Type: "bold #004461", # class: 'kt'
Operator: "#582800", # class: 'o'
Operator.Word: "bold #004461", # class: 'ow' - like keywords
Punctuation: "bold #000000", # class: 'p'
# because special names such as Name.Class, Name.Function, etc.
# are not recognized as such later in the parsing, we choose them
# to look the same as ordinary variables.
Name: "#000000", # class: 'n'
Name.Attribute: "#c4a000", # class: 'na' - to be revised
Name.Builtin: "#004461", # class: 'nb'
Name.Builtin.Pseudo: "#3465a4", # class: 'bp'
Name.Class: "#000000", # class: 'nc' - to be revised
Name.Constant: "#000000", # class: 'no' - to be revised
Name.Decorator: "#888", # class: 'nd' - to be revised
Name.Entity: "#ce5c00", # class: 'ni'
Name.Exception: "bold #cc0000", # class: 'ne'
Name.Function: "#000000", # class: 'nf'
Name.Property: "#000000", # class: 'py'
Name.Label: "#f57900", # class: 'nl'
Name.Namespace: "#000000", # class: 'nn' - to be revised
Name.Other: "#000000", # class: 'nx'
Name.Tag: "bold #004461", # class: 'nt' - like a keyword
Name.Variable: "#000000", # class: 'nv' - to be revised
Name.Variable.Class: "#000000", # class: 'vc' - to be revised
Name.Variable.Global: "#000000", # class: 'vg' - to be revised
Name.Variable.Instance: "#000000", # class: 'vi' - to be revised
Number: "#990000", # class: 'm'
Literal: "#000000", # class: 'l'
Literal.Date: "#000000", # class: 'ld'
String: "#4e9a06", # class: 's'
String.Backtick: "#4e9a06", # class: 'sb'
String.Char: "#4e9a06", # class: 'sc'
String.Doc: "italic #8f5902", # class: 'sd' - like a comment
String.Double: "#4e9a06", # class: 's2'
String.Escape: "#4e9a06", # class: 'se'
String.Heredoc: "#4e9a06", # class: 'sh'
String.Interpol: "#4e9a06", # class: 'si'
String.Other: "#4e9a06", # class: 'sx'
String.Regex: "#4e9a06", # class: 'sr'
String.Single: "#4e9a06", # class: 's1'
String.Symbol: "#4e9a06", # class: 'ss'
Generic: "#000000", # class: 'g'
Generic.Deleted: "#a40000", # class: 'gd'
Generic.Emph: "italic #000000", # class: 'ge'
Generic.Error: "#ef2929", # class: 'gr'
Generic.Heading: "bold #000080", # class: 'gh'
Generic.Inserted: "#00A000", # class: 'gi'
Generic.Output: "#888", # class: 'go'
Generic.Prompt: "#745334", # class: 'gp'
Generic.Strong: "bold #000000", # class: 'gs'
Generic.Subheading: "bold #800080", # class: 'gu'
Generic.Traceback: "bold #a40000", # class: 'gt'
}
+10
View File
@@ -0,0 +1,10 @@
.. _openerp library:
Server-side library
-------------------
.. automodule:: openerp
:members:
:undoc-members:
+7
View File
@@ -0,0 +1,7 @@
ORM and models
--------------
.. automodule:: openerp.osv.orm
:members:
:undoc-members:
+20
View File
@@ -0,0 +1,20 @@
Start-up script
---------------
To run the OpenERP server, the conventional approach is to use the
`openerp-server` script. It loads the :ref:`openerp library`, sets a few
configuration variables corresponding to command-line arguments, and starts to
listen to incoming connections from clients.
Depending on your deployment needs, you can write such a start-up script very
easily. We also recommend you take a look at an alternative tool called
`openerp-command` that can, among other things, launch the server.
.. versionadded:: 6.1
Yet another alternative is to use a WSGI-compatible HTTP server and let it call
into one of the WSGI entry points of the server.
.. versionadded:: 6.1
+256
View File
@@ -0,0 +1,256 @@
# -*- coding: utf-8 -*-
#
# OpenERP Technical Documentation configuration file, created by
# sphinx-quickstart on Fri Feb 17 16:14:06 2012.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
sys.path.append(os.path.abspath('_themes'))
sys.path.append(os.path.abspath('..'))
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.viewcode']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'OpenERP Server Developers Documentation'
copyright = u'2012, OpenERP s.a.'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '6.1'
# The full version, including alpha/beta/rc tags.
release = '6.1'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'flask'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
html_theme_path = ['_themes']
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
html_sidebars = {
'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'],
'**': ['sidebarlogo.html', 'localtoc.html', 'relations.html',
'sourcelink.html', 'searchbox.html']
}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'openerp-server-doc'
# -- Options for LaTeX output --------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'openerp-server-doc.tex', u'OpenERP Server Developers Documentation',
u'OpenERP s.a.', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output --------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'openerp-server-doc', u'OpenERP Server Developers Documentation',
[u'OpenERP s.a.'], 1)
]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output ------------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'OpenERPServerDocumentation', u'OpenERP Server Developers Documentation',
u'OpenERP s.a.', 'OpenERPServerDocumentation', 'Developers documentation for the openobject-server project.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {
'python': ('http://docs.python.org/', None),
'openerpweb': ('http://doc.openerp.com/trunk/developers/web', None),
'openerpdev': ('http://doc.openerp.com/trunk/developers', None),
}
+6
View File
@@ -0,0 +1,6 @@
:orphan:
OpenERP Server Developers Documentation
=======================================
.. include:: index.rst.inc
+8
View File
@@ -0,0 +1,8 @@
OpenERP Server
''''''''''''''
.. toctree::
:maxdepth: 1
test-framework
+100
View File
@@ -0,0 +1,100 @@
.. _test-framework:
Test framework
==============
In addition to the YAML-based tests, OpenERP uses the unittest2_ testing
framework to test both the core ``openerp`` package and its addons. For the
core and each addons, tests are divided between three (overlapping) sets:
1. A test suite that comprises all the tests that can be run right after the
addons is installed (or, for the core, right after a database is created).
That suite is called ``fast_suite`` and must contain only tests that can be run
frequently. Actually most of the tests should be considered fast enough to be
included in that ``fast_suite`` list and only tests that take a long time to run
(e.g. more than a minute) should not be listed. Those long tests should come up
pretty rarely.
2. A test suite called ``checks`` provides sanity checks. These tests are
invariants that must be full-filled at any time. They are expected to always
pass: obviously they must pass right after the module is installed (i.e. just
like the ``fast_suite`` tests), but they must also pass after any other module is
installed, after a migration, or even after the database was put in production
for a few months.
3. The third suite is made of all the tests: those provided by the two above
suites, but also tests that are not explicitely listed in ``fast_suite`` or
``checks``. They are not explicitely listed anywhere and are discovered
automatically.
As the sanity checks provide stronger guarantees about the code and database
structure, new tests must be added to the ``checks`` suite whenever it is
possible. Said with other words: one should try to avoid writing tests that
assume a freshly installed/unaltered module or database.
It is possible to have tests that are not listed in ``fast_suite`` or
``checks``. This is useful if a test takes a lot of time. By default, when
using the testing infrastructure, tests should run fast enough so that people
can use them frequently. One can also use that possiblity for tests that
require some complex setup before they can be successfuly run.
As a rule of thumb when writing a new test, try to add it to the ``checks``
suite. If it really needs that the module it belongs to is freshly installed,
add it to ``fast_suite``. Finally, if it can not be run in an acceptable time
frame, don't add it to any explicit list.
Writing tests
-------------
The tests must be developed under ``<addons-name>.tests`` (or ``openerp.tests``
for the core). For instance, with respect to the tests, a module ``foo``
should be organized as follow::
foo/
__init__.py # does not import .tests
tests/
__init__.py # import some of the tests sub-modules, and
# list them in fast_suite or checks
test_bar.py # contains unittest2 classes
test_baz.py # idem
... and so on ...
The two explicit lists of tests are thus the variables ``foo.tests.fast_suite``
and ``foo.tests.checks``. As an example, you can take a look at the
``openerp.tests`` module (which follows exactly the same conventions even if it
is not an addons).
Note that the ``fast_suite`` and ``checks`` variables are really lists of
module objects. They could be directly unittest2 suite objects if necessary in
the future.
Running the tests
-----------------
To run the tests (see :ref:`above <test-framework>` to learn how tests are
organized), the simplest way is to use the ``oe`` command (provided by the
``openerp-command`` project).
::
> oe run-tests # will run all the fast_suite tests
> oe run-tests -m openerp # will run all the fast_suite tests defined in `openerp.tests`
> oe run-tests -m sale # will run all the fast_suite tests defined in `openerp.addons.sale.tests`
> oe run-tests -m foo.test_bar # will run the tests defined in `openerp.addons.foo.tests.test_bar`
In addition to the above possibilities, when invoked with a non-existing module
(or module.sub-module) name, oe will reply with a list of available test
sub-modules.
Depending on the unittest2_ class that is used to write the tests (see
``openerp.tests.common`` for some helper classes that you can re-use), a database
may be created before the test is run, and the module providing the test will
be installed on that database.
Because creating a database, installing modules, and then dropping it is
expensive, it is possible to interleave the run of the ``fast_suite`` tests
with the initialization of a new database: the dabase is created, and after
each requested module is installed, its fast_suite tests are run. The database
is thus created and dropped (and the modules installed) only once.
.. _unittest2: http://pypi.python.org/pypi/unittest2
-1
View File
@@ -91,7 +91,6 @@
'test/bug_lp541545.xml',
'test/test_osv_expression.yml',
'test/test_ir_rule.yml', # <-- These tests modify/add/delete ir_rules.
'test/test_ir_values.yml',
# Commented because this takes some time.
# This must be (un)commented with the corresponding import statement
# in test/__init__.py.
+1 -1
View File
@@ -2,7 +2,7 @@
<openerp>
<data noupdate="1">
<record id="demo_address" model="res.partner.address">
<record id="demo_address" model="res.partner">
<field name="name">Fabien Dupont</field>
<field name="street">Chaussee de Namur</field>
<field name="zip">1367</field>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+91 -44
View File
@@ -8,14 +8,14 @@ msgstr ""
"Project-Id-Version: openobject-server\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-02-08 00:44+0000\n"
"PO-Revision-Date: 2012-02-18 21:50+0000\n"
"PO-Revision-Date: 2012-02-28 16:02+0000\n"
"Last-Translator: Akira Hiyama <Unknown>\n"
"Language-Team: Japanese <ja@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-02-19 05:41+0000\n"
"X-Generator: Launchpad (build 14814)\n"
"X-Launchpad-Export-Date: 2012-02-29 04:41+0000\n"
"X-Generator: Launchpad (build 14874)\n"
#. module: base
#: model:res.country,name:base.sh
@@ -7930,73 +7930,73 @@ msgstr "スペイン語(メキシコ)"
#: code:addons/base/publisher_warranty/publisher_warranty.py:145
#, python-format
msgid "Please verify your publisher warranty serial number and validity."
msgstr ""
msgstr "発行者の保証シリアル番号とその有効性を確認して下さい。"
#. module: base
#: view:res.log:0
msgid "My Logs"
msgstr ""
msgstr "マイログ"
#. module: base
#: model:res.country,name:base.bt
msgid "Bhutan"
msgstr ""
msgstr "ブータン"
#. module: base
#: help:ir.sequence,number_next:0
msgid "Next number of this sequence"
msgstr ""
msgstr "この順番の次の番号"
#. module: base
#: model:res.partner.category,name:base.res_partner_category_11
msgid "Textile Suppliers"
msgstr ""
msgstr "繊維供給元"
#. module: base
#: selection:ir.actions.url,target:0
msgid "This Window"
msgstr ""
msgstr "このウィンドウ"
#. module: base
#: view:publisher_warranty.contract:0
msgid "Publisher Warranty Contracts"
msgstr ""
msgstr "発行者保証契約"
#. module: base
#: help:res.log,name:0
msgid "The logging message."
msgstr ""
msgstr "ロギングメッセージ"
#. module: base
#: field:base.language.export,format:0
msgid "File Format"
msgstr ""
msgstr "ファイル形式"
#. module: base
#: field:res.lang,iso_code:0
msgid "ISO code"
msgstr ""
msgstr "ISOコード"
#. module: base
#: view:res.log:0
#: field:res.log,read:0
msgid "Read"
msgstr ""
msgstr "読み込み"
#. module: base
#: model:ir.module.module,shortdesc:base.module_association
msgid "Associations Management"
msgstr ""
msgstr "アソシエーション管理"
#. module: base
#: help:ir.model,modules:0
msgid "List of modules in which the object is defined or inherited"
msgstr ""
msgstr "オブジェクトが定義または継承されたモジュールのリスト"
#. module: base
#: model:ir.module.module,shortdesc:base.module_hr_payroll
msgid "Payroll"
msgstr ""
msgstr "給与"
#. module: base
#: model:ir.actions.act_window,help:base.action_country_state
@@ -8004,7 +8004,7 @@ msgid ""
"If you are working on the American market, you can manage the different "
"federal states you are working on from here. Each state is attached to one "
"country."
msgstr ""
msgstr "あなたがアメリカの市場で働いているなら、あなたはここからあなたが働く異なる州を管理できます。それぞれの州はひとつの国に属しています。"
#. module: base
#: model:ir.module.module,description:base.module_delivery
@@ -8019,23 +8019,31 @@ msgid ""
"\n"
" "
msgstr ""
"\n"
"受注と引き取りに関する配送方法を追加することができます。\n"
"==============================================================\n"
"\n"
"価格によるあなた自身の運搬人や配送グリッドの定義ができます。\n"
"引き取りから請求書を作成する場合は、OpenERPは配送の行を追加し計算を行います。\n"
"\n"
" "
#. module: base
#: view:workflow.workitem:0
msgid "Workflow Workitems"
msgstr ""
msgstr "ワークフロー作業項目"
#. module: base
#: model:res.country,name:base.vc
msgid "Saint Vincent & Grenadines"
msgstr ""
msgstr "セントビンセント・グレナディーン"
#. module: base
#: field:ir.mail_server,smtp_pass:0
#: field:partner.sms.send,password:0
#: field:res.users,password:0
msgid "Password"
msgstr ""
msgstr "パスワード"
#. module: base
#: model:ir.module.module,description:base.module_account_anglo_saxon
@@ -8059,11 +8067,23 @@ msgid ""
"Secondly, price differences between actual purchase price and fixed product "
"standard price are booked on a separate account"
msgstr ""
"\n"
"このモジュールは株式トランザクションの会計ロジックの変更によりアングロサクソン会計方式をサポートします。\n"
"============================================================================="
"========================================\n"
"\n"
"アングロサクソン会計(英米型会計)国と、ラインまたはコンチネンタル会計(大陸型会計)国と呼ばれる違いは\n"
"売上原価の計上タイミングの違いです。\n"
"アングロサクソン会計は販売の請求書が作成された時に原価を計上します。コンチネンタル会計では\n"
"商品が出荷された時に原価を計上します。\n"
"このモジュールは中間勘定を使うことによってこの機能を加えます。借方または貸方アカウントにその総額を転送\n"
"するために請求書が作成された時に、出荷された商品の価値を保存し、そしてこの中間勘定を反対記帳します。\n"
"第2に、実際の購入価格と固定の製品標準価格の差は分離された勘定に記帳されます。"
#. module: base
#: field:res.partner,title:0
msgid "Partner Firm"
msgstr ""
msgstr "パートナ企業"
#. module: base
#: model:ir.actions.act_window,name:base.action_model_fields
@@ -8073,12 +8093,12 @@ msgstr ""
#: view:ir.model.fields:0
#: model:ir.ui.menu,name:base.ir_model_model_fields
msgid "Fields"
msgstr ""
msgstr "項目"
#. module: base
#: model:ir.actions.act_window,name:base.action_partner_employee_form
msgid "Employees"
msgstr ""
msgstr "従業員"
#. module: base
#: field:ir.exports.line,name:0
@@ -8091,7 +8111,7 @@ msgstr "項目名"
#: help:res.log,read:0
msgid ""
"If this log item has been read, get() should not send it to the client"
msgstr ""
msgstr "もしこのログ項目が読まれたなら、get()をクライアントに送るべきではありません。"
#. module: base
#: model:ir.module.module,description:base.module_web_uservoice
@@ -8103,48 +8123,54 @@ msgid ""
"Invite OpenERP user feedback, powered by uservoice.\n"
" "
msgstr ""
"\n"
"ヘッダーの中にフィードバックボタンを加えて下さい。\n"
"==============================\n"
"\n"
"ユーザの声によるOpenERPのユーザフィードバックを求めて下さい。\n"
" "
#. module: base
#: field:res.company,rml_header2:0
#: field:res.company,rml_header3:0
msgid "RML Internal Header"
msgstr ""
msgstr "RML内部ヘッダー"
#. module: base
#: field:ir.actions.act_window,search_view_id:0
msgid "Search View Ref."
msgstr ""
msgstr "リファレンスの検索ビュー"
#. module: base
#: field:ir.module.module,installed_version:0
msgid "Latest version"
msgstr ""
msgstr "最新バージョン"
#. module: base
#: view:ir.mail_server:0
msgid "Test Connection"
msgstr ""
msgstr "接続テスト"
#. module: base
#: model:ir.actions.act_window,name:base.action_partner_address_form
#: model:ir.ui.menu,name:base.menu_partner_address_form
msgid "Addresses"
msgstr ""
msgstr "住所"
#. module: base
#: model:res.country,name:base.mm
msgid "Myanmar"
msgstr ""
msgstr "ミャンマー"
#. module: base
#: help:ir.model.fields,modules:0
msgid "List of modules in which the field is defined"
msgstr ""
msgstr "項目が定義されているモジュールのリスト"
#. module: base
#: selection:base.language.install,lang:0
msgid "Chinese (CN) / 简体中文"
msgstr ""
msgstr "中国語(简体中文)"
#. module: base
#: field:res.bank,street:0
@@ -8157,7 +8183,7 @@ msgstr ""
#. module: base
#: model:res.country,name:base.yu
msgid "Yugoslavia"
msgstr ""
msgstr "ユーゴスラビア"
#. module: base
#: model:ir.module.module,description:base.module_purchase_double_validation
@@ -8170,22 +8196,29 @@ msgid ""
"that exceeds minimum amount set by configuration wizard.\n"
" "
msgstr ""
"\n"
"最低額を超える購入のための二重検証\n"
"=========================================================\n"
"\n"
"このモジュールは、コンフィギュレーションウィザードによって設定される最低額を超えた購入を\n"
"検証するための購入ワークフローを変更します。\n"
" "
#. module: base
#: field:res.currency,rounding:0
msgid "Rounding Factor"
msgstr ""
msgstr "丸め係数"
#. module: base
#: model:res.country,name:base.ca
msgid "Canada"
msgstr ""
msgstr "カナダ"
#. module: base
#: code:addons/base/res/res_company.py:158
#, python-format
msgid "Reg: "
msgstr ""
msgstr "Reg: "
#. module: base
#: help:res.currency.rate,currency_rate_type_id:0
@@ -8193,27 +8226,28 @@ msgid ""
"Allow you to define your own currency rate types, like 'Average' or 'Year to "
"Date'. Leave empty if you simply want to use the normal 'spot' rate type"
msgstr ""
"平均または年初から本日までといったあなた自身の通貨レートタイプを定義できます。通常のスポットレートを希望する場合には空白のままとして下さい。"
#. module: base
#: selection:ir.module.module.dependency,state:0
msgid "Unknown"
msgstr ""
msgstr "不明"
#. module: base
#: model:ir.actions.act_window,name:base.action_res_users_my
msgid "Change My Preferences"
msgstr ""
msgstr "設定の変更"
#. module: base
#: code:addons/base/ir/ir_actions.py:167
#, python-format
msgid "Invalid model name in the action definition."
msgstr ""
msgstr "アクション定義におけるモデル名が無効です。"
#. module: base
#: field:partner.sms.send,text:0
msgid "SMS Message"
msgstr ""
msgstr "SMSメッセージ"
#. module: base
#: model:ir.module.module,description:base.module_l10n_ro
@@ -8227,21 +8261,28 @@ msgid ""
"Romanian accounting chart and localization.\n"
" "
msgstr ""
"\n"
"このモジュールはルーマニアのOpenERPのため会計表、VAT構造、登録番号を管理するためのモジュールです。\n"
"============================================================================="
"===================================\n"
"\n"
"ルーマニアの会計表とローカル化\n"
" "
#. module: base
#: model:res.country,name:base.cm
msgid "Cameroon"
msgstr ""
msgstr "カメルーン"
#. module: base
#: model:res.country,name:base.bf
msgid "Burkina Faso"
msgstr ""
msgstr "ブルキナファソ"
#. module: base
#: selection:ir.model.fields,state:0
msgid "Custom Field"
msgstr ""
msgstr "カスタム項目"
#. module: base
#: model:ir.module.module,description:base.module_project_retro_planning
@@ -8254,6 +8295,12 @@ msgid ""
"all the tasks will change accordingly.\n"
" "
msgstr ""
"\n"
"変更日はプロジェクトの終了日の変更に応じて変更されます。\n"
"======================================================\n"
"\n"
"もしプロジェクトの終了日が変更された時は、すべてのタスクの期限と開始日が変更されます。\n"
" "
#. module: base
#: help:res.users,view:0
File diff suppressed because it is too large Load Diff
@@ -6,15 +6,13 @@
<rml_footer2 type="field" name="rml_footer2"/>
<title type="field" name="partner_id.title"/>
<name type="field" name="partner_id.name"/>
<address type="zoom" name="partner_id.address">
<street type="field" name="street"/>
<zip type="field" name="zip"/>
<city type="field" name="city"/>
<state type="field" name="state_id.name"/>
<country type="field" name="country_id.name"/>
<phone type="field" name="phone"/>
<email type="field" name="email"/>
</address>
<street type="field" name="street"/>
<zip type="field" name="zip"/>
<city type="field" name="city"/>
<state type="field" name="state_id.name"/>
<country type="field" name="country_id.name"/>
<phone type="field" name="phone"/>
<email type="field" name="email"/>
</corporation>
<user>
<name type="field" name="name"/>
@@ -22,18 +22,18 @@
<setFont name="Helvetica" size="10"/>
<drawRightString x="20cm" y="28.5cm"><xsl:value-of select="//corporate-header/corporation/rml_header1"/></drawRightString>
<drawString x="1cm" y="27cm"><xsl:value-of select="//corporate-header/corporation/address/street"/></drawString>
<drawString x="1cm" y="27cm"><xsl:value-of select="//corporate-header/corporation/street"/></drawString>
<drawString x="1cm" y="26.5cm">
<xsl:value-of select="//corporate-header/corporation/address/zip"/>
<xsl:value-of select="//corporate-header/corporation/zip"/>
<xsl:text> </xsl:text>
<xsl:value-of select="//corporate-header/corporation/address/city"/>
<xsl:value-of select="//corporate-header/corporation/city"/>
<xsl:text> - </xsl:text>
<xsl:value-of select="//corporate-header/corporation/address/country"/>
<xsl:value-of select="//corporate-header/corporation/country"/>
</drawString>
<drawString x="1cm" y="26cm">Phone:</drawString>
<drawRightString x="7cm" y="26cm"><xsl:value-of select="//corporate-header/corporation/address/phone"/></drawRightString>
<drawRightString x="7cm" y="26cm"><xsl:value-of select="//corporate-header/corporation/phone"/></drawRightString>
<drawString x="1cm" y="25.5cm">Mail:</drawString>
<drawRightString x="7cm" y="25.5cm"><xsl:value-of select="//corporate-header/corporation/address/email"/></drawRightString>
<drawRightString x="7cm" y="25.5cm"><xsl:value-of select="//corporate-header/corporation/email"/></drawRightString>
<!--page bottom-->
@@ -57,18 +57,18 @@
<setFont name="Helvetica" size="10"/>
<drawRightString x="1cm" y="27.5cm"><xsl:value-of select="//corporate-header/corporation/rml_header1"/></drawRightString>
<drawString x="1cm" y="27cm"><xsl:value-of select="//corporate-header/corporation/address/street"/></drawString>
<drawString x="1cm" y="27cm"><xsl:value-of select="//corporate-header/corporation/street"/></drawString>
<drawString x="1cm" y="26.5cm">
<xsl:value-of select="//corporate-header/corporation/address/zip"/>
<xsl:value-of select="//corporate-header/corporation/zip"/>
<xsl:text> </xsl:text>
<xsl:value-of select="//corporate-header/corporation/address/city"/>
<xsl:value-of select="//corporate-header/corporation/city"/>
<xsl:text> - </xsl:text>
<xsl:value-of select="//corporate-header/corporation/address/country"/>
<xsl:value-of select="//corporate-header/corporation/country"/>
</drawString>
<drawString x="1cm" y="26cm">Phone:</drawString>
<drawRightString x="7cm" y="26cm"><xsl:value-of select="//corporate-header/corporation/address/phone"/></drawRightString>
<drawRightString x="7cm" y="26cm"><xsl:value-of select="//corporate-header/corporation/phone"/></drawRightString>
<drawString x="1cm" y="25.5cm">Mail:</drawString>
<drawRightString x="7cm" y="25.5cm"><xsl:value-of select="//corporate-header/corporation/address/email"/></drawRightString>
<drawRightString x="7cm" y="25.5cm"><xsl:value-of select="//corporate-header/corporation/email"/></drawRightString>
<!--page bottom-->
@@ -204,8 +204,8 @@
</table:table-cell>
</table:table-row>
</table:table>
<text:p text:style-name="P3">[[ company.partner_id.address and company.partner_id.address[0].street ]]</text:p>
<text:p text:style-name="P3">[[ company.partner_id.address and company.partner_id.address[0].zip ]] [[ company.partner_id.address and company.partner_id.address[0].city ]] - [[ company.partner_id.address and company.partner_id.address[0].country_id and company.partner_id.address[0].country_id.name ]]</text:p>
<text:p text:style-name="P3">[[ company.partner_id and company.partner_id.street ]]</text:p>
<text:p text:style-name="P3">[[ company.partner_id and company.partner_id.zip ]] [[ company.partner_id and company.partner_id.city ]] - [[ company.partner_id and company.partner_id.country_id and company.partner_id.country_id.name ]]</text:p>
<table:table table:name="Table3" table:style-name="Table3">
<table:table-column table:style-name="Table3.A"/>
<table:table-column table:style-name="Table3.B"/>
@@ -214,7 +214,7 @@
<text:p text:style-name="P4">Phone :</text:p>
</table:table-cell>
<table:table-cell table:style-name="Table3.A1" table:value-type="string">
<text:p text:style-name="P5">[[ company.partner_id.address and company.partner_id.address[0].phone ]]</text:p>
<text:p text:style-name="P5">[[ company.partner_id and company.partner_id.phone ]]</text:p>
</table:table-cell>
</table:table-row>
<table:table-row>
@@ -222,7 +222,7 @@
<text:p text:style-name="P4">Mail :</text:p>
</table:table-cell>
<table:table-cell table:style-name="Table3.A2" table:value-type="string">
<text:p text:style-name="P5">[[ company.partner_id.address and company.partner_id.address[0].email ]]</text:p>
<text:p text:style-name="P5">[[ company.partner_id and company.partner_id.email ]]</text:p>
</table:table-cell>
</table:table-row>
</table:table>
+9 -9
View File
@@ -14,33 +14,33 @@
<tr>
<td>
% if company['partner_id']['address'] and company['partner_id']['address'][0]['street']:
<small>${company.partner_id.address[0].street}</small></br>
% if company['partner_id'] and company['partner_id']['street']:
<small>${company.partner_id.street}</small></br>
%endif
</td>
</tr>
<tr>
<td>
% if company['partner_id']['address'] and company['partner_id']['address'][0]['zip']:
<small>${company.partner_id.address[0].zip}
${company.partner_id.address[0].city}-${company.partner_id.address[0].country_id and company.partner_id.address[0].country_id.name}</small></br>
% if company['partner_id'] and company['partner_id']['zip']:
<small>${company.partner_id.zip}
${company.partner_id.city}-${company.partner_id.country_id and company.partner_id.country_id.name}</small></br>
%endif
</td>
</tr>
<tr>
<td>
% if company['partner_id']['address'] and company['partner_id']['address'][0]['phone']:
<small><b>Phone:</b>${company.partner_id.address and company.partner_id.address[0].phone}</small></br>
% if company['partner_id'] and company['partner_id']['phone']:
<small><b>Phone:</b>${company.partner_id and company.partner_id.phone}</small></br>
%endif
</td>
</tr>
<tr>
<td>
% if company['partner_id']['address'] and company['partner_id']['address'][0]['email']:
<small><b>Mail:</b>${company.partner_id.address and company.partner_id.address[0].email}</small></br></<address>
% if company['partner_id'] and company['partner_id']['email']:
<small><b>Mail:</b>${company.partner_id and company.partner_id.email}</small></br></<address>
%endif
</td>
</tr>
Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

@@ -3,16 +3,12 @@
<address type="fields" name="id">
<company-title type="field" name="title.name"/>
<company-name type="field" name="name"/>
<contact type="zoom" name="address">
<type type="field" name="type"/>
<title type="field" name="title.name"/>
<name type="field" name="name"/>
<street type="field" name="street"/>
<street2 type="field" name="street2"/>
<zip type="field" name="zip"/>
<city type="field" name="city"/>
<state type="field" name="state_id.name"/>
<country type="field" name="country_id.name"/>
</contact>
<type type="field" name="type"/>
<street type="field" name="street"/>
<street2 type="field" name="street2"/>
<zip type="field" name="zip"/>
<city type="field" name="city"/>
<state type="field" name="state_id.name"/>
<country type="field" name="country_id.name"/>
</address>
</addresses>
@@ -58,31 +58,17 @@
<xsl:template match="address" mode="story">
<para style="nospace"><xsl:value-of select="company-name"/><xsl:text> </xsl:text><xsl:value-of select="company-title"/></para>
<xsl:choose>
<xsl:when test="count(contact[type='default']) >= 1">
<!-- apply the first 'contact' node with the type 'default' -->
<xsl:apply-templates select="contact[type='default'][1]"/>
</xsl:when>
<xsl:when test="count(contact[type='']) >= 1">
<!-- apply the first 'contact' node with an empty type -->
<xsl:apply-templates select="contact[type=''][1]"/>
</xsl:when>
<xsl:otherwise>
<!-- apply the first 'contact' node -->
<xsl:apply-templates select="contact[1]"/>
</xsl:otherwise>
</xsl:choose>
<para style="nospace"><xsl:value-of select="street"/></para>
<para style="nospace"><xsl:value-of select="street2"/></para>
<para style="nospace"><xsl:value-of select="zip"/><xsl:text> </xsl:text><xsl:value-of select="city"/></para>
<para style="nospace"><xsl:value-of select="state"/></para>
<para style="nospace"><xsl:value-of select="country"/></para>
<xsl:if test="position() &lt; last()">
<nextFrame/>
</xsl:if>
</xsl:template>
<xsl:template match="contact">
<para style="nospace"><xsl:value-of select="title"/><xsl:text> </xsl:text><xsl:value-of select="name"/></para>
<para style="nospace"><xsl:value-of select="street"/></para>
<para style="nospace"><xsl:value-of select="street2"/></para>
<para style="nospace"><xsl:value-of select="zip"/><xsl:text> </xsl:text><xsl:value-of select="city"/></para>
<para style="nospace"><xsl:value-of select="state"/></para>
<para style="nospace"><xsl:value-of select="country"/></para>
</xsl:template>
</xsl:stylesheet>
+5 -5
View File
@@ -219,11 +219,11 @@ class res_partner_bank(osv.osv):
if partner_id:
part = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context)
result['owner_name'] = part.name
result['street'] = part.address and part.address[0].street or False
result['city'] = part.address and part.address[0].city or False
result['zip'] = part.address and part.address[0].zip or False
result['country_id'] = part.address and part.address[0].country_id and part.address[0].country_id.id or False
result['state_id'] = part.address and part.address[0].state_id and part.address[0].state_id.id or False
result['street'] = part.street or False
result['city'] = part.city or False
result['zip'] = part.zip or False
result['country_id'] = part.country_id.id
result['state_id'] = part.state_id.id
return {'value': result}
res_partner_bank()
+7 -9
View File
@@ -77,13 +77,12 @@ class res_company(osv.osv):
""" Read the 'address' functional fields. """
result = {}
part_obj = self.pool.get('res.partner')
address_obj = self.pool.get('res.partner.address')
for company in self.browse(cr, uid, ids, context=context):
result[company.id] = {}.fromkeys(field_names, False)
if company.partner_id:
address_data = part_obj.address_get(cr, uid, [company.partner_id.id], adr_pref=['default'])
if address_data['default']:
address = address_obj.read(cr, uid, address_data['default'], field_names, context=context)
address = part_obj.read(cr, uid, address_data['default'], field_names, context=context)
for field in field_names:
result[company.id][field] = address[field] or False
return result
@@ -105,13 +104,12 @@ class res_company(osv.osv):
company = self.browse(cr, uid, company_id, context=context)
if company.partner_id:
part_obj = self.pool.get('res.partner')
address_obj = self.pool.get('res.partner.address')
address_data = part_obj.address_get(cr, uid, [company.partner_id.id], adr_pref=['default'])
address = address_data['default']
if address:
address_obj.write(cr, uid, [address], {name: value or False})
part_obj.write(cr, uid, [address], {name: value or False})
else:
address_obj.create(cr, uid, {name: value or False, 'partner_id': company.partner_id.id}, context=context)
part_obj.create(cr, uid, {name: value or False, 'parent_id': company.partner_id.id}, context=context)
return True
@@ -296,12 +294,12 @@ class res_company(osv.osv):
<drawString x="1.3cm" y="%s">[[ company.partner_id.name ]]</drawString>
<drawString x="1.3cm" y="%s">[[ company.partner_id.address and company.partner_id.address[0].street or '' ]]</drawString>
<drawString x="1.3cm" y="%s">[[ company.partner_id.address and company.partner_id.address[0].zip or '' ]] [[ company.partner_id.address and company.partner_id.address[0].city or '' ]] - [[ company.partner_id.address and company.partner_id.address[0].country_id and company.partner_id.address[0].country_id.name or '']]</drawString>
<drawString x="1.3cm" y="%s">[[ company.partner_id and company.partner_id.street or '' ]]</drawString>
<drawString x="1.3cm" y="%s">[[ company.partner_id and company.partner_id.city or '' ]] - [[ company.partner_id and company.partner_id.country_id and company.partner_id.country_id.name or '']]</drawString>
<drawString x="1.3cm" y="%s">Phone:</drawString>
<drawRightString x="7cm" y="%s">[[ company.partner_id.address and company.partner_id.address[0].phone or '' ]]</drawRightString>
<drawRightString x="7cm" y="%s">[[ company.partner_id and company.partner_id.phone or '' ]]</drawRightString>
<drawString x="1.3cm" y="%s">Mail:</drawString>
<drawRightString x="7cm" y="%s">[[ company.partner_id.address and company.partner_id.address[0].email or '' ]]</drawRightString>
<drawRightString x="7cm" y="%s">[[ company.partner_id and company.partner_id.email or '' ]]</drawRightString>
<lines>1.3cm %s 7cm %s</lines>
<!--page bottom-->
+1 -1
View File
@@ -46,7 +46,7 @@ addresses belonging to this country.\n\nYou can use the python-style string pate
'The code of the country must be unique !')
]
_defaults = {
'address_format': "%(street)s\n%(street2)s\n%(city)s,%(state_code)s %(zip)s\n%(country_name)s",
'address_format': "%(company_name)s\n%(street)s\n%(street2)s\n%(city)s,%(state_code)s %(zip)s\n%(country_name)s",
}
def name_search(self, cr, user, name='', args=None, operator='ilike',
+190 -126
View File
@@ -19,12 +19,14 @@
#
##############################################################################
import os
import math
from osv import fields,osv
from osv import osv, fields
import tools
import pooler
from tools.translate import _
import logging
import pooler
class res_payterm(osv.osv):
_description = 'Payment term'
@@ -33,7 +35,7 @@ class res_payterm(osv.osv):
_columns = {
'name': fields.char('Payment Term (short name)', size=64),
}
res_payterm()
class res_partner_category(osv.osv):
@@ -45,7 +47,7 @@ class res_partner_category(osv.osv):
used to select the short version of the
category name (without the direct parent),
when set to ``'short'``. The default is
the long version."""
the long version."""
if context is None:
context = {}
if context.get('partner_category_display') == 'short':
@@ -98,7 +100,7 @@ class res_partner_category(osv.osv):
_parent_store = True
_parent_order = 'name'
_order = 'parent_left'
res_partner_category()
class res_partner_title(osv.osv):
_name = 'res.partner.title'
@@ -108,25 +110,31 @@ class res_partner_title(osv.osv):
'domain': fields.selection([('partner','Partner'),('contact','Contact')], 'Domain', required=True, size=24)
}
_order = 'name'
res_partner_title()
def _lang_get(self, cr, uid, context=None):
obj = self.pool.get('res.lang')
ids = obj.search(cr, uid, [], context=context)
res = obj.read(cr, uid, ids, ['code', 'name'], context)
lang_pool = self.pool.get('res.lang')
ids = lang_pool.search(cr, uid, [], context=context)
res = lang_pool.read(cr, uid, ids, ['code', 'name'], context)
return [(r['code'], r['name']) for r in res] + [('','')]
def value_or_id(val):
""" return val or val.id if val is a browse record """
return val if isinstance(val, (bool, int, long, float, basestring)) else val.id
POSTAL_ADDRESS_FIELDS = ('street', 'street2', 'zip', 'city', 'state_id', 'country_id')
ADDRESS_FIELDS = POSTAL_ADDRESS_FIELDS + ('email', 'phone', 'fax', 'mobile', 'website', 'ref', 'lang')
class res_partner(osv.osv):
_description='Partner'
_name = "res.partner"
_order = "name"
_columns = {
'name': fields.char('Name', size=128, required=True, select=True),
'name': fields.char('Name', size=128, select=True),
'date': fields.date('Date', select=1),
'title': fields.many2one('res.partner.title','Partner Firm'),
'title': fields.many2one('res.partner.title','Title'),
'parent_id': fields.many2one('res.partner','Parent Partner'),
'child_ids': fields.one2many('res.partner', 'parent_id', 'Partner Ref.'),
'child_ids': fields.one2many('res.partner', 'parent_id', 'Contacts'),
'ref': fields.char('Reference', size=64, select=1),
'lang': fields.selection(_lang_get, 'Language', help="If the selected language is loaded in the system, all documents related to this partner will be printed in this language. If not, it will be english."),
'user_id': fields.many2one('res.users', 'Salesman', help='The internal user that is in charge of communicating with this partner if any.'),
@@ -134,7 +142,7 @@ class res_partner(osv.osv):
'bank_ids': fields.one2many('res.partner.bank', 'partner_id', 'Banks'),
'website': fields.char('Website',size=64, help="Website of Partner."),
'comment': fields.text('Notes'),
'address': fields.one2many('res.partner.address', 'partner_id', 'Contacts'),
'address': fields.one2many('res.partner.address', 'partner_id', 'Contacts'), # should be removed in version 7, but kept until then for backward compatibility
'category_id': fields.many2many('res.partner.category', 'res_partner_category_rel', 'partner_id', 'category_id', 'Categories'),
'events': fields.one2many('res.partner.event', 'partner_id', 'Events'),
'credit_limit': fields.float(string='Credit Limit'),
@@ -142,41 +150,80 @@ class res_partner(osv.osv):
'active': fields.boolean('Active'),
'customer': fields.boolean('Customer', help="Check this box if the partner is a customer."),
'supplier': fields.boolean('Supplier', help="Check this box if the partner is a supplier. If it's not checked, purchase people will not see it when encoding a purchase order."),
'city': fields.related('address', 'city', type='char', string='City'),
'function': fields.related('address', 'function', type='char', string='function'),
'subname': fields.related('address', 'name', type='char', string='Contact Name'),
'phone': fields.related('address', 'phone', type='char', string='Phone'),
'mobile': fields.related('address', 'mobile', type='char', string='Mobile'),
'country': fields.related('address', 'country_id', type='many2one', relation='res.country', string='Country'),
'employee': fields.boolean('Employee', help="Check this box if the partner is an Employee."),
'email': fields.related('address', 'email', type='char', size=240, string='E-mail'),
'function': fields.char('Function', size=128),
'type': fields.selection( [('default','Default'),('invoice','Invoice'),
('delivery','Delivery'), ('contact','Contact'),
('other','Other')],
'Address Type', help="Used to select automatically the right address according to the context in sales and purchases documents."),
'street': fields.char('Street', size=128),
'street2': fields.char('Street2', size=128),
'zip': fields.char('Zip', change_default=True, size=24),
'city': fields.char('City', size=128),
'state_id': fields.many2one("res.country.state", 'Fed. State', domain="[('country_id','=',country_id)]"),
'country_id': fields.many2one('res.country', 'Country'),
'country': fields.related('country_id', type='many2one', relation='res.country', string='Country'), # for backward compatibility
'email': fields.char('E-Mail', size=240),
'phone': fields.char('Phone', size=64),
'fax': fields.char('Fax', size=64),
'mobile': fields.char('Mobile', size=64),
'birthdate': fields.char('Birthdate', size=64),
'is_company': fields.boolean('Company', help="Check if the contact is a company, otherwise it is a person"),
'use_parent_address': fields.boolean('Use Company Address', help="Check to use the company's address"),
'photo': fields.binary('Photo'),
'company_id': fields.many2one('res.company', 'Company', select=1),
'color': fields.integer('Color Index'),
}
def _default_category(self, cr, uid, context=None):
if context is None:
context = {}
if 'category_id' in context and context['category_id']:
if context.get('category_id'):
return [context['category_id']]
return []
return False
def _get_photo(self, cr, uid, is_company, context=None):
if is_company:
path = os.path.join( tools.config['root_path'], 'addons', 'base', 'res', 'company_icon.png')
else:
path = os.path.join( tools.config['root_path'], 'addons', 'base', 'res', 'photo.png')
return open(path, 'rb').read().encode('base64')
_defaults = {
'active': lambda *a: 1,
'customer': lambda *a: 1,
'active': True,
'customer': True,
'category_id': _default_category,
'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'res.partner', context=c),
'color': 0,
'is_company': False,
'type': 'default',
'use_parent_address': True,
'photo': lambda self, cr, uid, context: self._get_photo(cr, uid, False, context),
}
def copy(self, cr, uid, id, default=None, context=None):
if default is None:
default = {}
name = self.read(cr, uid, [id], ['name'], context)[0]['name']
default.update({'name': name+ _(' (copy)'), 'events':[]})
default.update({'name': _('%s (copy)')%(name), 'events':[]})
return super(res_partner, self).copy(cr, uid, id, default, context)
def do_share(self, cr, uid, ids, *args):
return True
def onchange_type(self, cr, uid, ids, is_company, context=None):
value = {'title': False,
'photo': self._get_photo(cr, uid, is_company, context)}
if is_company:
value['parent_id'] = False
domain = {'title': [('domain', '=', 'partner')]}
else:
value['child_ids'] = [(5,)]
domain = {'title': [('domain', '=', 'contact')]}
return {'value': value, 'domain': domain}
def onchange_address(self, cr, uid, ids, use_parent_address, parent_id, context=None):
if use_parent_address and parent_id:
parent = self.browse(cr, uid, parent_id, context=context)
return {'value': dict((key, value_or_id(parent[key])) for key in ADDRESS_FIELDS)}
return {}
def _check_ean_key(self, cr, uid, ids, context=None):
for partner_o in pooler.get_pool(cr.dbname).get('res.partner').read(cr, uid, ids, ['ean13',]):
@@ -196,6 +243,35 @@ class res_partner(osv.osv):
# _constraints = [(_check_ean_key, 'Error: Invalid ean code', ['ean13'])]
def write(self, cr, uid, ids, vals, context=None):
# Update parent and siblings or children records
if isinstance(ids, (int, long)):
ids = [ids]
for partner in self.browse(cr, uid, ids, context=context):
update_ids = []
if partner.is_company:
domain_children = [('parent_id', '=', partner.id), ('use_parent_address', '=', True)]
update_ids = self.search(cr, uid, domain_children, context=context)
elif partner.parent_id:
domain_siblings = [('parent_id', '=', partner.parent_id.id), ('use_parent_address', '=', True)]
update_ids = [partner.parent_id.id] + self.search(cr, uid, domain_siblings, context=context)
self.update_address(cr, uid, update_ids, vals, context)
return super(res_partner,self).write(cr, uid, ids, vals, context=context)
def create(self, cr, uid, vals, context=None):
# Update parent and siblings records
if vals.get('parent_id') and vals.get('use_parent_address'):
domain_siblings = [('parent_id', '=', vals['parent_id']), ('use_parent_address', '=', True)]
update_ids = [vals['parent_id']] + self.search(cr, uid, domain_siblings, context=context)
self.update_address(cr, uid, update_ids, vals, context)
if 'photo' not in vals:
vals['photo'] = self._get_photo(cr, uid, vals.get('is_company', False), context)
return super(res_partner,self).create(cr, uid, vals, context=context)
def update_address(self, cr, uid, ids, vals, context=None):
addr_vals = dict((key, vals[key]) for key in POSTAL_ADDRESS_FIELDS if vals.get(key))
return super(res_partner, self).write(cr, uid, ids, addr_vals, context)
def name_get(self, cr, uid, ids, context=None):
if context is None:
context = {}
@@ -205,8 +281,13 @@ class res_partner(osv.osv):
rec_name = 'ref'
else:
rec_name = 'name'
res = [(r['id'], r[rec_name]) for r in self.read(cr, uid, ids, [rec_name], context)]
reads = self.read(cr, uid, ids, [rec_name,'parent_id'], context=context)
res = []
for record in reads:
name = record.get('name', '/')
if record['parent_id']:
name = "%s (%s)"%(name, record['parent_id'][1])
res.append((record['id'], name))
return res
def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=100):
@@ -215,6 +296,19 @@ class res_partner(osv.osv):
# short-circuit ref match when possible
if name and operator in ('=', 'ilike', '=ilike', 'like'):
ids = self.search(cr, uid, [('ref', '=', name)] + args, limit=limit, context=context)
if not ids:
names = map(lambda x : x.strip(),name.split('('))
for name in range(len(names)):
domain = [('name', operator, names[name])]
if name > 0:
domain.extend([('id', 'child_of', ids)])
ids = self.search(cr, uid, domain, limit=limit, context=context)
contact_ids = ids
while contact_ids:
contact_ids = self.search(cr, uid, [('parent_id', 'in', contact_ids)], limit=limit, context=context)
ids.extend(contact_ids)
if args:
ids = self.search(cr, uid, [('id', 'in', ids)] + args, limit=limit, context=context)
if ids:
return self.name_get(cr, uid, ids, context)
return super(res_partner,self).name_search(cr, uid, name, args, operator=operator, context=context, limit=limit)
@@ -222,9 +316,8 @@ class res_partner(osv.osv):
def _email_send(self, cr, uid, ids, email_from, subject, body, on_error=None):
partners = self.browse(cr, uid, ids)
for partner in partners:
if len(partner.address):
if partner.address[0].email:
tools.email_send(email_from, [partner.address[0].email], subject, body, on_error)
if partner.email:
tools.email_send(email_from, [partner.email], subject, body, on_error)
return True
def email_send(self, cr, uid, ids, email_from, subject, body, on_error=''):
@@ -232,7 +325,6 @@ class res_partner(osv.osv):
self.pool.get('ir.cron').create(cr, uid, {
'name': 'Send Partner Emails',
'user_id': uid,
# 'nextcall': False,
'model': 'res.partner',
'function': '_email_send',
'args': repr([ids[:16], email_from, subject, body, on_error])
@@ -243,20 +335,22 @@ class res_partner(osv.osv):
def address_get(self, cr, uid, ids, adr_pref=None):
if adr_pref is None:
adr_pref = ['default']
address_obj = self.pool.get('res.partner.address')
address_ids = address_obj.search(cr, uid, [('partner_id', 'in', ids)])
address_rec = address_obj.read(cr, uid, address_ids, ['type'])
res = list((addr['type'],addr['id']) for addr in address_rec)
adr = dict(res)
result = {}
# retrieve addresses from the partner itself and its children
res = []
# need to fix the ids ,It get False value in list like ids[False]
if ids and ids[0]!=False:
for p in self.browse(cr, uid, ids):
res.append((p.type, p.id))
res.extend((c.type, c.id) for c in p.child_ids)
addr = dict(reversed(res))
# get the id of the (first) default address if there is one,
# otherwise get the id of the first address in the list
default_address = False
if res:
default_address = adr.get('default', res[0][1])
else:
default_address = False
result = {}
for a in adr_pref:
result[a] = adr.get(a, default_address)
default_address = addr.get('default', res[0][1])
for adr in adr_pref:
result[adr] = addr.get(adr, default_address)
return result
def gen_next_ref(self, cr, uid, ids):
@@ -282,23 +376,60 @@ class res_partner(osv.osv):
if (not context.get('category_id', False)):
return False
return _('Partners: ')+self.pool.get('res.partner.category').browse(cr, uid, context['category_id'], context).name
def main_partner(self, cr, uid):
''' Return the id of the main partner
'''
model_data = self.pool.get('ir.model.data')
return model_data.browse(
cr, uid,
model_data.search(cr, uid, [('module','=','base'),
('name','=','main_partner')])[0],
).res_id
res_partner()
return model_data.browse(cr, uid,
model_data.search(cr, uid, [('module','=','base'),
('name','=','main_partner')])[0],
).res_id
def _display_address(self, cr, uid, address, type, context=None):
'''
The purpose of this function is to build and return an address formatted accordingly to the
standards of the country where it belongs.
:param address: browse record of the res.partner.address to format
:returns: the address formatted in a display that fit its country habits (or the default ones
if not country is specified)
:rtype: string
'''
if type:
if address.is_company and address.child_ids:
for child_id in address.child_ids:
if child_id.type == type:
address = child_id
# get the information that will be injected into the display format
# get the address format
address_format = address.country_id and address.country_id.address_format or \
'%(street)s\n%(street2)s\n%(city)s,%(state_code)s %(zip)s'
args = {
'state_code': address.state_id and address.state_id.code or '',
'state_name': address.state_id and address.state_id.name or '',
'country_code': address.country_id and address.country_id.code or '',
'country_name': address.country_id and address.country_id.name or '',
'company_name': address.parent_id and address.parent_id.name or '',
}
address_field = ['title', 'street', 'street2', 'zip', 'city']
for field in address_field :
args[field] = getattr(address, field) or ''
return address_format % args
# res.partner.address is deprecated; it is still there for backward compability only
class res_partner_address(osv.osv):
_description ='Partner Addresses'
_table = "res_partner"
_name = 'res.partner.address'
_order = 'type, name'
_columns = {
'partner_id': fields.many2one('res.partner', 'Partner Name', ondelete='set null', select=True, help="Keep empty for a private address, not related to partner."),
'parent_id': fields.many2one('res.partner', 'Company', ondelete='set null', select=True),
'partner_id': fields.related('parent_id', type='many2one', relation='res.partner', string='Partner'), # for backward compatibility
'type': fields.selection( [ ('default','Default'),('invoice','Invoice'), ('delivery','Delivery'), ('contact','Contact'), ('other','Other') ],'Address Type', help="Used to select automatically the right address according to the context in sales and purchases documents."),
'function': fields.char('Function', size=128),
'title': fields.many2one('res.partner.title','Title'),
@@ -317,83 +448,16 @@ class res_partner_address(osv.osv):
'is_customer_add': fields.related('partner_id', 'customer', type='boolean', string='Customer'),
'is_supplier_add': fields.related('partner_id', 'supplier', type='boolean', string='Supplier'),
'active': fields.boolean('Active', help="Uncheck the active field to hide the contact."),
# 'company_id': fields.related('partner_id','company_id',type='many2one',relation='res.company',string='Company', store=True),
'company_id': fields.many2one('res.company', 'Company',select=1),
'color': fields.integer('Color Index'),
}
_defaults = {
'active': lambda *a: 1,
'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'res.partner.address', context=c),
}
def name_get(self, cr, user, ids, context=None):
if context is None:
context = {}
if not len(ids):
return []
res = []
for r in self.read(cr, user, ids, ['name','zip','country_id', 'city','partner_id', 'street']):
if context.get('contact_display', 'contact')=='partner' and r['partner_id']:
res.append((r['id'], r['partner_id'][1]))
else:
# make a comma-separated list with the following non-empty elements
elems = [r['name'], r['country_id'] and r['country_id'][1], r['city'], r['street']]
addr = ', '.join(filter(bool, elems))
if (context.get('contact_display', 'contact')=='partner_address') and r['partner_id']:
res.append((r['id'], "%s: %s" % (r['partner_id'][1], addr or '/')))
else:
res.append((r['id'], addr or '/'))
return res
def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=100):
if not args:
args=[]
if not context:
context={}
if context.get('contact_display', 'contact')=='partner ' or context.get('contact_display', 'contact')=='partner_address ' :
ids = self.search(cr, user, [('partner_id',operator,name)], limit=limit, context=context)
else:
if not name:
ids = self.search(cr, user, args, limit=limit, context=context)
else:
ids = self.search(cr, user, [('zip','=',name)] + args, limit=limit, context=context)
if not ids:
ids = self.search(cr, user, [('city',operator,name)] + args, limit=limit, context=context)
if name:
ids += self.search(cr, user, [('name',operator,name)] + args, limit=limit, context=context)
ids += self.search(cr, user, [('partner_id',operator,name)] + args, limit=limit, context=context)
return self.name_get(cr, user, ids, context=context)
def get_city(self, cr, uid, id):
return self.browse(cr, uid, id).city
def _display_address(self, cr, uid, address, context=None):
'''
The purpose of this function is to build and return an address formatted accordingly to the
standards of the country where it belongs.
:param address: browse record of the res.partner.address to format
:returns: the address formatted in a display that fit its country habits (or the default ones
if not country is specified)
:rtype: string
'''
# get the address format
address_format = address.country_id and address.country_id.address_format or \
'%(street)s\n%(street2)s\n%(city)s,%(state_code)s %(zip)s'
# get the information that will be injected into the display format
args = {
'state_code': address.state_id and address.state_id.code or '',
'state_name': address.state_id and address.state_id.name or '',
'country_code': address.country_id and address.country_id.code or '',
'country_name': address.country_id and address.country_id.name or '',
}
address_field = ['title', 'street', 'street2', 'zip', 'city']
for field in address_field :
args[field] = getattr(address, field) or ''
return address_format % args
res_partner_address()
def write(self, cr, uid, ids, vals, context=None):
logging.getLogger('res.partner').warning("Deprecated use of res.partner.address")
return super(res_partner_address,self).write(cr, uid, ids, vals, context=context)
def create(self, cr, uid, vals, context=None):
logging.getLogger('res.partner').warning("Deprecated use of res.partner.address")
return super(res_partner_address,self).create(cr, uid, vals, context=context)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+115 -107
View File
@@ -93,53 +93,56 @@
<field eval="[(6, 0, [ref('res_partner_category_9')])]" name="category_id"/>
<field name="supplier">1</field>
<field eval="0" name="customer"/>
<field name="address" eval="[]"/>
<field name="is_company">1</field>
<field name="website">www.asustek.com</field>
</record>
<record id="res_partner_agrolait" model="res.partner">
<field name="name">Agrolait</field>
<field eval="[(6, 0, [ref('base.res_partner_category_0')])]" name="category_id"/>
<field name="address" eval="[]"/>
<field name="is_company">1</field>
<field name="website">www.agrolait.com</field>
</record>
<record id="res_partner_c2c" model="res.partner">
<field name="name">Camptocamp</field>
<field name="is_company">1</field>
<field eval="[(6, 0, [ref('res_partner_category_10'), ref('res_partner_category_5')])]" name="category_id"/>
<field name="supplier">1</field>
<field name="address" eval="[]"/>
<field name="is_company">1</field>
<field name="website">www.camptocamp.com</field>
</record>
<record id="res_partner_sednacom" model="res.partner">
<field name="website">www.syleam.fr</field>
<field name="name">Syleam</field>
<field eval="[(6, 0, [ref('res_partner_category_5')])]" name="category_id"/>
<field name="address" eval="[]"/>
<field name="is_company">1</field>
</record>
<record id="res_partner_thymbra" model="res.partner">
<field name="name">SmartBusiness</field>
<field name="is_company">1</field>
<field eval="[(6, 0, [ref('res_partner_category_4')])]" name="category_id"/>
</record>
<record id="res_partner_desertic_hispafuentes" model="res.partner">
<field name="name">Axelor</field>
<field eval="[(6, 0, [ref('res_partner_category_4')])]" name="category_id"/>
<field name="supplier">1</field>
<field name="address" eval="[]"/>
<field name="is_company">1</field>
<field name="website">www.axelor.com/</field>
</record>
<record id="res_partner_tinyatwork" model="res.partner">
<field name="name">Tiny AT Work</field>
<field name="is_company">1</field>
<field eval="[(6, 0, [ref('res_partner_category_5'), ref('res_partner_category_10')])]" name="category_id"/>
<field name="website">www.tinyatwork.com/</field>
</record>
<record id="res_partner_2" model="res.partner">
<field name="name">Bank Wealthy and sons</field>
<field name="address" eval="[]"/>
<field name="is_company">1</field>
<field name="website">www.wealthyandsons.com/</field>
</record>
<record id="res_partner_3" model="res.partner">
<field name="name">China Export</field>
<field eval="[(6, 0, [ref('res_partner_category_9')])]" name="category_id"/>
<field name="address" eval="[]"/>
<field name="is_company">1</field>
<field name="website">www.chinaexport.com/</field>
</record>
<record id="res_partner_4" model="res.partner">
@@ -147,13 +150,13 @@
<field eval="[(6, 0, [ref('res_partner_category_9')])]" name="category_id"/>
<field name="supplier">1</field>
<field eval="0" name="customer"/>
<field name="address" eval="[]"/>
<field name="is_company">1</field>
<field name="website">www.distribpc.com/</field>
</record>
<record id="res_partner_5" model="res.partner">
<field name="name">Ecole de Commerce de Liege</field>
<field eval="[(6, 0, [ref('res_partner_category_1')])]" name="category_id"/>
<field name="address" eval="[]"/>
<field name="is_company">1</field>
<field name="website">www.eci-liege.info//</field>
</record>
<record id="res_partner_6" model="res.partner">
@@ -161,7 +164,7 @@
<field eval="[(6, 0, [ref('res_partner_category_9')])]" name="category_id"/>
<field name="supplier">1</field>
<field eval="0" name="customer"/>
<field name="address" eval="[]"/>
<field name="is_company">1</field>
</record>
<record id="res_partner_maxtor" model="res.partner">
<field name="name">Maxtor</field>
@@ -169,20 +172,20 @@
<field eval="[(6, 0, [ref('res_partner_category_9')])]" name="category_id"/>
<field name="supplier">1</field>
<field eval="0" name="customer"/>
<field name="address" eval="[]"/>
<field name="is_company">1</field>
</record>
<record id="res_partner_seagate" model="res.partner">
<field name="name">Seagate</field>
<field eval="5000.00" name="credit_limit"/>
<field eval="[(6, 0, [ref('res_partner_category_9')])]" name="category_id"/>
<field name="supplier">1</field>
<field name="address" eval="[]"/>
<field name="is_company">1</field>
</record>
<record id="res_partner_8" model="res.partner">
<field name="website">http://mediapole.net</field>
<field name="name">Mediapole SPRL</field>
<field eval="[(6, 0, [ref('res_partner_category_1')])]" name="category_id"/>
<field name="address" eval="[]"/>
<field name="is_company">1</field>
</record>
<record id="res_partner_9" model="res.partner">
<field name="website">www.balmerinc.com</field>
@@ -190,40 +193,38 @@
<field eval="12000.00" name="credit_limit"/>
<field name="ref">or</field>
<field eval="[(6, 0, [ref('res_partner_category_1')])]" name="category_id"/>
<field name="address" eval="[]"/>
<field name="is_company">1</field>
</record>
<record id="res_partner_10" model="res.partner">
<field name="name">Tecsas</field>
<field name="ean13">3020170000003</field>
<field eval="[(6, 0, [ref('res_partner_category_9')])]" name="category_id"/>
<field name="address" eval="[]"/>
<field name="is_company">1</field>
</record>
<record id="res_partner_11" model="res.partner">
<field name="name">Leclerc</field>
<field eval="1200.00" name="credit_limit"/>
<field eval="[(6, 0, [ref('res_partner_category_0')])]" name="category_id"/>
<field name="address" eval="[]"/>
<field name="is_company">1</field>
</record>
<record id="res_partner_14" model="res.partner">
<field name="name">Centrale d'achats BML</field>
<field name="ean13">3020178572427</field>
<field eval="15000.00" name="credit_limit"/>
<field name="parent_id" ref="res_partner_10"/>
<field eval="[(6, 0, [ref('res_partner_category_11')])]" name="category_id"/>
<field name="address" eval="[]"/>
<field name="is_company">1</field>
</record>
<record id="res_partner_15" model="res.partner">
<field name="name">Magazin BML 1</field>
<field name="ean13">3020178570171</field>
<field name="parent_id" ref="res_partner_14"/>
<field eval="1500.00" name="credit_limit"/>
<field eval="[(6, 0, [ref('res_partner_category_11')])]" name="category_id"/>
<field name="address" eval="[]"/>
<field name="is_company">1</field>
</record>
<record id="res_partner_accent" model="res.partner">
<field name="name">Université de Liège</field>
<field eval="[(6, 0, [ref('res_partner_category_9')])]" name="category_id"/>
<field name="address" eval="[]"/>
<field name="is_company">1</field>
<field name="website">http://www.ulg.ac.be/</field>
</record>
@@ -234,35 +235,40 @@
<record id="res_partner_duboissprl0" model="res.partner">
<field eval="'Sprl Dubois would like to sell our bookshelves but they have no storage location, so it would be exclusively on order'" name="comment"/>
<field name="name">Dubois sprl</field>
<field name="address" eval="[]"/>
<field name="is_company">1</field>
<field name="website">http://www.dubois.be/</field>
</record>
<record id="res_partner_ericdubois0" model="res.partner">
<field name="name">Eric Dubois</field>
<field name="is_company">1</field>
<field name="address" eval="[]"/>
</record>
<record id="res_partner_fabiendupont0" model="res.partner">
<field name="name">Fabien Dupont</field>
<field name="is_company">1</field>
<field name="address" eval="[]"/>
</record>
<record id="res_partner_lucievonck0" model="res.partner">
<field name="name">Lucie Vonck</field>
<field name="address" eval="[]"/>
<field eval="'Grand-Rosière'" name="city"/>
<field eval="'1367'" name="zip"/>
<field name="country_id" ref="base.be"/>
<field eval="'Chaussée de Namur'" name="street"/>
</record>
<record id="res_partner_notsotinysarl0" model="res.partner">
<!--record id="res_partner_notsotinysarl0" model="res.partner">
<field name="name">NotSoTiny SARL</field>
<field name="address" eval="[]"/>
<field name="website">notsotiny.be</field>
</record>
</record-->
<record id="res_partner_theshelvehouse0" model="res.partner">
<field name="name">The Shelve House</field>
<field eval="[(6,0,[ref('res_partner_category_retailers0')])]" name="category_id"/>
<field name="address" eval="[]"/>
<field name="is_company">1</field>
</record>
<record id="res_partner_vickingdirect0" model="res.partner">
@@ -270,7 +276,7 @@
<field eval="[(6,0,[ref('res_partner_category_miscellaneoussuppliers0')])]" name="category_id"/>
<field name="supplier">1</field>
<field name="customer">0</field>
<field name="address" eval="[]"/>
<field name="is_company">1</field>
<field name="website">vicking-direct.be</field>
</record>
@@ -279,14 +285,14 @@
<field eval="[(6,0,[ref('res_partner_category_woodsuppliers0')])]" name="category_id"/>
<field name="supplier">1</field>
<field eval="0" name="customer"/>
<field name="address" eval="[]"/>
<field name="is_company">1</field>
<field name="website">woodywoodpecker.com</field>
</record>
<record id="res_partner_zerooneinc0" model="res.partner">
<field name="name">ZeroOne Inc</field>
<field eval="[(6,0,[ref('res_partner_category_consumers0')])]" name="category_id"/>
<field name="address" eval="[]"/>
<field name="is_company">1</field>
<field name="website">http://www.zerooneinc.com/</field>
</record>
@@ -294,7 +300,7 @@
Resource: res.partner.address
-->
<record id="res_partner_address_1" model="res.partner.address">
<record id="res_partner_address_1" model="res.partner">
<field name="city">Bruxelles</field>
<field name="name">Michel Schumacher</field>
<field name="zip">1000</field>
@@ -303,9 +309,9 @@
<field name="phone">(+32)2 211 34 83</field>
<field name="street">Rue des Palais 51, bte 33</field>
<field name="type">default</field>
<field name="partner_id" ref="res_partner_9"/>
<field name="parent_id" ref="res_partner_9"/>
</record>
<record id="res_partner_address_2" model="res.partner.address">
<record id="res_partner_address_2" model="res.partner">
<field name="city">Avignon CEDEX 09</field>
<field name="name">Laurent Jacot</field>
<field name="zip">84911</field>
@@ -314,9 +320,9 @@
<field name="phone">(+33)4.32.74.10.57</field>
<field name="street">85 rue du traite de Rome</field>
<field name="type">default</field>
<field name="partner_id" ref="res_partner_10"/>
<field name="parent_id" ref="res_partner_10"/>
</record>
<record id="res_partner_address_3000" model="res.partner.address">
<record id="res_partner_address_3000" model="res.partner">
<field name="city">Champs sur Marne</field>
<field name="name">Laith Jubair</field>
<field name="zip">77420</field>
@@ -325,18 +331,18 @@
<field name="phone">+33 1 64 61 04 01</field>
<field name="street">12 rue Albert Einstein</field>
<field name="type">default</field>
<field name="partner_id" ref="res_partner_desertic_hispafuentes"/>
<field name="parent_id" ref="res_partner_desertic_hispafuentes"/>
</record>
<record id="res_partner_address_3" model="res.partner.address">
<record id="res_partner_address_3" model="res.partner">
<field name="city">Louvain-la-Neuve</field>
<field name="name">Thomas Passot</field>
<field name="zip">1348</field>
<field model="res.country" name="country_id" search="[('name','=','Belgium')]"/>
<field name="phone">(+32).10.45.17.73</field>
<field name="street">Rue de l'Angelique, 1</field>
<field name="partner_id" ref="res_partner_8"/>
<field name="parent_id" ref="res_partner_8"/>
</record>
<record id="res_partner_address_tang" model="res.partner.address">
<record id="res_partner_address_tang" model="res.partner">
<field name="city">Taiwan</field>
<field name="name">Tang</field>
<field name="zip">23410</field>
@@ -345,9 +351,9 @@
<field name="email">info@asustek.com</field>
<field name="phone">+ 1 64 61 04 01</field>
<field name="type">default</field>
<field name="partner_id" ref="res_partner_asus"/>
<field name="parent_id" ref="res_partner_asus"/>
</record>
<record id="res_partner_address_wong" model="res.partner.address">
<record id="res_partner_address_wong" model="res.partner">
<field name="city">Hong Kong</field>
<field name="name">Wong</field>
<field name="zip">23540</field>
@@ -356,9 +362,9 @@
<field name="email">info@maxtor.com</field>
<field name="phone">+ 11 8528 456 789</field>
<field name="type">default</field>
<field name="partner_id" ref="res_partner_maxtor"/>
<field name="parent_id" ref="res_partner_maxtor"/>
</record>
<record id="res_partner_address_6" model="res.partner.address">
<record id="res_partner_address_6" model="res.partner">
<field name="city">Brussels</field>
<field name="name">Etienne Lacarte</field>
<field name="zip">2365</field>
@@ -367,9 +373,9 @@
<field name="type">default</field>
<field name="email">info@elecimport.com</field>
<field name="phone">+ 32 025 897 456</field>
<field name="partner_id" ref="res_partner_6"/>
<field name="parent_id" ref="res_partner_6"/>
</record>
<record id="res_partner_address_7" model="res.partner.address">
<record id="res_partner_address_7" model="res.partner">
<field name="city">Namur</field>
<field name="name">Jean Guy Lavente</field>
<field name="zip">2541</field>
@@ -378,9 +384,9 @@
<field name="type">default</field>
<field name="email">info@distribpc.com</field>
<field name="phone">+ 32 081256987</field>
<field name="partner_id" ref="res_partner_4"/>
<field name="parent_id" ref="res_partner_4"/>
</record>
<record id="res_partner_address_8" model="res.partner.address">
<record id="res_partner_address_8" model="res.partner">
<field name="city">Wavre</field>
<field name="name">Sylvie Lelitre</field>
<field name="zip">5478</field>
@@ -389,10 +395,10 @@
<field name="type">default</field>
<field name="email">s.l@agrolait.be</field>
<field name="phone">003281588558</field>
<field name="partner_id" ref="res_partner_agrolait"/>
<field name="parent_id" ref="res_partner_agrolait"/>
<field name="title" ref="base.res_partner_title_madam"/>
</record>
<record id="res_partner_address_8delivery" model="res.partner.address">
<record id="res_partner_address_8delivery" model="res.partner">
<field name="city">Wavre</field>
<field name="name">Paul Lelitre</field>
<field name="zip">5478</field>
@@ -401,10 +407,10 @@
<field name="type">delivery</field>
<field name="email">p.l@agrolait.be</field>
<field name="phone">003281588557</field>
<field name="partner_id" ref="res_partner_agrolait"/>
<field name="parent_id" ref="res_partner_agrolait"/>
<field name="title" ref="base.res_partner_title_sir"/>
</record>
<record id="res_partner_address_8invoice" model="res.partner.address">
<record id="res_partner_address_8invoice" model="res.partner">
<field name="city">Wavre</field>
<field name="name">Serge Lelitre</field>
<field name="zip">5478</field>
@@ -413,10 +419,10 @@
<field name="type">invoice</field>
<field name="email">serge.l@agrolait.be</field>
<field name="phone">003281588556</field>
<field name="partner_id" ref="res_partner_agrolait"/>
<field name="parent_id" ref="res_partner_agrolait"/>
<field name="title" ref="base.res_partner_title_sir"/>
</record>
<record id="res_partner_address_9" model="res.partner.address">
<record id="res_partner_address_9" model="res.partner">
<field name="city">Paris</field>
<field name="name">Arthur Grosbonnet</field>
<field name="zip">75016</field>
@@ -425,10 +431,10 @@
<field name="type">default</field>
<field name="email">a.g@wealthyandsons.com</field>
<field name="phone">003368978776</field>
<field name="partner_id" ref="res_partner_2"/>
<field name="parent_id" ref="res_partner_2"/>
<field name="title" ref="base.res_partner_title_sir"/>
</record>
<record id="res_partner_address_11" model="res.partner.address">
<record id="res_partner_address_11" model="res.partner">
<field name="city">Alencon</field>
<field name="name">Sebastien LANGE</field>
<field name="zip">61000</field>
@@ -437,9 +443,9 @@
<field name="phone">+33 (0) 2 33 31 22 10</field>
<field model="res.country" name="country_id" search="[('name','=','France')]"/>
<field name="type">default</field>
<field name="partner_id" ref="res_partner_sednacom"/>
<field name="parent_id" ref="res_partner_sednacom"/>
</record>
<record id="res_partner_address_10" model="res.partner.address">
<record id="res_partner_address_10" model="res.partner">
<field name="city">Liege</field>
<field name="name">Karine Lesbrouffe</field>
<field name="zip">6985</field>
@@ -448,9 +454,9 @@
<field name="email">k.lesbrouffe@eci-liege.info</field>
<field name="phone">+32 421 52571</field>
<field name="type">default</field>
<field name="partner_id" ref="res_partner_5"/>
<field name="parent_id" ref="res_partner_5"/>
</record>
<record id="res_partner_address_zen" model="res.partner.address">
<record id="res_partner_address_zen" model="res.partner">
<field name="city">Shanghai</field>
<field name="name">Zen</field>
<field name="zip">478552</field>
@@ -459,9 +465,9 @@
<field name="type">default</field>
<field name="email">zen@chinaexport.com</field>
<field name="phone">+86-751-64845671</field>
<field name="partner_id" ref="res_partner_3"/>
<field name="parent_id" ref="res_partner_3"/>
</record>
<record id="res_partner_address_12" model="res.partner.address">
<record id="res_partner_address_12" model="res.partner">
<field name="type">default</field>
<field name="city">Grenoble</field>
<field name="name">Loïc Dupont</field>
@@ -471,9 +477,9 @@
<field name="type">default</field>
<field name="email">l.dupont@tecsas.fr</field>
<field name="phone">+33-658-256545</field>
<field name="partner_id" ref="res_partner_10"/>
<field name="parent_id" ref="res_partner_10"/>
</record>
<record id="res_partner_address_13" model="res.partner.address">
<record id="res_partner_address_13" model="res.partner">
<field name="type">default</field>
<field name="name">Carl François</field>
<field name="city">Bruxelles</field>
@@ -482,9 +488,9 @@
<field name="street">89 Chaussée de Waterloo</field>
<field name="email">carl.françois@bml.be</field>
<field name="phone">+32-258-256545</field>
<field name="partner_id" ref="res_partner_14"/>
<field name="parent_id" ref="res_partner_14"/>
</record>
<record id="res_partner_address_14" model="res.partner.address">
<record id="res_partner_address_14" model="res.partner">
<field name="type">default</field>
<field name="name">Lucien Ferguson</field>
<field name="street">89 Chaussée de Liège</field>
@@ -492,9 +498,9 @@
<field name="zip">5000</field>
<field name="email">lucien.ferguson@bml.be</field>
<field name="phone">+32-621-568978</field>
<field name="partner_id" ref="res_partner_15"/>
<field name="parent_id" ref="res_partner_15"/>
</record>
<record id="res_partner_address_15" model="res.partner.address">
<record id="res_partner_address_15" model="res.partner">
<field name="type">default</field>
<field name="name">Marine Leclerc</field>
<field name="street">rue Grande</field>
@@ -502,9 +508,9 @@
<field name="zip">29200</field>
<field name="email">marine@leclerc.fr</field>
<field name="phone">+33-298.334558</field>
<field name="partner_id" ref="res_partner_11"/>
<field name="parent_id" ref="res_partner_11"/>
</record>
<record id="res_partner_address_16" model="res.partner.address">
<record id="res_partner_address_16" model="res.partner">
<field name="type">invoice</field>
<field name="name">Claude Leclerc</field>
<field name="street">rue Grande</field>
@@ -512,10 +518,10 @@
<field name="zip">29200</field>
<field name="email">claude@leclerc.fr</field>
<field name="phone">+33-298.334598</field>
<field name="partner_id" ref="res_partner_11"/>
<field name="parent_id" ref="res_partner_11"/>
</record>
<record id="res_partner_address_accent" model="res.partner.address">
<record id="res_partner_address_accent" model="res.partner">
<field name="type">default</field>
<field name="name">Martine Ohio</field>
<field name="street">Place du 20Août</field>
@@ -523,9 +529,9 @@
<field name="zip">4000</field>
<field name="email">martine.ohio@ulg.ac.be</field>
<field name="phone">+32-45895245</field>
<field name="partner_id" ref="res_partner_accent"/>
<field name="parent_id" ref="res_partner_accent"/>
</record>
<record id="res_partner_address_Camptocamp" model="res.partner.address">
<record id="res_partner_address_Camptocamp" model="res.partner">
<field name="city">Lausanne</field>
<field name="name">Luc Maurer</field>
<field name="zip">1015</field>
@@ -533,9 +539,9 @@
<field model="res.country" name="country_id" search="[('name','=','Switzerland')]"/>
<field name="street">PSE-A, EPFL </field>
<field name="type">default</field>
<field name="partner_id" ref="res_partner_c2c"/>
<field name="parent_id" ref="res_partner_c2c"/>
</record>
<record id="res_partner_address_seagate" model="res.partner.address">
<record id="res_partner_address_seagate" model="res.partner">
<field name="city">Cupertino</field>
<field name="name">Seagate Technology</field>
<field name="zip">95014</field>
@@ -544,9 +550,9 @@
<field name="email">info@seagate.com</field>
<field name="phone">+1 408 256987</field>
<field name="type">default</field>
<field name="partner_id" ref="res_partner_seagate"/>
<field name="parent_id" ref="res_partner_seagate"/>
</record>
<record id="res_partner_address_thymbra" model="res.partner.address">
<record id="res_partner_address_thymbra" model="res.partner">
<field name="city">Buenos Aires</field>
<field name="name">Jack Daniels</field>
<field name="zip">1659</field>
@@ -556,9 +562,9 @@
<field name="phone">(5411) 4773-9666 </field>
<field model="res.country" name="country_id" search="[('name','=','Argentina')]"/>
<field name="type">default</field>
<field name="partner_id" ref="res_partner_thymbra"/>
<field name="parent_id" ref="res_partner_thymbra"/>
</record>
<record id="res_partner_address_tinyatwork" model="res.partner.address">
<record id="res_partner_address_tinyatwork" model="res.partner">
<field name="city">Boston</field>
<field name="name">Tiny Work</field>
<field name="zip">5501</field>
@@ -567,18 +573,18 @@
<field model="res.country" name="country_id" search="[('name','=','United States')]"/>
<field name="street">One Lincoln Street</field>
<field name="type">default</field>
<field name="partner_id" ref="res_partner_tinyatwork"/>
<field name="parent_id" ref="res_partner_tinyatwork"/>
</record>
<!--
Resource: res.partner.address for Training
-->
<record id="res_partner_address_notsotinysarl0" model="res.partner.address">
<record id="res_partner_address_notsotinysarl0" model="res.partner">
<field eval="'Namur'" name="city"/>
<field eval="'NotSoTiny SARL'" name="name"/>
<field eval="'5000'" name="zip"/>
<field name="partner_id" ref="res_partner_notsotinysarl0"/>
<!--field name="parent_id" ref="res_partner_notsotinysarl0"/-->
<field name="country_id" ref="base.be"/>
<field eval="'(+32).81.81.37.00'" name="phone"/>
<field eval="'Rue du Nid 1'" name="street"/>
@@ -586,42 +592,43 @@
</record>
<record id="res_partner_address_henrychard0" model="res.partner.address">
<!--record id="res_partner_address_henrychard0" model="res.partner">
<field eval="'Paris'" name="city"/>
<field eval="'Henry Chard'" name="name"/>
<field name="country_id" ref="base.fr"/>
</record>
</record-->
<record id="res_partner_address_geoff0" model="res.partner.address">
<!--record id="res_partner_address_geoff0" model="res.partner">
<field eval="'Brussels'" name="city"/>
<field eval="'Geoff'" name="name"/>
<field name="country_id" ref="base.be"/>
</record>
</record-->
<record id="res_partner_address_rogerpecker0" model="res.partner.address">
<!--record id="res_partner_address_rogerpecker0" model="res.partner">
<field eval="'Kainuu'" name="city"/>
<field eval="'Roger Pecker'" name="name"/>
<field name="country_id" ref="base.fi"/>
</record>
</record-->
<record id="res_partner_address_0" model="res.partner.address">
<record id="res_partner_address_0" model="res.partner">
<field name="name">Brus</field>
<field eval="'Brussels'" name="city"/>
<field name="country_id" ref="base.be"/>
</record>
<record id="res_partner_address_henrychard1" model="res.partner.address">
<record id="res_partner_address_henrychard1" model="res.partner">
<field eval="'Paris'" name="city"/>
<field eval="'Henry Chard'" name="name"/>
<field name="partner_id" ref="res_partner_theshelvehouse0"/>
<field name="parent_id" ref="res_partner_theshelvehouse0"/>
<field name="country_id" ref="base.fr"/>
</record>
<record id="res_partner_address_brussels0" model="res.partner.address">
<record id="res_partner_address_brussels0" model="res.partner">
<field eval="'Brussels'" name="city"/>
<field eval="'Leen Vandenloep'" name="name"/>
<field eval="'Puurs'" name="city"/>
@@ -629,54 +636,54 @@
<field name="country_id" ref="base.be"/>
<field eval="'(+32).70.12.85.00'" name="phone"/>
<field eval="'Schoonmansveld 28'" name="street"/>
<field name="partner_id" ref="res_partner_vickingdirect0"/>
<field name="parent_id" ref="res_partner_vickingdirect0"/>
<field name="country_id" ref="base.be"/>
</record>
<record id="res_partner_address_rogerpecker1" model="res.partner.address">
<record id="res_partner_address_rogerpecker1" model="res.partner">
<field eval="'Kainuu'" name="city"/>
<field eval="'Roger Pecker'" name="name"/>
<field name="partner_id" ref="res_partner_woodywoodpecker0"/>
<field name="parent_id" ref="res_partner_woodywoodpecker0"/>
<field eval="'(+358).9.589 689'" name="phone"/>
<field name="country_id" ref="base.fi"/>
</record>
<record id="res_partner_address_geoff1" model="res.partner.address">
<record id="res_partner_address_geoff1" model="res.partner">
<field eval="'Brussels'" name="city"/>
<field eval="'Geoff'" name="name"/>
<field name="partner_id" ref="res_partner_zerooneinc0"/>
<field name="parent_id" ref="res_partner_zerooneinc0"/>
<field name="country_id" ref="base.be"/>
</record>
<record id="res_partner_address_marcdubois0" model="res.partner.address">
<record id="res_partner_address_marcdubois0" model="res.partner">
<field eval="'Brussels'" name="city"/>
<field eval="'Marc Dubois'" name="name"/>
<field eval="'1000'" name="zip"/>
<field name="partner_id" ref="res_partner_duboissprl0"/>
<field name="parent_id" ref="res_partner_duboissprl0"/>
<field name="country_id" ref="base.be"/>
<field eval="'Avenue de la Liberté 56'" name="street"/>
<field eval="'m.dubois@dubois.be'" name="email"/>
</record>
<record id="res_partner_address_fabiendupont0" model="res.partner.address">
<record id="res_partner_address_fabiendupont0" model="res.partner">
<field eval="'Namur'" name="city"/>
<field eval="'Fabien Dupont'" name="name"/>
<field eval="'5000'" name="zip"/>
<field name="partner_id" ref="res_partner_fabiendupont0"/>
<field name="parent_id" ref="res_partner_fabiendupont0"/>
<field name="country_id" ref="base.be"/>
<field eval="'Blvd Kennedy, 13'" name="street"/>
</record>
<record id="res_partner_address_ericdubois0" model="res.partner.address">
<record id="res_partner_address_ericdubois0" model="res.partner">
<field eval="'Mons'" name="city"/>
<field eval="'Eric Dubois'" name="name"/>
<field eval="'7000'" name="zip"/>
<field name="partner_id" ref="res_partner_ericdubois0"/>
<field name="parent_id" ref="res_partner_ericdubois0"/>
<field name="country_id" ref="base.be"/>
<field eval="'Chaussée de Binche, 27'" name="street"/>
<field eval="'e.dubois@gmail.com'" name="email"/>
@@ -684,23 +691,24 @@
</record>
<record id="res_partner_address_notsotinysarl1" model="res.partner.address">
<record id="res_partner_address_notsotinysarl1" model="res.partner">
<field eval="'Antwerpen'" name="city"/>
<field eval="'NotSoTiny'" name="name"/>
<field eval="'2000'" name="zip"/>
<field name="partner_id" ref="res_partner_notsotinysarl0"/>
<field name="parent_id" ref="res_partner_address_notsotinysarl0"/>
<field name="country_id" ref="base.be"/>
<field eval="'Antwerpsesteenweg 254'" name="street"/>
<field eval="'invoice'" name="type"/>
</record>
<record id="res_partner_address_4" model="res.partner.address">
<!--record id="res_partner_address_4" model="res.partner">
<field eval="'Grand-Rosière'" name="city"/>
<field eval="'1367'" name="zip"/>
<field name="partner_id" ref="res_partner_lucievonck0"/>
<field name="parent_id" ref="res_partner_lucievonck0"/>
<field name="country_id" ref="base.be"/>
<field eval="'Chaussée de Namur'" name="street"/>
</record>
</record-->
<!--
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<report id="res_partner_address_report" model="res.partner" name="res.partner.address" string="Labels" xml="base/res/report/partner_address.xml" xsl="base/res/report/partner_address.xsl" groups="base.group_extended"/>
<report id="res_partner_address_report" model="res.partner" name="res.partner" string="Labels" xml="base/res/report/partner_address.xml" xsl="base/res/report/partner_address.xsl" groups="base.group_extended"/>
<!--
<report string="Business Cards" model="res.partner" name="res.partner.businesscard" xml="base/res/report/business_card.xml" xsl="base/res/report/business_card.xsl"/>
-->
+192 -112
View File
@@ -309,14 +309,14 @@
<field name="type">tree</field>
<field eval="8" name="priority"/>
<field name="arch" type="xml">
<tree string="Partners">
<tree string="Contacts">
<field name="name"/>
<field name="ref" groups="base.group_extended"/>
<field name="function" invisible="1"/>
<field name="phone"/>
<field name="email"/>
<field name="city"/>
<field name="country"/>
<field name="user_id"/>
<field name="user_id" invisible="1"/>
<field name="is_company" invisible="1"/>
<field name="country_id" invisible="1"/>
</tree>
</field>
</record>
@@ -326,76 +326,145 @@
<field name="model">res.partner</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Partners" col='1'>
<group col="6" colspan="4">
<group colspan="5" col="6">
<field name="name" select="1"/>
<field name="ref" groups="base.group_extended"/>
<field domain="[('domain', '=', 'partner')]" name="title" size="0" groups="base.group_extended"/>
<field name="lang"/>
<form string="Partners">
<group col="8" colspan="4">
<group col="4" colspan="4">
<field name="name" required="1"/>
<field name="title" size="0" groups="base.group_extended" domain="[('domain', '=', 'contact')]"/>
<newline/>
<field name="function" attrs="{'invisible': [('is_company', '=', True)]}" colspan="4"/>
<field name="parent_id" string="Company" colspan="4" attrs="{'invisible': [('is_company','=', True)]}"
domain="[('is_company', '=', True)]" context="{'default_is_company': True}"
on_change="onchange_address(use_parent_address, parent_id)"/>
</group>
<group colspan="1" col="2">
<field name="customer" select="1"/>
<group col="2">
<field name="is_company" on_change="onchange_type(is_company)"/>
</group>
<group col="2">
<field name="customer"/>
<field name="supplier"/>
<!-- <field name="employee"/>-->
</group>
<group col="2">
<field name="photo" widget='image' nolabel="1"/>
</group>
</group>
<notebook colspan="4">
<page string="General">
<field colspan="4" mode="form,tree" name="address" nolabel="1" select="1" height="260">
<form string="Partner Contacts">
<group colspan="4" col="6">
<field name="name" string="Contact Name"/>
<field domain="[('domain', '=', 'contact')]" name="title" size="0"/>
<field name="function"/>
<group colspan="2">
<separator string="Address" colspan="4"/>
<field name="type" string="Type" attrs="{'invisible': [('is_company','=', True)]}"/>
<group colspan="2">
<field name="use_parent_address" attrs="{'invisible': [('parent_id', '=', False)]}"
on_change="onchange_address(use_parent_address, parent_id)"/>
</group>
<newline/>
<field name="street" colspan="4"/>
<field name="street2" colspan="4"/>
<field name="zip"/>
<field name="city"/>
<field name="country_id"/>
<field name="state_id"/>
</group>
<group colspan="2">
<separator string="Communication" colspan="4"/>
<field name="lang" colspan="4"/>
<field name="phone" colspan="4"/>
<field name="mobile" colspan="4"/>
<field name="fax" colspan="4"/>
<field name="email" widget="email" colspan="4"/>
<field name="website" widget="url" colspan="4"/>
<field name="ref" groups="base.group_extended" colspan="4"/>
</group>
<group colspan="4" attrs="{'invisible': [('is_company','=', False)]}">
<field name="child_ids" context="{'default_parent_id': active_id}" nolabel="1">
<form string="Partners">
<group col="8" colspan="4">
<group col="4" colspan="4">
<field name="name" required="1"/>
<field name="title" size="0" groups="base.group_extended" domain="[('domain', '=', 'contact')]"/>
<newline/>
<field name="function" attrs="{'invisible': [('is_company', '=', True)]}" colspan="4"/>
<field name="parent_id" string="Company" colspan="4" attrs="{'invisible': [('is_company','=', True)]}"
domain="[('is_company', '=', True)]" context="{'default_is_company': True}"
on_change="onchange_address(use_parent_address, parent_id)" invisible="1"/>
</group>
<group col="2">
<field name="is_company" on_change="onchange_type(is_company)"/>
</group>
<group col="2">
<field name="customer"/>
<field name="supplier"/>
</group>
<group col="2">
<field name="photo" widget='image' nolabel="1"/>
</group>
</group>
<newline/>
<group colspan="2" col="4">
<separator string="Postal Address" colspan="4" col="4" />
<field name="type" string="Type" colspan="2"/>
<field name="street" colspan="4"/>
<field name="street2" colspan="4"/>
<field name="zip"/>
<field name="city"/>
<field name="country_id" completion="1"/>
<field name="state_id"/>
</group>
<group colspan="2" col="2">
<separator string="Communication" colspan="2" col="2" />
<field name="phone"/>
<field name="mobile"/>
<field name="fax"/>
<field name="email" widget="email"/>
</group>
</form>
<tree string="Partner Contacts">
<field name="name"/>
<field name="zip"/>
<field name="city"/>
<field name="country_id"/>
<field name="phone"/>
<field name="email"/>
</tree>
</field>
<group groups="base.group_extended">
<separator colspan="4" string="Categories"/>
<field colspan="4" name="category_id" nolabel="1"/>
<notebook colspan="4">
<page string="General">
<group colspan="2">
<separator string="Address" colspan="4"/>
<group colspan="2">
<field name="type" string="Type" attrs="{'invisible': [('is_company','=', True)]}"/>
<field name="use_parent_address" attrs="{'invisible': [('is_company','=', True)]}" on_change="onchange_address(use_parent_address, parent_id)"/>
</group>
<newline/>
<field name="street" colspan="4"/>
<field name="street2" colspan="4"/>
<field name="zip"/>
<field name="city"/>
<field name="country_id"/>
<field name="state_id"/>
</group>
<group colspan="2">
<separator string="Communication" colspan="4"/>
<field name="lang" colspan="4"/>
<field name="phone" colspan="4"/>
<field name="mobile" colspan="4"/>
<field name="fax" colspan="4"/>
<field name="email" widget="email" colspan="4"/>
<field name="website" widget="url" colspan="4"/>
<field name="ref" groups="base.group_extended" colspan="4"/>
</group>
<group colspan="4" attrs="{'invisible': [('is_company','=', False)]}">
<field name="child_ids" domain="[('id','!=',parent_id.id)]" nolabel="1"/>
</group>
</page>
<page string="Sales &amp; Purchases" attrs="{'invisible': [('customer', '=', False), ('supplier', '=', False)]}">
<separator string="General Information" colspan="4"/>
<field name="user_id"/>
<field name="active" groups="base.group_extended"/>
<field name="date"/>
<field name="company_id" groups="base.group_multi_company" widget="selection"/>
<newline/>
</page>
<page string="History" groups="base.group_extended" invisible="True">
</page>
<page string="Categories" groups="base.group_extended">
<field name="category_id" colspan="4" nolabel="1"/>
</page>
<page string="Notes">
<field name="comment" colspan="4" nolabel="1"/>
</page>
</notebook>
</form>
</field>
</group>
</page>
<page string="Sales &amp; Purchases">
<page string="Sales &amp; Purchases" attrs="{'invisible': [('customer', '=', False), ('supplier', '=', False)]}">
<separator string="General Information" colspan="4"/>
<field name="user_id"/>
<field name="active" groups="base.group_extended"/>
<field name="website" widget="url"/>
<field name="date"/>
<field name="parent_id" groups="base.group_extended"/>
<field name="company_id" groups="base.group_multi_company" widget="selection"/>
<newline/>
</page>
<page string="History" groups="base.group_extended" invisible="True">
</page>
<page string="Categories" groups="base.group_extended">
<field name="category_id" colspan="4" nolabel="1"/>
</page>
<page string="Notes">
<field colspan="4" name="comment" nolabel="1"/>
<field name="comment" colspan="4" nolabel="1"/>
</page>
</notebook>
</form>
@@ -409,12 +478,15 @@
<field name="arch" type="xml">
<search string="Search Partner">
<group col='10' colspan='4'>
<filter string="Persons" name="type_person" icon="terp-personal" domain="[('is_company','=',0)]"/>
<filter string="Companies" name="type_company" icon="terp-partner" domain="[('is_company','=',1)]"/>
<separator orientation="vertical"/>
<filter string="Customers" name="customer" icon="terp-personal" domain="[('customer','=',1)]" help="Customer Partners"/>
<filter string="Suppliers" name="supplier" icon="terp-personal" domain="[('supplier','=',1)]" help="Supplier Partners"/>
<separator orientation="vertical"/>
<field name="name" select="1"/>
<field name="address" select="1"/>
<field name="country" select="1"/>
<!--field name="address" select="1"/-->
<!--field name="country" select="1"/-->
<field name="category_id" select="1" groups="base.group_extended"/>
<field name="user_id" select="1">
<filter help="My Partners" icon="terp-personal+" domain="[('user_id','=',uid)]"/>
@@ -423,6 +495,7 @@
<newline />
<group expand="0" string="Group By...">
<filter string="Salesman" icon="terp-personal" domain="[]" context="{'group_by' : 'user_id'}" />
<filter string="Contact Type" context="{'group_by': 'is_company'}"/>
</group>
</search>
</field>
@@ -430,66 +503,66 @@
<!-- Partner Kanban View -->
<record model="ir.ui.view" id="res_partner_kanban_view">
<field name="name">RES - PARTNER KANBAN</field>
<field name="name">res.partner.kanban</field>
<field name="model">res.partner</field>
<field name="type">kanban</field>
<field name="arch" type="xml">
<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="photo"/>
<field name="zip"/>
<field name="city"/>
<field name="country_id"/>
<field name="mobile"/>
<templates>
<t t-name="kanban-box">
<t t-set="color" t-value="kanban_color(record.color.raw_value || record.name.raw_value)"/>
<div t-att-class="color + (record.color.raw_value == 1 ? ' oe_kanban_color_alert' : '')">
<div class="oe_kanban_box oe_kanban_color_border">
<div class="oe_kanban_box_header oe_kanban_color_bgdark oe_kanban_color_border oe_kanban_draghandle">
<table class="oe_kanban_table">
<tr>
<td class="oe_kanban_title1" align="left" valign="middle">
<field name="name"/>
</td>
<td valign="top" width="22">
<img t-att-src="kanban_gravatar(record.email.value, 22)" class="oe_kanban_gravatar"/>
</td>
</tr>
</table>
</div>
<div class="oe_kanban_box_content oe_kanban_color_bglight oe_kanban_box_show_onclick_trigger oe_kanban_color_border">
<table class="oe_kanban_table">
<tr>
<td valign="top" width="64" align="left">
<img src="/base/static/src/img/kanban_partner.png" width="64" height="64"/>
</td>
<td valign="top" align="left">
<div class="oe_kanban_title2">
<field name="title"/>
<t t-if="record.title.raw_value and record.country.raw_value">,</t>
<field name="country"/>
</div>
<div class="oe_kanban_title3">
<field name="subname"/>
<t t-if="record.subname.raw_value and record.function.raw_value">,</t>
<field name="function"/>
</div>
<div class="oe_kanban_title3">
<i><field name="email"/>
<t t-if="record.phone.raw_value and record.email.raw_value">,</t>
<field name="phone"/></i>
</div>
</td>
</tr>
</table>
</div>
<div class="oe_kanban_buttons_set oe_kanban_color_border oe_kanban_color_bglight oe_kanban_box_show_onclick">
<div class="oe_kanban_left">
<a string="Edit" icon="gtk-edit" type="edit"/>
<a string="Change Color" icon="color-picker" type="color" name="color"/>
<a title="Mail" t-att-href="'mailto:'+record.email.value" style="text-decoration: none;" >
<img src="/web/static/src/img/icons/terp-mail-message-new.png" border="0" width="16" height="16"/>
</a>
<div class="oe_module_vignette">
<a type="edit">
<img t-att-src="kanban_image('res.partner', 'photo', record.id.value)" width="64" height="64" class="oe_module_icon"/>
</a>
<div class="oe_module_desc">
<div class="oe_kanban_box_content oe_kanban_color_bglight oe_kanban_box_show_onclick_trigger oe_kanban_color_border">
<table class="oe_kanban_table">
<tr>
<td class="oe_kanban_title1" align="left" valign="middle">
<h4><a type="edit"><field name="name"/></a>
<div t-if="record.parent_id.raw_value"><field name="parent_id"/></div>
</h4>
<i><div t-if="record.street.raw_value or record.street2.raw_value"><field name="street"/>
<field name="street2"/><br/></div>
<div t-if="record.city.raw_value or record.zip.raw_value"><field name="city"/>
<field name="zip"/><br/></div><div t-if="record.country_id.raw_value">
<field name="country_id"/><br/></div><div t-if="record.email.raw_value">
<field name="email"/><br/></div><div t-if="record.mobile.raw_value">
<span>+91</span><field name="mobile"/><br/></div></i>
</td>
<td t-if="record.is_company.raw_value" valign="top" align="right">
<!--img t-att-src="kanban_image('res.partner', 'photo', record.id.value)" class="oe_kanban_gravatar"/-->
</td>
</tr>
</table>
</div>
<br class="oe_kanban_clear"/>
<div class="oe_kanban_buttons_set oe_kanban_color_border oe_kanban_color_bglight oe_kanban_box_show_onclick">
<div class="oe_kanban_left">
<a string="Edit" icon="gtk-edit" type="edit"/>
<a string="Change Color" icon="color-picker" type="color" name="color"/>
<a title="Mail" t-att-href="'mailto:'+record.email.value" style="text-decoration: none;" >
<img src="/web/static/src/img/icons/terp-mail-message-new.png" border="0" width="16" height="16"/>
</a>
</div>
<br class="oe_kanban_clear"/>
</div>
</div>
</div>
</div>
@@ -500,27 +573,34 @@
</record>
<record id="action_partner_form" model="ir.actions.act_window">
<field name="name">Customers</field>
<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</field>
<field name="view_mode">tree,form,kanban</field>
<field name="context">{"search_default_customer":1}</field>
<field name="search_view_id" ref="view_res_partner_filter"/>
<field name="help">A customer is an entity you do business with, like a company or an organization. A customer can have several contacts or addresses which are the people working for this company. You can use the history tab, to follow all transactions related to a customer: sales order, emails, opportunities, claims, etc. If you use the email gateway, the Outlook or the Thunderbird plugin, don't forget to register emails to each contact so that the gateway will automatically attach incoming emails to the right partner.</field>
</record>
<record id="action_partner_form_view1" model="ir.actions.act_window.view">
<field eval="10" name="sequence"/>
<field name="view_mode">tree</field>
<field name="view_id" ref="view_partner_tree"/>
<field eval="0" name="sequence"/>
<field name="view_mode">kanban</field>
<field name="view_id" ref="res_partner_kanban_view"/>
<field name="act_window_id" ref="action_partner_form"/>
</record>
<record id="action_partner_form_view2" model="ir.actions.act_window.view">
<field eval="20" name="sequence"/>
<field eval="2" name="sequence"/>
<field name="view_mode">form</field>
<field name="view_id" ref="view_partner_form"/>
<field name="act_window_id" ref="action_partner_form"/>
</record>
<record id="action_partner_tree_view1" model="ir.actions.act_window.view">
<field name="sequence" eval="1"/>
<field name="view_mode">tree</field>
<field name="view_id" ref="view_partner_tree"/>
<field name="act_window_id" ref="action_partner_form"/>
</record>
<menuitem
action="action_partner_form"
id="menu_partner_form"
@@ -60,7 +60,7 @@ class partner_massmail_wizard(osv.osv_memory):
ir_mail_server = self.pool.get('ir.mail_server')
emails_seen = set()
for partner in partners:
for adr in partner.address:
for adr in partner.child_ids:
if adr.email and not adr.email in emails_seen:
try:
emails_seen.add(adr.email)
@@ -55,8 +55,6 @@
"access_res_lang_group_user","res_lang group_user","model_res_lang","group_system",1,1,1,1
"access_res_partner_group_partner_manager","res_partner group_partner_manager","model_res_partner","group_partner_manager",1,1,1,1
"access_res_partner_group_user","res_partner group_user","model_res_partner","group_user",1,0,0,0
"access_res_partner_address_group_partner_manager","res_partner_address group_partner_manager","model_res_partner_address","group_partner_manager",1,1,1,1
"access_res_partner_address_group_user","res_partner_address group_user","model_res_partner_address","group_user",1,0,0,0
"access_res_partner_bank_group_user","res_partner_bank group_user","model_res_partner_bank","group_user",1,0,0,0
"access_res_partner_bank_group_partner_manager","res_partner_bank group_partner_manager","model_res_partner_bank","group_partner_manager",1,1,1,1
"access_res_partner_bank_type_group_partner_manager","res_partner_bank_type group_partner_manager","model_res_partner_bank_type","group_partner_manager",1,1,1,1
@@ -117,7 +115,6 @@
"access_ir_filter all","ir_filters all","model_ir_filters",,1,0,0,0
"access_ir_filter employee","ir_filters employee","model_ir_filters","group_user",1,1,1,1
"access_ir_filters","ir_filters_all","model_ir_filters",,1,1,1,1
"access_res_partner_address","res.partner.address","model_res_partner_address","group_system",1,1,1,1
"access_res_widget","res.widget","model_res_widget","group_erp_manager",1,1,1,1
"access_res_widget_user","res.widget.user","model_res_widget",,1,0,0,0
"access_res_log_all","res.log","model_res_log",,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
55 access_res_lang_group_user res_lang group_user model_res_lang group_system 1 1 1 1
56 access_res_partner_group_partner_manager res_partner group_partner_manager model_res_partner group_partner_manager 1 1 1 1
57 access_res_partner_group_user res_partner group_user model_res_partner group_user 1 0 0 0
access_res_partner_address_group_partner_manager res_partner_address group_partner_manager model_res_partner_address group_partner_manager 1 1 1 1
access_res_partner_address_group_user res_partner_address group_user model_res_partner_address group_user 1 0 0 0
58 access_res_partner_bank_group_user res_partner_bank group_user model_res_partner_bank group_user 1 0 0 0
59 access_res_partner_bank_group_partner_manager res_partner_bank group_partner_manager model_res_partner_bank group_partner_manager 1 1 1 1
60 access_res_partner_bank_type_group_partner_manager res_partner_bank_type group_partner_manager model_res_partner_bank_type group_partner_manager 1 1 1 1
115 access_ir_filter all ir_filters all model_ir_filters 1 0 0 0
116 access_ir_filter employee ir_filters employee model_ir_filters group_user 1 1 1 1
117 access_ir_filters ir_filters_all model_ir_filters 1 1 1 1
access_res_partner_address res.partner.address model_res_partner_address group_system 1 1 1 1
118 access_res_widget res.widget model_res_widget group_erp_manager 1 1 1 1
119 access_res_widget_user res.widget.user model_res_widget 1 0 0 0
120 access_res_log_all res.log model_res_log 1 1 1 1
@@ -1,87 +0,0 @@
-
Create some default value for some (non-existing) model, for all users.
-
!python {model: ir.values }: |
# use the old API
self.set(cr, uid, 'default', False, 'my_test_field',['unexisting_model'], 'global value')
# use the new API
self.set_default(cr, uid, 'other_unexisting_model', 'my_other_test_field', 'conditional value', condition='foo=bar')
-
Retrieve them.
-
!python {model: ir.values }: |
# d is a list of triplets (id, name, value)
# Old API
d = self.get(cr, uid, 'default', False, ['unexisting_model'])
assert len(d) == 1, "Only one single value should be retrieved for this model"
assert d[0][1] == 'my_test_field', "Can't retrieve the created default value. (1)"
assert d[0][2] == 'global value', "Can't retrieve the created default value. (2)"
# New API, Conditional version
d = self.get_defaults(cr, uid, 'other_unexisting_model')
assert len(d) == 0, "No value should be retrieved, the condition is not met"
d = self.get_defaults(cr, uid, 'other_unexisting_model', condition="foo=eggs")
assert len(d) == 0, 'Condition is not met either, no defaults should be returned'
d = self.get_defaults(cr, uid, 'other_unexisting_model', condition="foo=bar")
assert len(d) == 1, "Only one single value should be retrieved"
assert d[0][1] == 'my_other_test_field', "Can't retrieve the created default value. (5)"
assert d[0][2] == 'conditional value', "Can't retrieve the created default value. (6)"
-
Do it again but for a specific user.
-
!python {model: ir.values }: |
self.set(cr, uid, 'default', False, 'my_test_field',['unexisting_model'], 'specific value', preserve_user=True)
-
Retrieve it and check it is the one for the current user.
-
!python {model: ir.values }: |
d = self.get(cr, uid, 'default', False, ['unexisting_model'])
assert len(d) == 1, "Only one default must be returned per field"
assert d[0][1] == 'my_test_field', "Can't retrieve the created default value."
assert d[0][2] == 'specific value', "Can't retrieve the created default value."
-
Create some action bindings for a non-existing model
-
!python {model: ir.values }: |
self.set(cr, uid, 'action', 'tree_but_open', 'OnDblClick Action', ['unexisting_model'], 'ir.actions.act_window,10', isobject=True)
self.set(cr, uid, 'action', 'tree_but_open', 'OnDblClick Action 2', ['unexisting_model'], 'ir.actions.act_window,11', isobject=True)
self.set(cr, uid, 'action', 'client_action_multi', 'Side Wizard', ['unexisting_model'], 'ir.actions.act_window,12', isobject=True)
self.set(cr, uid, 'action', 'client_print_multi', 'Nice Report', ['unexisting_model'], 'ir.actions.report.xml,2', isobject=True)
self.set(cr, uid, 'action', 'client_action_relate', 'Related Stuff', ['unexisting_model'], 'ir.actions.act_window,14', isobject=True)
-
Replace one action binding to set a new name
-
!python {model: ir.values }: |
self.set(cr, uid, 'action', 'tree_but_open', 'OnDblClick Action New', ['unexisting_model'], 'ir.actions.act_window,10', isobject=True)
-
Retrieve the action bindings and check they're correct
-
!python {model: ir.values }: |
actions = self.get(cr, uid, 'action', 'tree_but_open', ['unexisting_model'])
assert len(actions) == 2, "Mismatching number of bound actions"
#first action
assert len(actions[0]) == 3, "Malformed action definition"
assert actions[0][1] == 'OnDblClick Action 2', 'Bound action does not match definition'
assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == 11, 'Bound action does not match definition'
#second action - this ones comes last because it was re-created with a different name
assert len(actions[1]) == 3, "Malformed action definition"
assert actions[1][1] == 'OnDblClick Action New', 'Re-Registering an action should replace it'
assert isinstance(actions[1][2], dict) and actions[1][2]['id'] == 10, 'Bound action does not match definition'
actions = self.get(cr, uid, 'action', 'client_action_multi', ['unexisting_model'])
assert len(actions) == 1, "Mismatching number of bound actions"
assert len(actions[0]) == 3, "Malformed action definition"
assert actions[0][1] == 'Side Wizard', 'Bound action does not match definition'
assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == 12, 'Bound action does not match definition'
actions = self.get(cr, uid, 'action', 'client_print_multi', ['unexisting_model'])
assert len(actions) == 1, "Mismatching number of bound actions"
assert len(actions[0]) == 3, "Malformed action definition"
assert actions[0][1] == 'Nice Report', 'Bound action does not match definition'
assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == 2, 'Bound action does not match definition'
actions = self.get(cr, uid, 'action', 'client_action_relate', ['unexisting_model'])
assert len(actions) == 1, "Mismatching number of bound actions"
assert len(actions[0]) == 3, "Malformed action definition"
assert actions[0][1] == 'Related Stuff', 'Bound action does not match definition'
assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == 14, 'Bound action does not match definition'
@@ -68,20 +68,20 @@
-
Testing that some domain expressions work
-
!python {model: res.partner.address }: |
ids = self.search(cr, uid, [('partner_id','=','Agrolait')])
!python {model: res.partner }: |
ids = self.search(cr, uid, [('parent_id','=','Agrolait')])
assert len(ids) >= 1, ids
-
Trying the "in" operator, for scalar value
-
!python {model: res.partner.address }: |
ids = self.search(cr, uid, [('partner_id','in','Agrolait')])
!python {model: res.partner }: |
ids = self.search(cr, uid, [('parent_id','in','Agrolait')])
assert len(ids) >= 1, ids
-
Trying the "in" operator for list value
-
!python {model: res.partner.address }: |
ids = self.search(cr, uid, [('partner_id','in',['Agrolait','ASUStek'])])
!python {model: res.partner }: |
ids = self.search(cr, uid, [('parent_id','in',['Agrolait','ASUStek'])])
assert len(ids) >= 1, ids
-
Check we can use "in" operator for plain fields.
@@ -89,15 +89,6 @@
!python {model: ir.ui.menu }: |
ids = self.search(cr, uid, [('sequence','in',[1, 2, 10, 20])])
assert len(ids) >= 1, ids
-
Test one2many operator with empty search list
-
!assert {model: res.partner, search: "[('address', 'in', [])]", count: 0, string: "Ids should be empty"}
-
Test one2many operator with False
-
!assert {model: res.partner, search: "[('address', '=', False)]"}:
- address in (False, None, [])
-
Test many2many operator with empty search list
-
@@ -107,10 +98,6 @@
-
!assert {model: res.partner, search: "[('category_id', '=', False)]"}:
- category_id in (False, None, [])
-
Filtering on invalid value across x2many relationship should return an empty set
-
!assert {model: res.partner, search: "[('address.city','=','foo')]", count: 0, string: "Searching for address.city = foo should give empty results"}
-
Check if many2one works with empty search list
-
@@ -440,16 +427,6 @@
assert res_2 == expected
assert res_3 == expected
assert res_4 == expected
-
Verify that normalize_domain() works.
-
!python {model: res.partner}: |
from osv import expression
norm_domain = domain = ['&',(1,'=',1),('a','=','b')]
assert norm_domain == expression.normalize(domain), "Normalized domains should be left untouched"
domain = [('x','in',['y','z']),('a.v','=','e'),'|','|',('a','=','b'),'!',('c','>','d'),('e','!=','f'),('g','=','h')]
norm_domain = ['&','&','&'] + domain
assert norm_domain == expression.normalize(domain), "Non-normalized domains should be properly normalized"
-
Unaccent. Create a company with an accent in its name.
-
@@ -525,15 +502,7 @@
vals = {'category_id': [(6, 0, [ref("base.res_partner_category_8")])],
'name': 'OpenERP Test',
'active': False,
'address': [(0, 0, {'country_id': ref("base.be")})]
}
self.create(cr, uid, vals, context=context)
res_ids = self.search(cr, uid, [('category_id', 'ilike', 'supplier'), ('active', '=', False)])
assert len(res_ids) != 0, "Record not Found with category supplier and active False."
-
Testing for One2Many field with country Belgium and active=False
-
!python {model: res.partner }: |
res_ids = self.search(cr, uid, [('address.country_id','=','Belgium'),('active','=',False)])
assert len(res_ids) != 0, "Record not Found with country Belgium and active False."
+5
View File
@@ -0,0 +1,5 @@
import test_ir_values
checks = [
test_ir_values,
]
@@ -0,0 +1,95 @@
import unittest2
import openerp.tests.common as common
class test_ir_values(common.TransactionCase):
def test_00(self):
# Create some default value for some (non-existing) model, for all users.
ir_values = self.registry('ir.values')
# use the old API
ir_values.set(self.cr, self.uid, 'default', False, 'my_test_field',
['unexisting_model'], 'global value')
# use the new API
ir_values.set_default(self.cr, self.uid, 'other_unexisting_model',
'my_other_test_field', 'conditional value', condition='foo=bar')
# Retrieve them.
ir_values = self.registry('ir.values')
# d is a list of triplets (id, name, value)
# Old API
d = ir_values.get(self.cr, self.uid, 'default', False, ['unexisting_model'])
assert len(d) == 1, "Only one single value should be retrieved for this model"
assert d[0][1] == 'my_test_field', "Can't retrieve the created default value. (1)"
assert d[0][2] == 'global value', "Can't retrieve the created default value. (2)"
# New API, Conditional version
d = ir_values.get_defaults(self.cr, self.uid, 'other_unexisting_model')
assert len(d) == 0, "No value should be retrieved, the condition is not met"
d = ir_values.get_defaults(self.cr, self.uid, 'other_unexisting_model', condition="foo=eggs")
assert len(d) == 0, 'Condition is not met either, no defaults should be returned'
d = ir_values.get_defaults(self.cr, self.uid, 'other_unexisting_model', condition="foo=bar")
assert len(d) == 1, "Only one single value should be retrieved"
assert d[0][1] == 'my_other_test_field', "Can't retrieve the created default value. (5)"
assert d[0][2] == 'conditional value', "Can't retrieve the created default value. (6)"
# Do it again but for a specific user.
ir_values = self.registry('ir.values')
ir_values.set(self.cr, self.uid, 'default', False, 'my_test_field',['unexisting_model'], 'specific value', preserve_user=True)
# Retrieve it and check it is the one for the current user.
ir_values = self.registry('ir.values')
d = ir_values.get(self.cr, self.uid, 'default', False, ['unexisting_model'])
assert len(d) == 1, "Only one default must be returned per field"
assert d[0][1] == 'my_test_field', "Can't retrieve the created default value."
assert d[0][2] == 'specific value', "Can't retrieve the created default value."
# Create some action bindings for a non-existing model.
ir_values = self.registry('ir.values')
ir_values.set(self.cr, self.uid, 'action', 'tree_but_open', 'OnDblClick Action', ['unexisting_model'], 'ir.actions.act_window,10', isobject=True)
ir_values.set(self.cr, self.uid, 'action', 'tree_but_open', 'OnDblClick Action 2', ['unexisting_model'], 'ir.actions.act_window,11', isobject=True)
ir_values.set(self.cr, self.uid, 'action', 'client_action_multi', 'Side Wizard', ['unexisting_model'], 'ir.actions.act_window,12', isobject=True)
ir_values.set(self.cr, self.uid, 'action', 'client_print_multi', 'Nice Report', ['unexisting_model'], 'ir.actions.report.xml,2', isobject=True)
ir_values.set(self.cr, self.uid, 'action', 'client_action_relate', 'Related Stuff', ['unexisting_model'], 'ir.actions.act_window,14', isobject=True)
# Replace one action binding to set a new name.
ir_values = self.registry('ir.values')
ir_values.set(self.cr, self.uid, 'action', 'tree_but_open', 'OnDblClick Action New', ['unexisting_model'], 'ir.actions.act_window,10', isobject=True)
# Retrieve the action bindings and check they're correct
ir_values = self.registry('ir.values')
actions = ir_values.get(self.cr, self.uid, 'action', 'tree_but_open', ['unexisting_model'])
assert len(actions) == 2, "Mismatching number of bound actions"
#first action
assert len(actions[0]) == 3, "Malformed action definition"
assert actions[0][1] == 'OnDblClick Action 2', 'Bound action does not match definition'
assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == 11, 'Bound action does not match definition'
#second action - this ones comes last because it was re-created with a different name
assert len(actions[1]) == 3, "Malformed action definition"
assert actions[1][1] == 'OnDblClick Action New', 'Re-Registering an action should replace it'
assert isinstance(actions[1][2], dict) and actions[1][2]['id'] == 10, 'Bound action does not match definition'
actions = ir_values.get(self.cr, self.uid, 'action', 'client_action_multi', ['unexisting_model'])
assert len(actions) == 1, "Mismatching number of bound actions"
assert len(actions[0]) == 3, "Malformed action definition"
assert actions[0][1] == 'Side Wizard', 'Bound action does not match definition'
assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == 12, 'Bound action does not match definition'
actions = ir_values.get(self.cr, self.uid, 'action', 'client_print_multi', ['unexisting_model'])
assert len(actions) == 1, "Mismatching number of bound actions"
assert len(actions[0]) == 3, "Malformed action definition"
assert actions[0][1] == 'Nice Report', 'Bound action does not match definition'
assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == 2, 'Bound action does not match definition'
actions = ir_values.get(self.cr, self.uid, 'action', 'client_action_relate', ['unexisting_model'])
assert len(actions) == 1, "Mismatching number of bound actions"
assert len(actions[0]) == 3, "Malformed action definition"
assert actions[0][1] == 'Related Stuff', 'Bound action does not match definition'
assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == 14, 'Bound action does not match definition'
+29 -17
View File
@@ -50,6 +50,7 @@ import openerp.pooler as pooler
import openerp.release as release
import openerp.tools as tools
import openerp.tools.osutil as osutil
import openerp.tools.assertion_report as assertion_report
from openerp.tools.safe_eval import safe_eval as eval
from openerp.tools.translate import _
@@ -94,19 +95,20 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=
def load_test(module_name, idref, mode):
cr.commit()
if not tools.config.options['test_disable']:
try:
threading.currentThread().testing = True
_load_data(cr, module_name, idref, mode, 'test')
except Exception, e:
_logger.exception(
'Tests failed to execute in module %s', module_name)
finally:
threading.currentThread().testing = False
if tools.config.options['test_commit']:
cr.commit()
else:
cr.rollback()
try:
threading.currentThread().testing = True
_load_data(cr, module_name, idref, mode, 'test')
return True
except Exception, e:
_logger.error(
'module %s: an exception occurred in a test', module_name)
return False
finally:
threading.currentThread().testing = False
if tools.config.options['test_commit']:
cr.commit()
else:
cr.rollback()
def _load_data(cr, module_name, idref, mode, kind):
"""
@@ -133,7 +135,7 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=
elif ext == '.sql':
process_sql_file(cr, fp)
elif ext == '.yml':
tools.convert_yaml_import(cr, module_name, fp, idref, mode, noupdate)
tools.convert_yaml_import(cr, module_name, fp, idref, mode, noupdate, report)
else:
tools.convert_xml_import(cr, module_name, fp, idref, mode, noupdate, report)
finally:
@@ -201,7 +203,14 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=
# on demo data. Other tests can be added into the regular
# 'data' section, but should probably not alter the data,
# as there is no rollback.
load_test(module_name, idref, mode)
if tools.config.options['test_enable']:
report.record_result(load_test(module_name, idref, mode))
# Run the `fast_suite` and `checks` tests given by the module.
if module_name == 'base':
# Also run the core tests after the database is created.
report.record_result(openerp.modules.module.run_unit_tests('openerp'))
report.record_result(openerp.modules.module.run_unit_tests(module_name))
processed_modules.append(package.name)
@@ -282,7 +291,6 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
# This is a brand new pool, just created in pooler.get_db_and_pool()
pool = pooler.get_pool(cr.dbname)
report = tools.assertion_report()
if 'base' in tools.config['update'] or 'all' in tools.config['update']:
cr.execute("update ir_module_module set state=%s where name=%s and state=%s", ('to upgrade', 'base', 'installed'))
@@ -295,6 +303,7 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
# processed_modules: for cleanup step after install
# loaded_modules: to avoid double loading
report = assertion_report.assertion_report()
loaded_modules, processed_modules = load_module_graph(cr, graph, status, perform_checks=(not update_module), report=report)
if tools.config['load_language']:
@@ -414,7 +423,10 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
cr.execute("update ir_module_module set state=%s where state=%s", ('uninstalled', 'to remove',))
cr.commit()
_logger.info('Modules loaded.')
if report.failures:
_logger.error('At least one test failed when loading the modules.')
else:
_logger.info('Modules loaded.')
finally:
cr.close()
+124 -2
View File
@@ -20,9 +20,12 @@
#
##############################################################################
import os, sys, imp
from os.path import join as opj
import imp
import itertools
import os
from os.path import join as opj
import sys
import types
import zipimport
import openerp
@@ -460,5 +463,124 @@ def get_modules_with_version():
continue
return res
def get_test_modules(module, submodule, explode):
"""
Return a list of submodules containing tests.
`submodule` can be:
- None
- the name of a submodule
- '__fast_suite__'
- '__sanity_checks__'
"""
# Turn command-line module, submodule into importable names.
if module is None:
pass
elif module == 'openerp':
module = 'openerp.tests'
else:
module = 'openerp.addons.' + module + '.tests'
# Try to import the module
try:
__import__(module)
except Exception, e:
if explode:
print 'Can not `import %s`.' % module
import logging
logging.exception('')
sys.exit(1)
else:
if str(e) == 'No module named tests':
# It seems the module has no `tests` sub-module, no problem.
pass
else:
print 'Can not `import %s`.' % module
return []
# Discover available test sub-modules.
m = sys.modules[module]
submodule_names = sorted([x for x in dir(m) \
if x.startswith('test_') and \
isinstance(getattr(m, x), types.ModuleType)])
submodules = [getattr(m, x) for x in submodule_names]
def show_submodules_and_exit():
if submodule_names:
print 'Available submodules are:'
for x in submodule_names:
print ' ', x
sys.exit(1)
if submodule is None:
# Use auto-discovered sub-modules.
ms = submodules
elif submodule == '__fast_suite__':
# Obtain the explicit test sub-modules list.
ms = getattr(sys.modules[module], 'fast_suite', None)
# `suite` was used before the 6.1 release instead of `fast_suite`.
ms = ms if ms else getattr(sys.modules[module], 'suite', None)
if ms is None:
if explode:
print 'The module `%s` has no defined test suite.' % (module,)
show_submodules_and_exit()
else:
ms = []
elif submodule == '__sanity_checks__':
ms = getattr(sys.modules[module], 'checks', None)
if ms is None:
if explode:
print 'The module `%s` has no defined sanity checks.' % (module,)
show_submodules_and_exit()
else:
ms = []
else:
# Pick the command-line-specified test sub-module.
m = getattr(sys.modules[module], submodule, None)
ms = [m]
if m is None:
if explode:
print 'The module `%s` has no submodule named `%s`.' % \
(module, submodule)
show_submodules_and_exit()
else:
ms = []
return ms
def run_unit_tests(module_name):
"""
Return True or False if some tests were found and succeeded or failed.
Return None if no test was found.
"""
import unittest2
ms = get_test_modules(module_name, '__fast_suite__', explode=False)
ms.extend(get_test_modules(module_name, '__sanity_checks__', explode=False))
suite = unittest2.TestSuite()
for m in ms:
suite.addTests(unittest2.TestLoader().loadTestsFromModule(m))
if ms:
_logger.info('module %s: executing %s `fast_suite` and/or `checks` sub-modules', module_name, len(ms))
# Use a custom stream object to log the test executions.
class MyStream(object):
def __init__(self):
self.r = re.compile(r'^-*$|^ *... *$|^ok$')
def flush(self):
pass
def write(self, s):
if self.r.match(s):
return
first = True
for c in s.split('\n'):
if not first:
c = '` ' + c
first = False
_logger.log(logging.TEST, c)
result = unittest2.TextTestRunner(verbosity=2, stream=MyStream()).run(suite)
if result.wasSuccessful():
return True
else:
_logger.error('module %s: at least one error occurred in a test', module_name)
return False
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+4 -1
View File
@@ -866,7 +866,10 @@ class BaseModel(object):
parent_names = [parent_names]
else:
name = cls._name
# for res.parnter.address compatiblity ,should be remove in v7
if 'res.partner.address' in parent_names:
parent_names.pop(parent_names.index('res.partner.address'))
parent_names.append('res.partner')
if not name:
raise TypeError('_name is mandatory in case of multiple inheritance')
+2 -2
View File
@@ -320,8 +320,8 @@ class rml_parse(object):
res='%s %s'%(currency_obj.symbol, res)
return res
def display_address(self, address_browse_record):
return self.pool.get('res.partner.address')._display_address(self.cr, self.uid, address_browse_record)
def display_address(self, address_browse_record,type=''):
return self.pool.get('res.partner')._display_address(self.cr, self.uid, address_browse_record,type)
def repeatIn(self, lst, name,nodes_parent=False):
ret_lst = []
+2
View File
@@ -48,6 +48,8 @@ import openerp.wsgi
_logger = logging.getLogger(__name__)
# TODO block until the server is really up, accepting connections
# TODO be idemptotent (as long as stop_service was not called).
def start_services():
""" Start all services.
+17 -8
View File
@@ -1,15 +1,24 @@
# -*- coding: utf-8 -*-
import unittest2
"""
Tests for the OpenERP library.
import test_orm
This module groups a few sub-modules containing unittest2 test cases.
Tests can be explicitely added to the `fast_suite` or `checks` lists or not.
See the :ref:`test-framework` section in the :ref:`features` list.
"""
import test_expression
import test_ir_sequence
import test_xmlrpc
import test_orm
# Explicit declaration list of test sub-modules.
suite = [
test_xmlrpc, # Creates a database
test_ir_sequence, # Assume an existing database
test_orm, # Assume an existing database
fast_suite = [
test_ir_sequence,
]
checks = [
test_expression,
test_orm,
]
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+52 -34
View File
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
import os
import time
import unittest2
import xmlrpclib
import openerp
@@ -16,15 +17,6 @@ ADMIN_USER = 'admin'
ADMIN_USER_ID = 1
ADMIN_PASSWORD = 'admin'
common_proxy_60 = None
db_proxy_60 = None
object_proxy_60 = None
common_proxy_61 = None
db_proxy_61 = None
model_proxy_61 = None
model_uri_61 = None
def start_openerp():
"""
Start the OpenERP server similary to the openerp-server script.
@@ -34,33 +26,59 @@ def start_openerp():
# Ugly way to ensure the server is listening.
time.sleep(2)
def create_xmlrpc_proxies():
def stop_openerp():
"""
setup some xmlrpclib proxies.
Shutdown the OpenERP server similarly to a single ctrl-c.
"""
global common_proxy_60
global db_proxy_60
global object_proxy_60
# Use the old (pre 6.1) API.
url = 'http://%s:%d/xmlrpc/' % (HOST, PORT)
common_proxy_60 = xmlrpclib.ServerProxy(url + 'common')
db_proxy_60 = xmlrpclib.ServerProxy(url + 'db')
object_proxy_60 = xmlrpclib.ServerProxy(url + 'object')
global common_proxy_61
global db_proxy_61
global model_proxy_61
global model_uri_61
# Use the new (6.1) API.
model_uri_61 = 'http://%s:%d/openerp/xmlrpc/1/' % (HOST, PORT)
common_proxy_61 = xmlrpclib.ServerProxy(model_uri_61 + 'common')
db_proxy_61 = xmlrpclib.ServerProxy(model_uri_61 + 'db')
model_proxy_61 = xmlrpclib.ServerProxy(model_uri_61 + 'model/' + DB)
def tearDownModule():
""" Shutdown the OpenERP server similarly to a single ctrl-c. """
openerp.service.stop_services()
class TransactionCase(unittest2.TestCase):
"""
Subclass of TestCase with a single transaction, rolled-back at the end of
the tests.
"""
def setUp(self):
self.cr = openerp.modules.registry.RegistryManager.get(DB).db.cursor()
self.uid = openerp.SUPERUSER_ID
def tearDown(self):
self.cr.rollback()
self.cr.close()
def registry(self, model):
return openerp.modules.registry.RegistryManager.get(DB)[model]
class RpcCase(unittest2.TestCase):
"""
Subclass of TestCase with a few XML-RPC proxies.
"""
def __init__(self, name):
super(RpcCase, self).__init__(name)
class A(object):
pass
self.proxy = A()
# Use the old (pre 6.1) API.
self.proxy.url_60 = url_60 = 'http://%s:%d/xmlrpc/' % (HOST, PORT)
self.proxy.common_60 = xmlrpclib.ServerProxy(url_60 + 'common')
self.proxy.db_60 = xmlrpclib.ServerProxy(url_60 + 'db')
self.proxy.object_60 = xmlrpclib.ServerProxy(url_60 + 'object')
# Use the new (6.1) API.
self.proxy.url_61 = url_61 = 'http://%s:%d/openerp/xmlrpc/1/' % (HOST, PORT)
self.proxy.common_61 = xmlrpclib.ServerProxy(url_61 + 'common')
self.proxy.db_61 = xmlrpclib.ServerProxy(url_61 + 'db')
self.proxy.model_61 = xmlrpclib.ServerProxy(url_61 + 'model/' + DB)
@classmethod
def generate_database_name(cls):
if hasattr(cls, '_database_id'):
cls._database_id += 1
else:
cls._database_id = 0
return '_fresh_name_' + str(cls._database_id) + '_'
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+12
View File
@@ -0,0 +1,12 @@
import unittest2
import openerp
class test_domain_normalization(unittest2.TestCase):
def test_normalize_domain(self):
expression = openerp.osv.expression
norm_domain = domain = ['&',(1,'=',1),('a','=','b')]
assert norm_domain == expression.normalize(domain), "Normalized domains should be left untouched"
domain = [('x','in',['y','z']),('a.v','=','e'),'|','|',('a','=','b'),'!',('c','>','d'),('e','!=','f'),('g','=','h')]
norm_domain = ['&','&','&'] + domain
assert norm_domain == expression.normalize(domain), "Non-normalized domains should be properly normalized"
+1 -7
View File
@@ -16,11 +16,6 @@ import common
DB = common.DB
ADMIN_USER_ID = common.ADMIN_USER_ID
def setUpModule():
common.create_xmlrpc_proxies()
tearDownModule = common.tearDownModule
def registry(model):
return openerp.modules.registry.RegistryManager.get(DB)[model]
@@ -174,8 +169,7 @@ class test_ir_sequence_generate(unittest2.TestCase):
def test_ir_sequence_create_no_gap(self):
""" Try to create a sequence object. """
cr = cursor()
d = dict(code='test_sequence_type_6', name='Test sequence type',
implementation='no_gap')
d = dict(code='test_sequence_type_6', name='Test sequence type')
c = registry('ir.sequence.type').create(cr, ADMIN_USER_ID, d, {})
assert c
d = dict(code='test_sequence_type_6', name='Test sequence')
+7 -10
View File
@@ -2,9 +2,10 @@ import os
import unittest2
import openerp
import common
UID = 1
DB = openerp.tools.config['db_name']
UID = common.ADMIN_USER_ID
DB = common.DB
CREATE = lambda values: (0, False, values)
UPDATE = lambda id, values: (1, id, values)
@@ -14,16 +15,12 @@ LINK_TO = lambda id: (4, id, False)
DELETE_ALL = lambda: (5, False, False)
REPLACE_WITH = lambda ids: (6, False, ids)
class TestO2MSerialization(unittest2.TestCase):
class TestO2MSerialization(common.TransactionCase):
def setUp(self):
self.cr = openerp.modules.registry.RegistryManager.get(DB).db.cursor()
self.partner = openerp.modules.registry.RegistryManager.get(DB)['res.partner']
self.address = openerp.modules.registry.RegistryManager.get(DB)['res.partner.address']
def tearDown(self):
self.cr.rollback()
self.cr.close()
super(TestO2MSerialization, self).setUp()
self.partner = self.registry('res.partner')
self.address = self.registry('res.partner.address')
def test_no_command(self):
" empty list of commands yields an empty list of records "
+20 -12
View File
@@ -13,56 +13,64 @@ import xmlrpclib
import openerp
import common
DB = common.DB
DB = None
ADMIN_USER = common.ADMIN_USER
ADMIN_USER_ID = common.ADMIN_USER_ID
ADMIN_PASSWORD = common.ADMIN_PASSWORD
def setUpModule():
common.start_openerp()
common.create_xmlrpc_proxies()
common.start_openerp()
global DB
DB = common.RpcCase.generate_database_name()
tearDownModule = common.tearDownModule
tearDownModule = common.stop_openerp
class test_xmlrpc(unittest2.TestCase):
class test_xmlrpc(common.RpcCase):
def test_00_xmlrpc_create_database_polling(self):
"""
Simulate a OpenERP client requesting the creation of a database and
polling the server until the creation is complete.
"""
progress_id = common.db_proxy_60.create(ADMIN_PASSWORD, DB, True,
False, ADMIN_PASSWORD)
progress_id = self.proxy.db_60.create(ADMIN_PASSWORD,DB, True, False,
ADMIN_PASSWORD)
while True:
time.sleep(1)
progress, users = common.db_proxy_60.get_progress(ADMIN_PASSWORD,
progress, users = self.proxy.db_60.get_progress(ADMIN_PASSWORD,
progress_id)
if progress == 1.0:
break
def test_xmlrpc_login(self):
""" Try to login on the common service. """
uid = common.common_proxy_60.login(DB, ADMIN_USER, ADMIN_PASSWORD)
uid = self.proxy.common_60.login(DB, ADMIN_USER, ADMIN_PASSWORD)
assert uid == ADMIN_USER_ID
def test_xmlrpc_ir_model_search(self):
""" Try a search on the object service. """
ids = common.object_proxy_60.execute(DB, ADMIN_USER_ID, ADMIN_PASSWORD,
ids = self.proxy.object_60.execute(DB, ADMIN_USER_ID, ADMIN_PASSWORD,
'ir.model', 'search', [])
assert ids
ids = common.object_proxy_60.execute(DB, ADMIN_USER_ID, ADMIN_PASSWORD,
ids = self.proxy.object_60.execute(DB, ADMIN_USER_ID, ADMIN_PASSWORD,
'ir.model', 'search', [], {})
assert ids
def test_xmlrpc_61_ir_model_search(self):
""" Try a search on the object service. """
proxy = xmlrpclib.ServerProxy(common.model_uri_61 + 'model/' + DB + '/ir.model')
proxy = xmlrpclib.ServerProxy(self.proxy.url_61 + 'model/' + DB +
'/ir.model')
ids = proxy.execute(ADMIN_USER_ID, ADMIN_PASSWORD, 'search', [])
assert ids
ids = proxy.execute(ADMIN_USER_ID, ADMIN_PASSWORD, 'search', [], {})
assert ids
def test_zz_xmlrpc_drop_database(self):
"""
Simulate a OpenERP client requesting the deletion of a database.
"""
assert self.proxy.db_60.drop(ADMIN_PASSWORD, DB) is True
if __name__ == '__main__':
unittest2.main()
+29
View File
@@ -0,0 +1,29 @@
class assertion_report(object):
"""
Simple pair of success and failures counts (used to record YAML and XML
`assert` tags as well as unittest2 tests outcome (in this case, not
individual `assert`)).
"""
def __init__(self):
self.successes = 0
self.failures = 0
def record_success(self):
self.successes += 1
def record_failure(self):
self.failures += 1
def record_result(self, result):
if result is None:
pass
elif result is True:
self.record_success()
elif result is False:
self.record_failure()
def __str__(self):
res = 'Assertions report: %s successes, %s failures' % (self.successes, self.failures)
return res
+5 -13
View File
@@ -168,13 +168,10 @@ class configmanager(object):
help="Launch a YML test file.")
group.add_option("--test-report-directory", dest="test_report_directory", my_default=False,
help="If set, will save sample of all reports in this directory.")
group.add_option("--test-disable", action="store_true", dest="test_disable",
my_default=False, help="Disable loading test files.")
group.add_option("--test-enable", action="store_true", dest="test_enable",
my_default=False, help="Enable YAML and unit tests.")
group.add_option("--test-commit", action="store_true", dest="test_commit",
my_default=False, help="Commit database changes performed by tests.")
group.add_option("--assert-exit-level", dest='assert_exit_level', type="choice", choices=self._LOGLEVELS.keys(),
my_default='error',
help="specify the level at which a failed assertion will stop the server. Accepted values: %s" % (self._LOGLEVELS.keys(),))
my_default=False, help="Commit database changes performed by YAML or XML tests.")
parser.add_option_group(group)
# Logging Group
@@ -395,7 +392,7 @@ class configmanager(object):
'debug_mode', 'smtp_ssl', 'load_language',
'stop_after_init', 'logrotate', 'without_demo', 'netrpc', 'xmlrpc', 'syslog',
'list_db', 'xmlrpcs', 'proxy_mode',
'test_file', 'test_disable', 'test_commit', 'test_report_directory',
'test_file', 'test_enable', 'test_commit', 'test_report_directory',
'osv_memory_count_limit', 'osv_memory_age_limit', 'max_cron_threads',
'virtual_memory_limit', 'virtual_memory_reset', 'cpu_time_limit', 'unaccent',
]
@@ -408,11 +405,6 @@ class configmanager(object):
elif isinstance(self.options[arg], basestring) and self.casts[arg].type in optparse.Option.TYPE_CHECKER:
self.options[arg] = optparse.Option.TYPE_CHECKER[self.casts[arg].type](self.casts[arg], arg, self.options[arg])
if opt.assert_exit_level:
self.options['assert_exit_level'] = self._LOGLEVELS[opt.assert_exit_level]
else:
self.options['assert_exit_level'] = self._LOGLEVELS.get(self.options['assert_exit_level']) or int(self.options['assert_exit_level'])
self.options['root_path'] = os.path.abspath(os.path.expanduser(os.path.expandvars(os.path.dirname(openerp.__file__))))
if not self.options['addons_path'] or self.options['addons_path']=='None':
self.options['addons_path'] = os.path.join(self.options['root_path'], 'addons')
@@ -582,7 +574,7 @@ class configmanager(object):
continue
if opt in self.blacklist_for_save:
continue
if opt in ('log_level', 'assert_exit_level'):
if opt in ('log_level',):
p.set('options', opt, loglevelnames.get(self.options[opt], self.options[opt]))
else:
p.set('options', opt, self.options[opt])
+9 -45
View File
@@ -30,6 +30,8 @@ import re
import time
import openerp.release as release
import assertion_report
_logger = logging.getLogger(__name__)
try:
@@ -200,35 +202,6 @@ escape_re = re.compile(r'(?<!\\)/')
def escape(x):
return x.replace('\\/', '/')
class assertion_report(object):
def __init__(self):
self._report = {}
def record_assertion(self, success, severity):
"""
Records the result of an assertion for the failed/success count
returns success
"""
if severity in self._report:
self._report[severity][success] += 1
else:
self._report[severity] = {success:1, not success: 0}
return success
def get_report(self):
return self._report
def __str__(self):
res = '\nAssertions report:\nLevel\tsuccess\tfailed\n'
success = failed = 0
for sev in self._report:
res += sev + '\t' + str(self._report[sev][True]) + '\t' + str(self._report[sev][False]) + '\n'
success += self._report[sev][True]
failed += self._report[sev][False]
res += 'total\t' + str(success) + '\t' + str(failed) + '\n'
res += 'end of report (' + str(success + failed) + ' assertion(s) checked)'
return res
class xml_import(object):
@staticmethod
def nodeattr2bool(node, attr, default=False):
@@ -712,7 +685,6 @@ form: module.record_id""" % (xml_id,)
rec_src = rec.get("search",'').encode('utf8')
rec_src_count = rec.get("count")
severity = rec.get("severity",'').encode('ascii') or loglevels.LOG_ERROR
rec_string = rec.get("string",'').encode('utf8') or 'unknown'
ids = None
@@ -727,17 +699,13 @@ form: module.record_id""" % (xml_id,)
if rec_src_count:
count = int(rec_src_count)
if len(ids) != count:
self.assert_report.record_assertion(False, severity)
self.assertion_report.record_failure()
msg = 'assertion "%s" failed!\n' \
' Incorrect search count:\n' \
' expected count: %d\n' \
' obtained count: %d\n' \
% (rec_string, count, len(ids))
sevval = getattr(logging, severity.upper())
_logger.log(sevval, msg)
if sevval >= config['assert_exit_level']:
# TODO: define a dedicated exception
raise Exception('Severe assertion failure')
_logger.error(msg)
return
assert ids is not None,\
@@ -759,20 +727,16 @@ form: module.record_id""" % (xml_id,)
expected_value = _eval_xml(self, test, self.pool, cr, uid, self.idref, context=context) or True
expression_value = unsafe_eval(f_expr, globals_dict)
if expression_value != expected_value: # assertion failed
self.assert_report.record_assertion(False, severity)
self.assertion_report.record_failure()
msg = 'assertion "%s" failed!\n' \
' xmltag: %s\n' \
' expected value: %r\n' \
' obtained value: %r\n' \
% (rec_string, etree.tostring(test), expected_value, expression_value)
sevval = getattr(logging, severity.upper())
_logger.log(sevval, msg)
if sevval >= config['assert_exit_level']:
# TODO: define a dedicated exception
raise Exception('Severe assertion failure')
_logger.error(msg)
return
else: # all tests were successful for this assertion tag (no break)
self.assert_report.record_assertion(True, severity)
self.assertion_report.record_success()
def _tag_record(self, cr, rec, data_node=None):
rec_model = rec.get("model").encode('ascii')
@@ -906,8 +870,8 @@ form: module.record_id""" % (xml_id,)
self.pool = pooler.get_pool(cr.dbname)
self.uid = 1
if report is None:
report = assertion_report()
self.assert_report = report
report = assertion_report.assertion_report()
self.assertion_report = report
self.noupdate = noupdate
self._tags = {
'menuitem': self._tag_menuitem,
+2 -2
View File
@@ -86,8 +86,8 @@ class ReceiverEmail2Event(object):
def get_partners(self, headers, msg):
alladdresses = self.get_addresses(headers, msg)
address_ids = self.rpc(('res.partner.address', 'search', [('email', 'in', alladdresses)]))
addresses = self.rpc(('res.partner.address', 'read', address_ids))
address_ids = self.rpc(('res.partner', 'search', [('email', 'in', alladdresses)]))
addresses = self.rpc(('res.partner', 'read', address_ids))
return [x['partner_id'][0] for x in addresses]
def __call__(self, request):
+17 -49
View File
@@ -19,6 +19,8 @@ from lxml import etree
unsafe_eval = eval
from safe_eval import safe_eval as eval
import assertion_report
_logger = logging.getLogger(__name__)
class YamlImportException(Exception):
@@ -85,33 +87,6 @@ def is_ir_set(node):
def is_string(node):
return isinstance(node, basestring)
class TestReport(object):
def __init__(self):
self._report = {}
def record(self, success, severity):
"""
Records the result of an assertion for the failed/success count.
Returns success.
"""
if severity in self._report:
self._report[severity][success] += 1
else:
self._report[severity] = {success: 1, not success: 0}
return success
def __str__(self):
res = []
res.append('\nAssertions report:\nLevel\tsuccess\tfailure')
success = failure = 0
for severity in self._report:
res.append("%s\t%s\t%s" % (severity, self._report[severity][True], self._report[severity][False]))
success += self._report[severity][True]
failure += self._report[severity][False]
res.append("total\t%s\t%s" % (success, failure))
res.append("end of report (%s assertion(s) checked)" % (success + failure))
return "\n".join(res)
class RecordDictWrapper(dict):
"""
Used to pass a record as locals in eval:
@@ -125,13 +100,15 @@ class RecordDictWrapper(dict):
return dict.__getitem__(self, key)
class YamlInterpreter(object):
def __init__(self, cr, module, id_map, mode, filename, noupdate=False):
def __init__(self, cr, module, id_map, mode, filename, report=None, noupdate=False):
self.cr = cr
self.module = module
self.id_map = id_map
self.mode = mode
self.filename = filename
self.assert_report = TestReport()
if report is None:
report = assertion_report.assertion_report()
self.assertion_report = report
self.noupdate = noupdate
self.pool = pooler.get_pool(cr.dbname)
self.uid = 1
@@ -210,18 +187,9 @@ class YamlInterpreter(object):
def process_comment(self, node):
return node
def _log_assert_failure(self, severity, msg, *args):
if isinstance(severity, types.StringTypes):
levelname = severity.strip().upper()
level = logging.getLevelName(levelname)
else:
level = severity
levelname = logging.getLevelName(level)
self.assert_report.record(False, levelname)
_logger.log(level, msg, *args)
if level >= config['assert_exit_level']:
raise YamlImportAbortion('Severe assertion failure (%s), aborting.' % levelname)
return
def _log_assert_failure(self, msg, *args):
self.assertion_report.record_failure()
_logger.error(msg, *args)
def _get_assertion_id(self, assertion):
if assertion.id:
@@ -250,7 +218,7 @@ class YamlInterpreter(object):
' expected count: %d\n' \
' obtained count: %d\n'
args = (assertion.string, assertion.count, len(ids))
self._log_assert_failure(assertion.severity, msg, *args)
self._log_assert_failure(msg, *args)
else:
context = self.get_context(assertion, self.eval_context)
for id in ids:
@@ -283,10 +251,10 @@ class YamlInterpreter(object):
args += ( lmsg, aop, rmsg )
break
self._log_assert_failure(assertion.severity, msg, *args)
self._log_assert_failure(msg, *args)
return
else: # all tests were successful for this assertion tag (no break)
self.assert_report.record(True, assertion.severity)
self.assertion_report.record_success()
def _coerce_bool(self, value, default=False):
if isinstance(value, types.BooleanType):
@@ -528,13 +496,13 @@ class YamlInterpreter(object):
code_obj = compile(statements, self.filename, 'exec')
unsafe_eval(code_obj, {'ref': self.get_id}, code_context)
except AssertionError, e:
self._log_assert_failure(python.severity, 'AssertionError in Python code %s: %s', python.name, e)
self._log_assert_failure('AssertionError in Python code %s: %s', python.name, e)
return
except Exception, e:
_logger.debug('Exception during evaluation of !python block in yaml_file %s.', self.filename, exc_info=True)
raise
else:
self.assert_report.record(True, python.severity)
self.assertion_report.record_success()
def process_workflow(self, node):
workflow, values = node.items()[0]
@@ -827,7 +795,7 @@ class YamlInterpreter(object):
"""
Empty node or commented node should not pass silently.
"""
self._log_assert_failure(logging.WARNING, "You have an empty block in your tests.")
self._log_assert_failure("You have an empty block in your tests.")
def process(self, yaml_string):
@@ -900,11 +868,11 @@ class YamlInterpreter(object):
is_preceded_by_comment = False
return is_preceded_by_comment
def yaml_import(cr, module, yamlfile, idref=None, mode='init', noupdate=False):
def yaml_import(cr, module, yamlfile, idref=None, mode='init', noupdate=False, report=None):
if idref is None:
idref = {}
yaml_string = yamlfile.read()
yaml_interpreter = YamlInterpreter(cr, module, idref, mode, filename=yamlfile.name, noupdate=noupdate)
yaml_interpreter = YamlInterpreter(cr, module, idref, mode, filename=yamlfile.name, report=report, noupdate=noupdate)
yaml_interpreter.process(yaml_string)
# keeps convention of convert.py
+1 -1
View File
@@ -430,7 +430,7 @@ def serve():
else:
app = application
suffix = ''
httpd = werkzeug.serving.make_server(interface, port, application, threaded=True)
httpd = werkzeug.serving.make_server(interface, port, app, threaded=True)
_logger.info('HTTP service (werkzeug) running on %s:%s%s', interface, port, suffix)
except ImportError:
import wsgiref.simple_server