[MERGE]: Merge with lp:openobject-server
bzr revid: mma@tinyerp.com-20121010054128-vxfn678x1d7i23qy bzr revid: mma@tinyerp.com-20121012051742-ds6ve0vsa08zzc3v
This commit is contained in:
commit
0a44106cff
31
.bzrignore
31
.bzrignore
|
@ -1,14 +1,19 @@
|
|||
.*
|
||||
*.egg-info
|
||||
*.orig
|
||||
*.vim
|
||||
.*.swp
|
||||
.bzrignore
|
||||
.idea
|
||||
.project
|
||||
.pydevproject
|
||||
.ropeproject
|
||||
.settings
|
||||
.DS_Store
|
||||
openerp/addons/*
|
||||
openerp/filestore*
|
||||
.Python
|
||||
*.pyc
|
||||
*.pyo
|
||||
bin/*
|
||||
build/
|
||||
RE:^bin/
|
||||
RE:^dist/
|
||||
RE:^include/
|
||||
|
||||
RE:^share/
|
||||
RE:^man/
|
||||
RE:^lib/
|
||||
|
||||
RE:^doc/_build/
|
||||
include/
|
||||
lib/
|
||||
share/
|
||||
doc/_build/*
|
||||
|
|
|
@ -3,9 +3,24 @@ User avatar
|
|||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
This revision adds an avatar for users. This replaces the use of gravatar to emulate avatars, used in views like the tasks kanban view. Two fields have been added to the res.users model:
|
||||
- avatar_big, a binary field holding the image. It is base-64 encoded, and PIL-supported. Images stored are resized to 540x450 px, to limitate the binary field size.
|
||||
- avatar, a function binary field holding an automatically resized version of the avatar_big field. It is also base-64 encoded, and PIL-supported. Dimensions of the resized avatar are 180x150. This field is used as an inteface to get and set the user avatar.
|
||||
When changing the avatar through the avatar function field, the new image is automatically resized to 540x450, and stored in the avatar_big field. This triggers the function field, that will compute a 180x150 resized version of the image.
|
||||
This revision adds an avatar for users. This replaces the use of
|
||||
gravatar to emulate avatars, used in views like the tasks kanban
|
||||
view. Two fields have been added to the res.users model:
|
||||
|
||||
An avatar field has been added to the users form view, as well as in Preferences. When creating a new user, a default avatar is chosen among 6 possible default images.
|
||||
* ``avatar_big``, a binary field holding the image. It is base-64
|
||||
encoded, and PIL-supported. Images stored are resized to 540x450 px,
|
||||
to limitate the binary field size.
|
||||
|
||||
* ``avatar``, a function binary field holding an automatically resized
|
||||
version of the avatar_big field. It is also base-64 encoded, and
|
||||
PIL-supported. Dimensions of the resized avatar are 180x150. This
|
||||
field is used as an inteface to get and set the user avatar.
|
||||
|
||||
When changing the avatar through the avatar function field, the new
|
||||
image is automatically resized to 540x450, and stored in the
|
||||
avatar_big field. This triggers the function field, that will compute
|
||||
a 180x150 resized version of the image.
|
||||
|
||||
An avatar field has been added to the users form view, as well as in
|
||||
Preferences. When creating a new user, a default avatar is chosen
|
||||
among 6 possible default images.
|
||||
|
|
22
doc/conf.py
22
doc/conf.py
|
@ -16,9 +16,10 @@ 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.insert(0, os.path.abspath('../addons'))
|
||||
sys.path.insert(0, os.path.abspath('..'))
|
||||
sys.path.append(os.path.abspath('..'))
|
||||
sys.path.append(os.path.abspath('../openerp'))
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
|
@ -42,7 +43,7 @@ source_suffix = '.rst'
|
|||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'OpenERP Web Developers Documentation'
|
||||
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
|
||||
|
@ -52,7 +53,7 @@ copyright = u'2012, OpenERP s.a.'
|
|||
# The short X.Y version.
|
||||
version = '7.0'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '7.0'
|
||||
release = '7.0b'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
@ -170,7 +171,7 @@ html_sidebars = {
|
|||
#html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'openerp-web-doc'
|
||||
htmlhelp_basename = 'openerp-server-doc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output --------------------------------------------------
|
||||
|
@ -189,7 +190,7 @@ latex_elements = {
|
|||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'openerp-web-doc.tex', u'OpenERP Web Developers Documentation',
|
||||
('index', 'openerp-server-doc.tex', u'OpenERP Server Developers Documentation',
|
||||
u'OpenERP s.a.', 'manual'),
|
||||
]
|
||||
|
||||
|
@ -219,7 +220,7 @@ latex_documents = [
|
|||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'openerp-web-doc', u'OpenERP Web Developers Documentation',
|
||||
('index', 'openerp-server-doc', u'OpenERP Server Developers Documentation',
|
||||
[u'OpenERP s.a.'], 1)
|
||||
]
|
||||
|
||||
|
@ -233,8 +234,8 @@ man_pages = [
|
|||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', 'OpenERPWebDocumentation', u'OpenERP Web Developers Documentation',
|
||||
u'OpenERP s.a.', 'OpenERPWebDocumentation', 'Developers documentation for the openerp-web project.',
|
||||
('index', 'OpenERPServerDocumentation', u'OpenERP Server Developers Documentation',
|
||||
u'OpenERP s.a.', 'OpenERPServerDocumentation', 'Developers documentation for the openobject-server project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
|
@ -247,11 +248,10 @@ texinfo_documents = [
|
|||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
#texinfo_show_urls = 'footnote'
|
||||
|
||||
todo_include_todos = True
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
intersphinx_mapping = {
|
||||
'python': ('http://docs.python.org/', None),
|
||||
'openerpserver': ('http://doc.openerp.com/trunk/developers/server', None),
|
||||
'openerpweb': ('http://doc.openerp.com/trunk/developers/web', None),
|
||||
'openerpdev': ('http://doc.openerp.com/trunk/developers', None),
|
||||
}
|
||||
|
|
|
@ -0,0 +1,263 @@
|
|||
.. _bulk-import:
|
||||
|
||||
Bulk Import
|
||||
===========
|
||||
|
||||
OpenERP has included a bulk import facility for CSV-ish files for a
|
||||
long time. With 7.0, both the interface and internal implementation
|
||||
have been redone, resulting in
|
||||
:meth:`~openerp.osv.orm.BaseModel.load`.
|
||||
|
||||
.. note::
|
||||
|
||||
the previous bulk-loading method,
|
||||
:meth:`~openerp.osv.orm.BaseModel.import_data`, remains for
|
||||
backwards compatibility but was re-implemented on top of
|
||||
:meth:`~openerp.osv.orm.BaseModel.load`, while its interface is
|
||||
unchanged its precise behavior has likely been altered for some
|
||||
cases (it shouldn't throw exceptions anymore in many cases where
|
||||
it previously did)
|
||||
|
||||
This document attempts to explain the behavior and limitations of
|
||||
:meth:`~openerp.osv.orm.BaseModel.load`.
|
||||
|
||||
Data
|
||||
----
|
||||
|
||||
The input ``data`` is a regular row-major matrix of strings (in Python
|
||||
datatype terms, a ``list`` of rows, each row being a ``list`` of
|
||||
``str``, all rows must be of equal length). Each row must be the same
|
||||
length as the ``fields`` list preceding it in the argslist.
|
||||
|
||||
Each field of ``fields`` maps to a (potentially relational and nested)
|
||||
field of the model under import, and the corresponding column of the
|
||||
``data`` matrix provides a value for the field for each record.
|
||||
|
||||
Generally speaking each row of the input yields a record of output,
|
||||
and each cell of a row yields a value for the corresponding field of
|
||||
the row's record. There is currently one exception for this rule:
|
||||
|
||||
One to Many fields
|
||||
++++++++++++++++++
|
||||
|
||||
Because O2M fields contain multiple records "embedded" in the main
|
||||
one, and these sub-records are fully dependent on the main record (are
|
||||
no other references to the sub-records in the system), they have to be
|
||||
spliced into the matrix somehow. This is done by adding lines composed
|
||||
*only* of o2m record fields below the main record:
|
||||
|
||||
.. literalinclude:: o2m.txt
|
||||
|
||||
the sections in double-lines represent the span of two o2m
|
||||
fields. During parsing, they are extracted into their own ``data``
|
||||
matrix for the o2m field they correspond to.
|
||||
|
||||
Import process
|
||||
--------------
|
||||
|
||||
Here are the phases of import. Note that the concept of "phases" is
|
||||
fuzzy as it's currently more of a pipeline, each record moves through
|
||||
the entire pipeline before the next one is processed.
|
||||
|
||||
Extraction
|
||||
++++++++++
|
||||
|
||||
The first phase of the import is the extraction of the current row
|
||||
(and potentially a section of rows following it if it has One to Many
|
||||
fields) into a record dictionary. The keys are the ``fields``
|
||||
originally passed to :meth:`~openerp.osv.orm.BaseModel.load`, and the
|
||||
values are either the string value at the corresponding cell (for
|
||||
non-relational fields) or a list of sub-records (for all relational
|
||||
fields).
|
||||
|
||||
This phase also generates the ``rows`` indexes for any
|
||||
:ref:`import-message` produced thereafter.
|
||||
|
||||
Conversion
|
||||
++++++++++
|
||||
|
||||
This second phase takes the record dicts, extracts the :ref:`dbid` and
|
||||
:ref:`xid` if present and attempts to convert each field to a type
|
||||
matching what OpenERP expects to write.
|
||||
|
||||
* Empty fields (empty strings) are replaced with the ``False`` value
|
||||
|
||||
* Non-empty fields are converted through
|
||||
:class:`~openerp.addons.base.ir.ir_fields.ir_fields_converter`
|
||||
|
||||
.. note:: if a field is specified in the import, its default will *never* be
|
||||
used. If some records need to have a value and others need to use
|
||||
the model's default, either specify that default explicitly or do
|
||||
the import in two phases.
|
||||
|
||||
Char, text and binary fields
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Are returned as-is, without any alteration.
|
||||
|
||||
Boolean fields
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
The string value is compared (in a case-insensitive manner) to ``0``,
|
||||
``false`` and ``no`` as well of any translation thereof loaded in the
|
||||
database. If the value matches one of these, the field is set to
|
||||
``False``.
|
||||
|
||||
Otherwise the field is compared to ``1``, ``true`` and ``yes`` (and
|
||||
any translation of these in the database). The field is always set to
|
||||
``True``, but if the value does not match one of these a warning will
|
||||
also be output.
|
||||
|
||||
Integers and float fields
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The field is parsed with Python's built-in conversion routines
|
||||
(``int`` and ``float`` respectively), if the conversion fails an error
|
||||
is generated.
|
||||
|
||||
Selection fields
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
The field is compared to 1. the values of the selection (first part of
|
||||
each selection tuple) and 2. all translations of the selection label
|
||||
found in the database.
|
||||
|
||||
If one of these is matched, the corresponding value is set on the
|
||||
field.
|
||||
|
||||
Otherwise an error is generated.
|
||||
|
||||
The same process applies to both list-type and function-type selection
|
||||
fields.
|
||||
|
||||
Many to One field
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
If the specified field is the relational field itself (``m2o``), the
|
||||
value is used in a ``name_search``. The first record returned by
|
||||
``name_search`` is used as the field's value.
|
||||
|
||||
If ``name_search`` finds no value, an error is generated. If
|
||||
``name_search`` finds multiple value, a warning is generated to warn
|
||||
the user of ``name_search`` collisions.
|
||||
|
||||
If the specified field is a :ref:`xid` (``m2o/id``), the
|
||||
corresponding record it looked up in the database and used as the
|
||||
field's value. If no record is found matching the provided external
|
||||
ID, an error is generated.
|
||||
|
||||
If the specified field is a :ref:`dbid` (``m2o/.id``), the process is
|
||||
the same as for external ids (on database identifiers instead of
|
||||
external ones).
|
||||
|
||||
Many to Many field
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The field's value is interpreted as a comma-separated list of names,
|
||||
external ids or database ids. For each one, the process previously
|
||||
used for the many to one field is applied.
|
||||
|
||||
One to Many field
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
For each o2m record extracted, if the record has a ``name``,
|
||||
:ref:`xid` or :ref:`dbid` the :ref:`dbid` is looked up and checked
|
||||
through the same process as for m2o fields.
|
||||
|
||||
If a :ref:`dbid` was found, a LINK_TO command is emmitted, followed by
|
||||
an UPDATE with the non-db values for the relational field.
|
||||
|
||||
Otherwise a CREATE command is emmitted.
|
||||
|
||||
Date fields
|
||||
~~~~~~~~~~~
|
||||
|
||||
The value's format is checked against
|
||||
:data:`~openerp.tools.misc.DEFAULT_SERVER_DATE_FORMAT`, an error is
|
||||
generated if it does not match the specified format.
|
||||
|
||||
Datetime fields
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
The value's format is checked against
|
||||
:data:`~openerp.tools.misc.DEFAULT_SERVER_DATETIME_FORMAT`, an error
|
||||
is generated if it does not match.
|
||||
|
||||
The value is then interpreted as a datetime in the user's
|
||||
timezone. The timezone is specified thus:
|
||||
|
||||
* If the import ``context`` contains a ``tz`` key with a valid
|
||||
timezone name, this is the timezone of the datetime.
|
||||
|
||||
* Otherwise if the user performing the import has a ``tz`` attribute
|
||||
set to a valid timezone name, this is the timezone of the datetime.
|
||||
|
||||
* Otherwise interpret the datetime as being in the ``UTC`` timezone.
|
||||
|
||||
Create/Write
|
||||
++++++++++++
|
||||
|
||||
If the conversion was successful, the converted record is then saved
|
||||
to the database via ``(ir.model.data)._update``.
|
||||
|
||||
Error handling
|
||||
++++++++++++++
|
||||
|
||||
The import process will only catch 2 types of exceptions to convert
|
||||
them to error messages: ``ValueError`` during the conversion process,
|
||||
and sub-exceptions of ``psycopg2.Error`` during the create/write
|
||||
process.
|
||||
|
||||
The import process uses savepoint to:
|
||||
|
||||
* protect the overall transaction from the failure of each ``_update``
|
||||
call, if an ``_update`` call fails the savepoint is rolled back and
|
||||
the import process keeps going in order to obtain as many error
|
||||
messages as possible during each run.
|
||||
|
||||
* protect the import as a whole, a savepoint is created before
|
||||
starting and if any error is generated that savepoint is rolled
|
||||
back. The rest of the transaction (anything not within the import
|
||||
process) will be left untouched.
|
||||
|
||||
.. _import-message:
|
||||
.. _import-messages:
|
||||
|
||||
Messages
|
||||
--------
|
||||
|
||||
A message is a dictionary with 5 mandatory keys and one optional key:
|
||||
|
||||
``type``
|
||||
the type of message, either ``warning`` or ``error``. Any
|
||||
``error`` message indicates the import failed and was rolled back.
|
||||
|
||||
``message``
|
||||
the message's actual text, which should be translated and can be
|
||||
shown to the user directly
|
||||
|
||||
``rows``
|
||||
a dict with 2 keys ``from`` and ``to``, indicates the range of
|
||||
rows in ``data`` which generated the message
|
||||
|
||||
``record``
|
||||
a single integer, for warnings the index of the record which
|
||||
generated the message (can be obtained from a non-false ``ids``
|
||||
result)
|
||||
|
||||
``field``
|
||||
the name of the (logical) OpenERP field for which the error or
|
||||
warning was generated
|
||||
|
||||
``moreinfo`` (optional)
|
||||
A string, a list or a dict, leading to more information about the
|
||||
warning.
|
||||
|
||||
* If ``moreinfo`` is a string, it is a supplementary warnings
|
||||
message which should be hidden by default
|
||||
* If ``moreinfo`` is a list, it provides a number of possible or
|
||||
alternative values for the string
|
||||
* If ``moreinfo`` is a dict, it is an OpenERP action descriptor
|
||||
which can be executed to get more information about the issues
|
||||
with the field. If present, the ``help`` key serves as a label
|
||||
for the action (e.g. the text of the link).
|
|
@ -5,6 +5,7 @@ OpenERP Server
|
|||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
import
|
||||
test-framework
|
||||
|
||||
Changed in 7.0
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
+-------+-------+===========+===========+-------+-------+
|
||||
|value01|value02‖o2m/value01|o2m/value02‖value03|value04|
|
||||
+-------+-------+-----------+-----------+-------+-------+
|
||||
| | ‖o2m/value11|o2m/value12‖ | |
|
||||
+-------+-------+-----------+-----------+-------+-------+
|
||||
| | ‖o2m/value21|o2m/value22‖ | |
|
||||
+-------+-------+===========+===========+-------+-------+
|
||||
|value11|value12‖o2m/value01|o2m/value02‖value13|value14|
|
||||
+-------+-------+-----------+-----------+-------+-------+
|
||||
| | ‖o2m/value11|o2m/value12‖ | |
|
||||
+-------+-------+===========+===========+-------+-------+
|
||||
|value21|value22| | |value23|value24|
|
||||
+-------+-------+-----------+-----------+-------+-------+
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:09+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:00+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:09+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:00+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:00+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:00+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:01+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:01+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:11+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:01+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:11+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:01+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
"X-Poedit-Language: Czech\n"
|
||||
|
||||
#. module: base
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:11+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:01+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:12+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:02+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -12,8 +12,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:12+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:03+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
"X-Poedit-Country: GREECE\n"
|
||||
"X-Poedit-Language: Greek\n"
|
||||
"X-Poedit-SourceCharset: utf-8\n"
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:17+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:08+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:16+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:06+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:17+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:08+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:17+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:08+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:18+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:08+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
"Language: \n"
|
||||
|
||||
#. module: base
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:18+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:09+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:18+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:09+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:11+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:02+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:00+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -9,8 +9,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:14+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:05+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
"X-Poedit-Country: IRAN, ISLAMIC REPUBLIC OF\n"
|
||||
"X-Poedit-Language: Persian\n"
|
||||
|
||||
|
@ -165,7 +165,7 @@ msgstr "پنجره هدف"
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_sale_analytic_plans
|
||||
msgid "Sales Analytic Distribution"
|
||||
msgstr ""
|
||||
msgstr "تحلیل توزیع فروش"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_web_process
|
||||
|
@ -175,7 +175,7 @@ msgstr "روند"
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_analytic_journal_billing_rate
|
||||
msgid "Billing Rates on Contracts"
|
||||
msgstr ""
|
||||
msgstr "نرخ صورت حساب در قراردادها"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/res/res_users.py:558
|
||||
|
@ -195,7 +195,7 @@ msgstr ""
|
|||
#: code:addons/osv.py:129
|
||||
#, python-format
|
||||
msgid "Constraint Error"
|
||||
msgstr ""
|
||||
msgstr "خطا در محدودیت"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_ir_ui_view_custom
|
||||
|
@ -286,7 +286,7 @@ msgstr "تعداد پیمانهها"
|
|||
#. module: base
|
||||
#: help:multi_company.default,company_dest_id:0
|
||||
msgid "Company to store the current record"
|
||||
msgstr ""
|
||||
msgstr "شرکت ذخیره رکورد جاری"
|
||||
|
||||
#. module: base
|
||||
#: field:res.partner.bank.type.field,size:0
|
||||
|
@ -361,7 +361,7 @@ msgstr "مدیریت شریک"
|
|||
#. module: base
|
||||
#: model:ir.module.category,name:base.module_category_customer_relationship_management
|
||||
msgid "Customer Relationship Management"
|
||||
msgstr ""
|
||||
msgstr "مدیریت ارتباط با مشتری"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.module.module:0
|
||||
|
@ -397,7 +397,7 @@ msgstr "تاریخ بروزرسانی"
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_base_action_rule
|
||||
msgid "Automated Action Rules"
|
||||
msgstr ""
|
||||
msgstr "قوانین عملیات خودکار"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.attachment:0
|
||||
|
@ -527,7 +527,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: view:ir.values:0
|
||||
msgid "Action Binding"
|
||||
msgstr ""
|
||||
msgstr "عمل اجباری"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.gf
|
||||
|
@ -659,7 +659,7 @@ msgstr "تَردستها"
|
|||
#. module: base
|
||||
#: model:res.partner.category,name:base.res_partner_category_miscellaneoussuppliers0
|
||||
msgid "Miscellaneous Suppliers"
|
||||
msgstr ""
|
||||
msgstr "تولیدکنندگان متفرقه"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/ir/ir_model.py:287
|
||||
|
@ -755,7 +755,7 @@ msgstr "حسابداری - رومانی"
|
|||
#. module: base
|
||||
#: view:partner.wizard.ean.check:0
|
||||
msgid "Want to check Ean ? "
|
||||
msgstr ""
|
||||
msgstr "آیا میخواهید بررسی شود؟ "
|
||||
|
||||
#. module: base
|
||||
#: help:ir.actions.server,mobile:0
|
||||
|
@ -771,7 +771,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: view:ir.mail_server:0
|
||||
msgid "Security and Authentication"
|
||||
msgstr ""
|
||||
msgstr "امنیت و احراز هویت"
|
||||
|
||||
#. module: base
|
||||
#: view:base.language.export:0
|
||||
|
@ -814,32 +814,32 @@ msgstr "کامبوج، پادشاهی"
|
|||
#: field:base.language.import,overwrite:0
|
||||
#: field:base.language.install,overwrite:0
|
||||
msgid "Overwrite Existing Terms"
|
||||
msgstr ""
|
||||
msgstr "بازنویسی قوانین موجود"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_base_language_import
|
||||
msgid "Language Import"
|
||||
msgstr ""
|
||||
msgstr "ورود زبان"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.cron,interval_number:0
|
||||
msgid "Repeat every x."
|
||||
msgstr ""
|
||||
msgstr "تکرار هر X."
|
||||
|
||||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
msgid "Albanian / Shqip"
|
||||
msgstr ""
|
||||
msgstr "آلبانیایی / Shqip"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.ui.menu,name:base.menu_crm_config_opportunity
|
||||
msgid "Opportunities"
|
||||
msgstr ""
|
||||
msgstr "فرصت"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_base_language_export
|
||||
msgid "base.language.export"
|
||||
msgstr ""
|
||||
msgstr "پایه.زبان.خروج"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.actions.server,write_id:0
|
||||
|
@ -858,7 +858,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_document_webdav
|
||||
msgid "Shared Repositories (WebDAV)"
|
||||
msgstr ""
|
||||
msgstr "منابع مشترک"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_import_google
|
||||
|
@ -870,7 +870,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: view:res.users:0
|
||||
msgid "Email Preferences"
|
||||
msgstr ""
|
||||
msgstr "تنظیمات ایمیل"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_audittrail
|
||||
|
@ -904,7 +904,7 @@ msgstr "همکاران من"
|
|||
#. module: base
|
||||
#: view:ir.actions.report.xml:0
|
||||
msgid "XML Report"
|
||||
msgstr ""
|
||||
msgstr "گزارش XML"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.es
|
||||
|
@ -914,7 +914,7 @@ msgstr "اسپانیا"
|
|||
#. module: base
|
||||
#: view:base.module.update:0
|
||||
msgid "Please be patient, as this operation may take a few seconds..."
|
||||
msgstr ""
|
||||
msgstr "لطفا منتظر بمانید ممکن است این عملیات طولانی شود."
|
||||
|
||||
#. module: base
|
||||
#: help:ir.actions.act_window,domain:0
|
||||
|
@ -926,12 +926,12 @@ msgstr ""
|
|||
#: model:ir.actions.act_window,name:base.action_view_base_module_upgrade
|
||||
#: model:ir.model,name:base.model_base_module_upgrade
|
||||
msgid "Module Upgrade"
|
||||
msgstr ""
|
||||
msgstr "به روز رسانی ماژول"
|
||||
|
||||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
msgid "Spanish (UY) / Español (UY)"
|
||||
msgstr ""
|
||||
msgstr "اسپانیایی / Español (UY)"
|
||||
|
||||
#. module: base
|
||||
#: field:res.partner,mobile:0
|
||||
|
@ -947,7 +947,7 @@ msgstr "عمان"
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_mrp
|
||||
msgid "MRP"
|
||||
msgstr ""
|
||||
msgstr "برنامه ریزی ساخت منابع"
|
||||
|
||||
#. module: base
|
||||
#: report:ir.module.reference.graph:0
|
||||
|
@ -962,7 +962,7 @@ msgstr "نیوئه"
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_membership
|
||||
msgid "Membership Management"
|
||||
msgstr ""
|
||||
msgstr "مدیریت عضویت"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.module.module,license:0
|
||||
|
@ -984,12 +984,12 @@ msgstr "هند"
|
|||
#: model:ir.actions.act_window,name:base.res_request_link-act
|
||||
#: model:ir.ui.menu,name:base.menu_res_request_link_act
|
||||
msgid "Request Reference Types"
|
||||
msgstr ""
|
||||
msgstr "درخواست انواع مرجع"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_google_base_account
|
||||
msgid "Google Users"
|
||||
msgstr ""
|
||||
msgstr "کاربران گوگل"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.server.object.lines,value:0
|
||||
|
@ -1026,6 +1026,8 @@ msgstr "بایگانی TGZ"
|
|||
msgid ""
|
||||
"Users added to this group are automatically added in the following groups."
|
||||
msgstr ""
|
||||
"کاربرانی که به این گروه اضافه میشوند به صورت خودکار به گروه های زیر افزوده "
|
||||
"میشوند."
|
||||
|
||||
#. module: base
|
||||
#: view:res.lang:0
|
||||
|
@ -1052,7 +1054,7 @@ msgstr "نوع"
|
|||
#. module: base
|
||||
#: field:ir.mail_server,smtp_user:0
|
||||
msgid "Username"
|
||||
msgstr ""
|
||||
msgstr "نام کاربری"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/orm.py:398
|
||||
|
@ -1071,13 +1073,13 @@ msgstr "گوام (آمریکا)"
|
|||
#: code:addons/base/res/res_users.py:558
|
||||
#, python-format
|
||||
msgid "Setting empty passwords is not allowed for security reasons!"
|
||||
msgstr ""
|
||||
msgstr "به دلایل امنیتی کلمه عبور نمیتواند خالی باشد!"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/ir/ir_mail_server.py:192
|
||||
#, python-format
|
||||
msgid "Connection test failed!"
|
||||
msgstr ""
|
||||
msgstr "آزمایش ارتباط ناموفق بود!"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.actions.server,state:0
|
||||
|
@ -1116,7 +1118,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: field:ir.module.module,contributors:0
|
||||
msgid "Contributors"
|
||||
msgstr ""
|
||||
msgstr "شرکت کنندگان"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_project_planning
|
||||
|
@ -1147,7 +1149,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
msgid "Slovak / Slovenský jazyk"
|
||||
msgstr ""
|
||||
msgstr "اسلواکی / Slovenský jazyk"
|
||||
|
||||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
|
@ -1162,7 +1164,7 @@ msgstr "اوگاندا"
|
|||
#. module: base
|
||||
#: field:ir.model.access,perm_unlink:0
|
||||
msgid "Delete Access"
|
||||
msgstr ""
|
||||
msgstr "حذف دسترسی"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.ne
|
||||
|
@ -1172,7 +1174,7 @@ msgstr "نیجر"
|
|||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
msgid "Chinese (HK)"
|
||||
msgstr ""
|
||||
msgstr "چینی"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.ba
|
||||
|
@ -1190,17 +1192,17 @@ msgstr ""
|
|||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
msgid "Spanish (GT) / Español (GT)"
|
||||
msgstr ""
|
||||
msgstr "اسپانیایی / Español (GT)"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.mail_server,smtp_port:0
|
||||
msgid "SMTP Port"
|
||||
msgstr ""
|
||||
msgstr "پورت SMTP"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_import_sugarcrm
|
||||
msgid "SugarCRM Import"
|
||||
msgstr ""
|
||||
msgstr "ورود SugarCRM"
|
||||
|
||||
#. module: base
|
||||
#: view:res.lang:0
|
||||
|
@ -1217,12 +1219,12 @@ msgstr ""
|
|||
#: code:addons/base/module/wizard/base_language_install.py:55
|
||||
#, python-format
|
||||
msgid "Language Pack"
|
||||
msgstr ""
|
||||
msgstr "بسته زبان"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_web_tests
|
||||
msgid "Tests"
|
||||
msgstr ""
|
||||
msgstr "آزمونها"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.ui.view_sc,res_id:0
|
||||
|
@ -1242,7 +1244,7 @@ msgstr "URL کنش"
|
|||
#. module: base
|
||||
#: field:base.module.import,module_name:0
|
||||
msgid "Module Name"
|
||||
msgstr ""
|
||||
msgstr "نام ماژول"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.mh
|
||||
|
@ -1253,7 +1255,7 @@ msgstr "جزایر مارشال"
|
|||
#: code:addons/base/ir/ir_model.py:368
|
||||
#, python-format
|
||||
msgid "Changing the model of a field is forbidden!"
|
||||
msgstr ""
|
||||
msgstr "تغییر مدل این فیلد ممنوع است"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.ht
|
||||
|
@ -1279,13 +1281,13 @@ msgstr ""
|
|||
#. module: base
|
||||
#: field:ir.module.category,parent_id:0
|
||||
msgid "Parent Application"
|
||||
msgstr ""
|
||||
msgstr "برنامه اصلی"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/res/res_users.py:222
|
||||
#, python-format
|
||||
msgid "Operation Canceled"
|
||||
msgstr ""
|
||||
msgstr "عملیات لغوشده"
|
||||
|
||||
#. module: base
|
||||
#: help:base.language.export,lang:0
|
||||
|
@ -1296,17 +1298,17 @@ msgstr "برای برونش زبانی نو، چیزی را برنگزینید."
|
|||
#: model:ir.module.module,shortdesc:base.module_document
|
||||
#: model:ir.module.module,shortdesc:base.module_knowledge
|
||||
msgid "Document Management System"
|
||||
msgstr ""
|
||||
msgstr "سیستم مدیریت اسناد"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_crm_claim
|
||||
msgid "Claims Management"
|
||||
msgstr ""
|
||||
msgstr "مدیریت ادعا"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.ui.menu,name:base.menu_purchase_root
|
||||
msgid "Purchases"
|
||||
msgstr ""
|
||||
msgstr "خریدها"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.md
|
||||
|
@ -1349,7 +1351,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_account_sequence
|
||||
msgid "Entries Sequence Numbering"
|
||||
msgstr ""
|
||||
msgstr "مرتب سازی ورودی ها"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_ir_exports
|
||||
|
@ -1416,7 +1418,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: view:wizard.ir.model.menu.create:0
|
||||
msgid "Create _Menu"
|
||||
msgstr ""
|
||||
msgstr "ایجاد منو"
|
||||
|
||||
#. module: base
|
||||
#: field:res.payterm,name:0
|
||||
|
@ -1461,12 +1463,12 @@ msgstr ""
|
|||
msgid ""
|
||||
"If you check this box, your customized translations will be overwritten and "
|
||||
"replaced by the official ones."
|
||||
msgstr ""
|
||||
msgstr "اگر این گزینه فعال باشد، ترجمه شما جایگزین ترجمه های قبلی میشود."
|
||||
|
||||
#. module: base
|
||||
#: field:ir.actions.report.xml,report_rml:0
|
||||
msgid "Main report file path"
|
||||
msgstr ""
|
||||
msgstr "مسیر فایل گزارش اصلی"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.actions.act_window,name:base.ir_action_report_xml
|
||||
|
@ -1512,7 +1514,7 @@ msgstr "ورود"
|
|||
#: model:ir.actions.act_window,name:base.action_wizard_update_translations
|
||||
#: model:ir.ui.menu,name:base.menu_wizard_update_translations
|
||||
msgid "Synchronize Terms"
|
||||
msgstr ""
|
||||
msgstr "هماهنگ سازی قوانین"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.actions.server:0
|
||||
|
@ -1542,18 +1544,18 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:ir.ui.menu,name:base.menu_tools
|
||||
msgid "Tools"
|
||||
msgstr ""
|
||||
msgstr "ابزار"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.property,type:0
|
||||
msgid "Float"
|
||||
msgstr ""
|
||||
msgstr "شناور"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.category,name:base.module_category_warehouse_management
|
||||
#: model:ir.module.module,shortdesc:base.module_stock
|
||||
msgid "Warehouse Management"
|
||||
msgstr ""
|
||||
msgstr "مدیریت انبار"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_res_request_link
|
||||
|
@ -1570,7 +1572,7 @@ msgstr "آگهگان تردست"
|
|||
#: model:ir.actions.act_window,name:base.action_wizard_lang_export
|
||||
#: model:ir.ui.menu,name:base.menu_wizard_lang_export
|
||||
msgid "Export Translation"
|
||||
msgstr ""
|
||||
msgstr "خروج ترجمه"
|
||||
|
||||
#. module: base
|
||||
#: help:res.log,secondary:0
|
||||
|
@ -1582,7 +1584,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_l10n_lu
|
||||
msgid "Luxembourg - Accounting"
|
||||
msgstr ""
|
||||
msgstr "حسابداری - لوکزامبرگ"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.tp
|
||||
|
@ -1753,27 +1755,27 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_html_view
|
||||
msgid "Html View"
|
||||
msgstr ""
|
||||
msgstr "مشاهده HTML"
|
||||
|
||||
#. module: base
|
||||
#: field:res.currency,position:0
|
||||
msgid "Symbol position"
|
||||
msgstr ""
|
||||
msgstr "جایگاه نماد"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_process
|
||||
msgid "Enterprise Process"
|
||||
msgstr ""
|
||||
msgstr "فرآیند سازمانی"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.cron,function:0
|
||||
msgid "Name of the method to be called when this job is processed."
|
||||
msgstr ""
|
||||
msgstr "نام این روش زمانی که این عمل در حال انجام است به آن داده میشود."
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_hr_evaluation
|
||||
msgid "Employee Appraisals"
|
||||
msgstr ""
|
||||
msgstr "ارزیابی کارکنان"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.actions.server,state:0
|
||||
|
@ -1790,7 +1792,7 @@ msgstr " STOCK_COPY"
|
|||
#. module: base
|
||||
#: field:res.company,rml_footer1:0
|
||||
msgid "General Information Footer"
|
||||
msgstr ""
|
||||
msgstr "اطلاعات عمومی پاورقی"
|
||||
|
||||
#. module: base
|
||||
#: view:res.lang:0
|
||||
|
@ -1822,7 +1824,7 @@ msgstr "مدل پیوست"
|
|||
#. module: base
|
||||
#: field:res.partner.bank,footer:0
|
||||
msgid "Display on Reports"
|
||||
msgstr ""
|
||||
msgstr "نمایش در گزارش"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_l10n_cn
|
||||
|
@ -1897,7 +1899,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_account_chart
|
||||
msgid "Template of Charts of Accounts"
|
||||
msgstr ""
|
||||
msgstr "قالب نمودار حساب ها"
|
||||
|
||||
#. module: base
|
||||
#: field:res.partner.address,type:0
|
||||
|
@ -1907,7 +1909,7 @@ msgstr "نوع نشانی"
|
|||
#. module: base
|
||||
#: view:ir.ui.menu:0
|
||||
msgid "Full Path"
|
||||
msgstr ""
|
||||
msgstr "آدرس کامل"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_l10n_br
|
||||
|
@ -1973,7 +1975,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: view:ir.ui.view:0
|
||||
msgid "Advanced"
|
||||
msgstr ""
|
||||
msgstr "پیشرفته"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.fi
|
||||
|
@ -1984,12 +1986,12 @@ msgstr "فنلاند"
|
|||
#: code:addons/base/res/res_company.py:156
|
||||
#, python-format
|
||||
msgid "Website: "
|
||||
msgstr ""
|
||||
msgstr "وب سایت "
|
||||
|
||||
#. module: base
|
||||
#: model:ir.ui.menu,name:base.menu_administration
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
msgstr "تنظیمات"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.actions.act_window,view_type:0
|
||||
|
@ -2003,12 +2005,12 @@ msgstr "درخت"
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_analytic_multicurrency
|
||||
msgid "Multi-Currency in Analytic"
|
||||
msgstr ""
|
||||
msgstr "تحلیل چند ارزی"
|
||||
|
||||
#. module: base
|
||||
#: view:base.language.export:0
|
||||
msgid "https://help.launchpad.net/Translations"
|
||||
msgstr ""
|
||||
msgstr "https://help.launchpad.net/Translations"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.actions.act_window,view_mode:0
|
||||
|
@ -2020,7 +2022,7 @@ msgstr "حالت نمایش"
|
|||
msgid ""
|
||||
"Display this bank account on the footer of printed documents like invoices "
|
||||
"and sales orders."
|
||||
msgstr ""
|
||||
msgstr "نمایش حساب بانکی در پاورقی اسناد چاپی مانند فاکتورها و سفارشات"
|
||||
|
||||
#. module: base
|
||||
#: view:base.language.import:0
|
||||
|
@ -2032,7 +2034,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: view:res.log:0
|
||||
msgid "Logs"
|
||||
msgstr ""
|
||||
msgstr "گزارشات"
|
||||
|
||||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
|
@ -2042,7 +2044,7 @@ msgstr "اسپانیایی / Español"
|
|||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
msgid "Korean (KP) / 한국어 (KP)"
|
||||
msgstr ""
|
||||
msgstr "کره / 한국어 (KP)"
|
||||
|
||||
#. module: base
|
||||
#: view:base.module.update:0
|
||||
|
@ -2090,7 +2092,7 @@ msgstr "نشان"
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_l10n_cr
|
||||
msgid "Costa Rica - Accounting"
|
||||
msgstr ""
|
||||
msgstr "حسابداری - کاستاریکا"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.module.module:0
|
||||
|
@ -2120,12 +2122,12 @@ msgstr ""
|
|||
#. module: base
|
||||
#: field:ir.values,action_id:0
|
||||
msgid "Action (change only)"
|
||||
msgstr ""
|
||||
msgstr "عملیات (فقط تغییر)"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_subscription
|
||||
msgid "Recurring Documents"
|
||||
msgstr ""
|
||||
msgstr "اسناد دوره ای"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.bs
|
||||
|
@ -2163,7 +2165,7 @@ msgstr "تعداد پیمانههای بروزرسانده"
|
|||
#. module: base
|
||||
#: field:ir.cron,function:0
|
||||
msgid "Method"
|
||||
msgstr ""
|
||||
msgstr "روش"
|
||||
|
||||
#. module: base
|
||||
#: view:res.partner.event:0
|
||||
|
@ -2173,7 +2175,7 @@ msgstr "شرح کلی"
|
|||
#. module: base
|
||||
#: view:workflow.activity:0
|
||||
msgid "Workflow Activity"
|
||||
msgstr ""
|
||||
msgstr "فعالیت گردش کار"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.actions.act_window,help:base.action_ui_view
|
||||
|
@ -2185,7 +2187,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_base_setup
|
||||
msgid "Initial Setup Tools"
|
||||
msgstr ""
|
||||
msgstr "ابزار تنظیمات اولیه"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.actions.act_window,groups_id:0
|
||||
|
@ -2209,7 +2211,7 @@ msgstr "گروهها"
|
|||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
msgid "Spanish (CL) / Español (CL)"
|
||||
msgstr ""
|
||||
msgstr "اسپانیایی / Español (CL)"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.bz
|
||||
|
@ -2242,12 +2244,12 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:ir.actions.act_window,name:base.action_res_widget_wizard
|
||||
msgid "Homepage Widgets Management"
|
||||
msgstr ""
|
||||
msgstr "مدیریت ابزارک های صفحه اصلی"
|
||||
|
||||
#. module: base
|
||||
#: field:res.company,rml_header1:0
|
||||
msgid "Report Header / Company Slogan"
|
||||
msgstr ""
|
||||
msgstr "سربرگ گزارش / شعار شرکت"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.pl
|
||||
|
@ -2270,7 +2272,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: view:workflow:0
|
||||
msgid "Workflow Editor"
|
||||
msgstr ""
|
||||
msgstr "ویرایشگر گردش کار"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.module.module,state:0
|
||||
|
@ -2297,7 +2299,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: field:ir.mail_server,smtp_debug:0
|
||||
msgid "Debugging"
|
||||
msgstr ""
|
||||
msgstr "اشکالزدایی"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_crm_helpdesk
|
||||
|
@ -2317,7 +2319,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: sql_constraint:ir.ui.view_sc:0
|
||||
msgid "Shortcut for this menu already exists!"
|
||||
msgstr ""
|
||||
msgstr "میانبر این منو موجود است"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_resource
|
||||
|
@ -2337,12 +2339,12 @@ msgstr ""
|
|||
#. module: base
|
||||
#: view:ir.rule:0
|
||||
msgid "Groups (no group = global)"
|
||||
msgstr ""
|
||||
msgstr "گروه ها (بدون گروه = عمومی)"
|
||||
|
||||
#. module: base
|
||||
#: selection:res.users,view:0
|
||||
msgid "Simplified"
|
||||
msgstr ""
|
||||
msgstr "ساده شده"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.st
|
||||
|
@ -2357,7 +2359,7 @@ msgstr "سیاهه"
|
|||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
msgid "Portugese (BR) / Português (BR)"
|
||||
msgstr ""
|
||||
msgstr "پرتقالی / Português (BR)"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.bb
|
||||
|
@ -2406,7 +2408,7 @@ msgstr "نرخ کنونی"
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_idea
|
||||
msgid "Ideas"
|
||||
msgstr ""
|
||||
msgstr "ایدهها"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_sale_crm
|
||||
|
@ -2433,22 +2435,22 @@ msgstr "هدف کنش"
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_base_calendar
|
||||
msgid "Calendar Layer"
|
||||
msgstr ""
|
||||
msgstr "لایه تقویم"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.actions.report.xml,name:base.report_ir_model_overview
|
||||
msgid "Model Overview"
|
||||
msgstr ""
|
||||
msgstr "بازنگری مدل"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_product_margin
|
||||
msgid "Margins by Products"
|
||||
msgstr ""
|
||||
msgstr "حاشیه های محصولات"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.ui.menu,name:base.menu_invoiced
|
||||
msgid "Invoicing"
|
||||
msgstr ""
|
||||
msgstr "صورتحساب"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.ui.view_sc,name:0
|
||||
|
@ -2483,13 +2485,13 @@ msgstr "درونش / برونش"
|
|||
#. module: base
|
||||
#: model:ir.actions.todo.category,name:base.category_tools_customization_config
|
||||
msgid "Tools / Customization"
|
||||
msgstr ""
|
||||
msgstr "ابزار / شخصی سازی"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.model.data,res_id:0
|
||||
#: field:ir.values,res_id:0
|
||||
msgid "Record ID"
|
||||
msgstr ""
|
||||
msgstr "شناسه ثبت شده"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.actions.server,email:0
|
||||
|
@ -2567,13 +2569,13 @@ msgstr "نگاشتهای فیلد"
|
|||
#: code:addons/base/publisher_warranty/publisher_warranty.py:163
|
||||
#, python-format
|
||||
msgid "Error during communication with the publisher warranty server."
|
||||
msgstr ""
|
||||
msgstr "خطا در هنگام ارتباط با سرور ناشر گارانتی"
|
||||
|
||||
#. module: base
|
||||
#: model:res.groups,name:base.group_sale_manager
|
||||
#: model:res.groups,name:base.group_tool_manager
|
||||
msgid "Manager"
|
||||
msgstr ""
|
||||
msgstr "مدیر"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.ui.menu,name:base.menu_custom
|
||||
|
@ -2605,17 +2607,17 @@ msgstr "لیتوانی"
|
|||
#: model:ir.model,name:base.model_partner_clear_ids
|
||||
#: view:partner.clear.ids:0
|
||||
msgid "Clear IDs"
|
||||
msgstr ""
|
||||
msgstr "پاک کردن شناسه ها"
|
||||
|
||||
#. module: base
|
||||
#: view:res.groups:0
|
||||
msgid "Inherited"
|
||||
msgstr ""
|
||||
msgstr "موروثی"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.model.fields,serialization_field_id:0
|
||||
msgid "Serialization Field"
|
||||
msgstr ""
|
||||
msgstr "رشته فیلد"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.category,description:base.module_category_report_designer
|
||||
|
@ -2649,7 +2651,7 @@ msgstr ""
|
|||
#: model:ir.actions.act_window,name:base.res_log_act_window
|
||||
#: model:ir.ui.menu,name:base.menu_res_log_act_window
|
||||
msgid "Client Logs"
|
||||
msgstr ""
|
||||
msgstr "ثبت مشتری"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/orm.py:1883
|
||||
|
@ -2670,7 +2672,7 @@ msgstr ""
|
|||
#: code:addons/base/module/wizard/base_update_translations.py:38
|
||||
#, python-format
|
||||
msgid "Error!"
|
||||
msgstr ""
|
||||
msgstr "خطا!"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_l10n_fr_rib
|
||||
|
@ -2690,7 +2692,7 @@ msgstr "کنشهای تکرار"
|
|||
#. module: base
|
||||
#: help:multi_company.default,company_id:0
|
||||
msgid "Company where the user is connected"
|
||||
msgstr ""
|
||||
msgstr "شرکتی که کاربر از آن وصل شده"
|
||||
|
||||
#. module: base
|
||||
#: field:publisher_warranty.contract,date_stop:0
|
||||
|
@ -2732,7 +2734,7 @@ msgstr "جزیرهی نورفولک"
|
|||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
msgid "Korean (KR) / 한국어 (KR)"
|
||||
msgstr ""
|
||||
msgstr "کره / 한국어 (KR)"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.model.fields,model:0
|
||||
|
@ -2758,14 +2760,14 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_stock_planning
|
||||
msgid "Master Procurement Schedule"
|
||||
msgstr ""
|
||||
msgstr "کارشناس ارشد برنامه ریز تدارکات"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_ir_module_category
|
||||
#: field:ir.module.module,application:0
|
||||
#: field:res.groups,category_id:0
|
||||
msgid "Application"
|
||||
msgstr ""
|
||||
msgstr "برنامه"
|
||||
|
||||
#. module: base
|
||||
#: selection:publisher_warranty.contract,state:0
|
||||
|
@ -2804,7 +2806,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: field:ir.actions.client,params_store:0
|
||||
msgid "Params storage"
|
||||
msgstr ""
|
||||
msgstr "پارامترهای ذخیره سازی"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/module/module.py:409
|
||||
|
@ -2851,7 +2853,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:res.widget,title:base.facebook_widget
|
||||
msgid "Facebook"
|
||||
msgstr ""
|
||||
msgstr "فیسبوک"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.am
|
||||
|
@ -2862,12 +2864,12 @@ msgstr "ارمنستان"
|
|||
#: model:ir.actions.act_window,name:base.ir_property_form
|
||||
#: model:ir.ui.menu,name:base.menu_ir_property_form_all
|
||||
msgid "Configuration Parameters"
|
||||
msgstr ""
|
||||
msgstr "پارامترهای پیکربندی"
|
||||
|
||||
#. module: base
|
||||
#: constraint:ir.cron:0
|
||||
msgid "Invalid arguments"
|
||||
msgstr ""
|
||||
msgstr "آرگومانهای نامعتبر"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.se
|
||||
|
@ -2906,7 +2908,7 @@ msgstr "نوع حساب بانکی"
|
|||
#: field:res.config,config_logo:0
|
||||
#: field:res.config.installer,config_logo:0
|
||||
msgid "Image"
|
||||
msgstr ""
|
||||
msgstr "تصویر"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.actions.server:0
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:18+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:09+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:11+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:02+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:12+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:02+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:12+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:03+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:12+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:03+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:12+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:03+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:06+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
"Language: hr\n"
|
||||
|
||||
#. module: base
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:12+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:03+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
@ -218,7 +218,7 @@ msgstr "ir.ui.view.custom"
|
|||
#: code:addons/base/ir/ir_model.py:313
|
||||
#, python-format
|
||||
msgid "Renaming sparse field \"%s\" is not allowed"
|
||||
msgstr ""
|
||||
msgstr "Nem megengedett a \"%s\" ritka mező átnevezése"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sz
|
||||
|
@ -234,12 +234,12 @@ msgstr "létrehozva."
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_l10n_tr
|
||||
msgid "Turkey - Accounting"
|
||||
msgstr ""
|
||||
msgstr "Török - könyvelés"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_mrp_subproduct
|
||||
msgid "MRP Subproducts"
|
||||
msgstr ""
|
||||
msgstr "MRP altermékek"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/module/module.py:390
|
||||
|
@ -272,7 +272,7 @@ msgstr "Inuktitut / ᐃᓄᒃᑎᑐᑦ"
|
|||
#: model:ir.module.category,name:base.module_category_sales_management
|
||||
#: model:ir.module.module,shortdesc:base.module_sale
|
||||
msgid "Sales Management"
|
||||
msgstr ""
|
||||
msgstr "Értékesítés menedzsment"
|
||||
|
||||
#. module: base
|
||||
#: view:res.partner:0
|
||||
|
@ -368,12 +368,12 @@ msgstr "Varázsló Neve"
|
|||
#. module: base
|
||||
#: model:res.groups,name:base.group_partner_manager
|
||||
msgid "Partner Manager"
|
||||
msgstr ""
|
||||
msgstr "Partner kapcsolati felelős"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.category,name:base.module_category_customer_relationship_management
|
||||
msgid "Customer Relationship Management"
|
||||
msgstr ""
|
||||
msgstr "Ügyfél kapcsolati irányítás"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.module.module:0
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:00+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:13+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:03+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:13+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:03+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:13+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:04+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:13+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:04+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:12+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:02+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:13+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:04+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:13+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:04+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:14+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:04+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:14+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:04+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:14+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:04+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:14+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:05+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:14+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:05+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:11+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:01+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
@ -12085,7 +12085,7 @@ msgstr "Wizards welke worden gestart"
|
|||
#: model:ir.module.category,name:base.module_category_manufacturing
|
||||
#: model:ir.ui.menu,name:base.menu_mrp_root
|
||||
msgid "Manufacturing"
|
||||
msgstr "Productiebeheer"
|
||||
msgstr "Productie"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.km
|
||||
|
@ -15550,10 +15550,11 @@ msgid ""
|
|||
"1,06,500;[1,2,-1] will represent it to be 106,50,0;[3] will represent it as "
|
||||
"106,500. Provided ',' as the thousand separator in each case."
|
||||
msgstr ""
|
||||
"Het scheidingsformaat moet zoals [,n] zijn waarin 0 < n: -1 eindigt de "
|
||||
"scheiding. Bijv. [3,2,-1] geeft op 106500 het volgende resultaat: 1,06,500. "
|
||||
"[1,2,-1] geeft 106,50,0; [3] geeft 106,500. Er is in deze voorbeelden "
|
||||
"uitgegaan van ',' als scheidingsteken."
|
||||
"De instellingen voor het scheidingsteken heeft het formaat [,n] waarbij n > "
|
||||
"0 gerekend vanaf het decimaalteken. -1 eindigt de scheiding. Bijv. [3,2,-1] "
|
||||
"geeft op 106500 het volgende resultaat: 1,06,500. [1,2,-1] geeft 106,50,0; "
|
||||
"[3] geeft 106,500. Er is in deze voorbeelden uitgegaan van ',' als "
|
||||
"scheidingsteken."
|
||||
|
||||
#. module: base
|
||||
#: field:ir.module.module,auto_install:0
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:17+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:08+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:14+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:05+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
@ -29,7 +29,7 @@ msgstr "Inna konfiguracja"
|
|||
#. module: base
|
||||
#: selection:ir.property,type:0
|
||||
msgid "DateTime"
|
||||
msgstr ""
|
||||
msgstr "Data i godzina"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_project_mailgate
|
||||
|
@ -83,7 +83,7 @@ msgstr "Kod (np:en__US)"
|
|||
#: field:workflow.transition,wkf_id:0
|
||||
#: field:workflow.workitem,wkf_id:0
|
||||
msgid "Workflow"
|
||||
msgstr "Obieg"
|
||||
msgstr "Przebieg procesu"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.sequence,implementation:0
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:05+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:17+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:08+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:05+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:06+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:06+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:06+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:10+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:00+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:15+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:06+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:19+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:09+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:16+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:07+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:16+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:07+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:16+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:07+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:16+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:07+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:16+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:07+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:16+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:07+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:17+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:07+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:18+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:09+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:17+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:07+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -13,8 +13,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-04 05:18+0000\n"
|
||||
"X-Generator: Launchpad (build 16061)\n"
|
||||
"X-Launchpad-Export-Date: 2012-10-12 05:08+0000\n"
|
||||
"X-Generator: Launchpad (build 16130)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
|
|
|
@ -40,6 +40,7 @@ import wizard
|
|||
import ir_config_parameter
|
||||
import osv_memory_autovacuum
|
||||
import ir_mail_server
|
||||
import ir_fields
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
|
|
|
@ -1019,6 +1019,7 @@
|
|||
<tree string="External Identifiers">
|
||||
<field name="complete_name"/>
|
||||
<field name="display_name"/>
|
||||
<field name="res_id"/>
|
||||
<field name="model" groups="base.group_no_one"/>
|
||||
</tree>
|
||||
</field>
|
||||
|
|
|
@ -19,14 +19,11 @@
|
|||
#
|
||||
##############################################################################
|
||||
|
||||
import ast
|
||||
import copy
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
import tools
|
||||
from xml import dom
|
||||
|
||||
import netsvc
|
||||
from osv import fields,osv
|
||||
|
@ -47,6 +44,9 @@ class actions(osv.osv):
|
|||
'name': fields.char('Name', size=64, required=True),
|
||||
'type': fields.char('Action Type', required=True, size=32,readonly=True),
|
||||
'usage': fields.char('Action Usage', size=32),
|
||||
'help': fields.text('Action description',
|
||||
help='Optional help text for the users with a description of the target view, such as its usage and purpose.',
|
||||
translate=True),
|
||||
}
|
||||
_defaults = {
|
||||
'usage': lambda *a: False,
|
||||
|
@ -107,6 +107,7 @@ class report_xml(osv.osv):
|
|||
r['report_xsl'] and opj('addons',r['report_xsl']))
|
||||
|
||||
_name = 'ir.actions.report.xml'
|
||||
_inherit = 'ir.actions.actions'
|
||||
_table = 'ir_act_report_xml'
|
||||
_sequence = 'ir_actions_id_seq'
|
||||
_order = 'name'
|
||||
|
@ -155,6 +156,7 @@ report_xml()
|
|||
class act_window(osv.osv):
|
||||
_name = 'ir.actions.act_window'
|
||||
_table = 'ir_act_window'
|
||||
_inherit = 'ir.actions.actions'
|
||||
_sequence = 'ir_actions_id_seq'
|
||||
_order = 'name'
|
||||
|
||||
|
@ -245,9 +247,6 @@ class act_window(osv.osv):
|
|||
'filter': fields.boolean('Filter'),
|
||||
'auto_search':fields.boolean('Auto Search'),
|
||||
'search_view' : fields.function(_search_view, type='text', string='Search View'),
|
||||
'help': fields.text('Action description',
|
||||
help='Optional help text for the users with a description of the target view, such as its usage and purpose.',
|
||||
translate=True),
|
||||
'multi': fields.boolean('Action on Multiple Doc.', help="If set to true, the action will not be displayed on the right toolbar of a form view"),
|
||||
}
|
||||
|
||||
|
@ -331,6 +330,7 @@ act_wizard()
|
|||
class act_url(osv.osv):
|
||||
_name = 'ir.actions.act_url'
|
||||
_table = 'ir_act_url'
|
||||
_inherit = 'ir.actions.actions'
|
||||
_sequence = 'ir_actions_id_seq'
|
||||
_order = 'name'
|
||||
_columns = {
|
||||
|
@ -432,6 +432,7 @@ class actions_server(osv.osv):
|
|||
|
||||
_name = 'ir.actions.server'
|
||||
_table = 'ir_act_server'
|
||||
_inherit = 'ir.actions.actions'
|
||||
_sequence = 'ir_actions_id_seq'
|
||||
_order = 'sequence,name'
|
||||
_columns = {
|
||||
|
|
|
@ -0,0 +1,365 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
import functools
|
||||
import operator
|
||||
import itertools
|
||||
import time
|
||||
|
||||
import psycopg2
|
||||
import pytz
|
||||
|
||||
from openerp.osv import orm
|
||||
from openerp.tools.translate import _
|
||||
from openerp.tools.misc import DEFAULT_SERVER_DATE_FORMAT,\
|
||||
DEFAULT_SERVER_DATETIME_FORMAT
|
||||
|
||||
REFERENCING_FIELDS = set([None, 'id', '.id'])
|
||||
def only_ref_fields(record):
|
||||
return dict((k, v) for k, v in record.iteritems()
|
||||
if k in REFERENCING_FIELDS)
|
||||
def exclude_ref_fields(record):
|
||||
return dict((k, v) for k, v in record.iteritems()
|
||||
if k not in REFERENCING_FIELDS)
|
||||
|
||||
CREATE = lambda values: (0, False, values)
|
||||
UPDATE = lambda id, values: (1, id, values)
|
||||
DELETE = lambda id: (2, id, False)
|
||||
FORGET = lambda id: (3, id, False)
|
||||
LINK_TO = lambda id: (4, id, False)
|
||||
DELETE_ALL = lambda: (5, False, False)
|
||||
REPLACE_WITH = lambda ids: (6, False, ids)
|
||||
|
||||
class ConversionNotFound(ValueError): pass
|
||||
|
||||
class ir_fields_converter(orm.Model):
|
||||
_name = 'ir.fields.converter'
|
||||
|
||||
def to_field(self, cr, uid, model, column, fromtype=str, context=None):
|
||||
""" Fetches a converter for the provided column object, from the
|
||||
specified type.
|
||||
|
||||
A converter is simply a callable taking a value of type ``fromtype``
|
||||
(or a composite of ``fromtype``, e.g. list or dict) and returning a
|
||||
value acceptable for a write() on the column ``column``.
|
||||
|
||||
By default, tries to get a method on itself with a name matching the
|
||||
pattern ``_$fromtype_to_$column._type`` and returns it.
|
||||
|
||||
Converter callables can either return a value and a list of warnings
|
||||
to their caller or raise ``ValueError``, which will be interpreted as a
|
||||
validation & conversion failure.
|
||||
|
||||
ValueError can have either one or two parameters. The first parameter
|
||||
is mandatory, **must** be a unicode string and will be used as the
|
||||
user-visible message for the error (it should be translatable and
|
||||
translated). It can contain a ``field`` named format placeholder so the
|
||||
caller can inject the field's translated, user-facing name (@string).
|
||||
|
||||
The second parameter is optional and, if provided, must be a mapping.
|
||||
This mapping will be merged into the error dictionary returned to the
|
||||
client.
|
||||
|
||||
If a converter can perform its function but has to make assumptions
|
||||
about the data, it can send a warning to the user through adding an
|
||||
instance of :class:`~openerp.osv.orm.ImportWarning` to the second value
|
||||
it returns. The handling of a warning at the upper levels is the same
|
||||
as ``ValueError`` above.
|
||||
|
||||
:param column: column object to generate a value for
|
||||
:type column: :class:`fields._column`
|
||||
:param type fromtype: type to convert to something fitting for ``column``
|
||||
:param context: openerp request context
|
||||
:return: a function (fromtype -> column.write_type), if a converter is found
|
||||
:rtype: Callable | None
|
||||
"""
|
||||
# FIXME: return None
|
||||
converter = getattr(
|
||||
self, '_%s_to_%s' % (fromtype.__name__, column._type), None)
|
||||
if not converter: return None
|
||||
|
||||
return functools.partial(
|
||||
converter, cr, uid, model, column, context=context)
|
||||
|
||||
def _str_to_boolean(self, cr, uid, model, column, value, context=None):
|
||||
# all translatables used for booleans
|
||||
true, yes, false, no = _(u"true"), _(u"yes"), _(u"false"), _(u"no")
|
||||
# potentially broken casefolding? What about locales?
|
||||
trues = set(word.lower() for word in itertools.chain(
|
||||
[u'1', u"true", u"yes"], # don't use potentially translated values
|
||||
self._get_translations(cr, uid, ['code'], u"true", context=context),
|
||||
self._get_translations(cr, uid, ['code'], u"yes", context=context),
|
||||
))
|
||||
if value.lower() in trues: return True, []
|
||||
|
||||
# potentially broken casefolding? What about locales?
|
||||
falses = set(word.lower() for word in itertools.chain(
|
||||
[u'', u"0", u"false", u"no"],
|
||||
self._get_translations(cr, uid, ['code'], u"false", context=context),
|
||||
self._get_translations(cr, uid, ['code'], u"no", context=context),
|
||||
))
|
||||
if value.lower() in falses: return False, []
|
||||
|
||||
return True, [orm.ImportWarning(
|
||||
_(u"Unknown value '%s' for boolean field '%%(field)s', assuming '%s'")
|
||||
% (value, yes), {
|
||||
'moreinfo': _(u"Use '1' for yes and '0' for no")
|
||||
})]
|
||||
|
||||
def _str_to_integer(self, cr, uid, model, column, value, context=None):
|
||||
try:
|
||||
return int(value), []
|
||||
except ValueError:
|
||||
raise ValueError(
|
||||
_(u"'%s' does not seem to be an integer for field '%%(field)s'")
|
||||
% value)
|
||||
|
||||
def _str_to_float(self, cr, uid, model, column, value, context=None):
|
||||
try:
|
||||
return float(value), []
|
||||
except ValueError:
|
||||
raise ValueError(
|
||||
_(u"'%s' does not seem to be a number for field '%%(field)s'")
|
||||
% value)
|
||||
|
||||
def _str_id(self, cr, uid, model, column, value, context=None):
|
||||
return value, []
|
||||
_str_to_char = _str_to_text = _str_to_binary = _str_id
|
||||
|
||||
def _str_to_date(self, cr, uid, model, column, value, context=None):
|
||||
try:
|
||||
time.strptime(value, DEFAULT_SERVER_DATE_FORMAT)
|
||||
return value, []
|
||||
except ValueError:
|
||||
raise ValueError(
|
||||
_(u"'%s' does not seem to be a valid date for field '%%(field)s'") % value, {
|
||||
'moreinfo': _(u"Use the format '%s'") % u"2012-12-31"
|
||||
})
|
||||
|
||||
def _input_tz(self, cr, uid, context):
|
||||
# if there's a tz in context, try to use that
|
||||
if context.get('tz'):
|
||||
try:
|
||||
return pytz.timezone(context['tz'])
|
||||
except pytz.UnknownTimeZoneError:
|
||||
pass
|
||||
|
||||
# if the current user has a tz set, try to use that
|
||||
user = self.pool['res.users'].read(
|
||||
cr, uid, [uid], ['tz'], context=context)[0]
|
||||
if user['tz']:
|
||||
try:
|
||||
return pytz.timezone(user['tz'])
|
||||
except pytz.UnknownTimeZoneError:
|
||||
pass
|
||||
|
||||
# fallback if no tz in context or on user: UTC
|
||||
return pytz.UTC
|
||||
|
||||
def _str_to_datetime(self, cr, uid, model, column, value, context=None):
|
||||
if context is None: context = {}
|
||||
try:
|
||||
parsed_value = datetime.datetime.strptime(
|
||||
value, DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
except ValueError:
|
||||
raise ValueError(
|
||||
_(u"'%s' does not seem to be a valid datetime for field '%%(field)s'") % value, {
|
||||
'moreinfo': _(u"Use the format '%s'") % u"2012-12-31 23:59:59"
|
||||
})
|
||||
|
||||
input_tz = self._input_tz(cr, uid, context)# Apply input tz to the parsed naive datetime
|
||||
dt = input_tz.localize(parsed_value, is_dst=False)
|
||||
# And convert to UTC before reformatting for writing
|
||||
return dt.astimezone(pytz.UTC).strftime(DEFAULT_SERVER_DATETIME_FORMAT), []
|
||||
|
||||
def _get_translations(self, cr, uid, types, src, context):
|
||||
types = tuple(types)
|
||||
# Cache translations so they don't have to be reloaded from scratch on
|
||||
# every row of the file
|
||||
tnx_cache = cr.cache.setdefault(self._name, {})
|
||||
if tnx_cache.setdefault(types, {}) and src in tnx_cache[types]:
|
||||
return tnx_cache[types][src]
|
||||
|
||||
Translations = self.pool['ir.translation']
|
||||
tnx_ids = Translations.search(
|
||||
cr, uid, [('type', 'in', types), ('src', '=', src)], context=context)
|
||||
tnx = Translations.read(cr, uid, tnx_ids, ['value'], context=context)
|
||||
result = tnx_cache[types][src] = map(operator.itemgetter('value'), tnx)
|
||||
return result
|
||||
|
||||
def _str_to_selection(self, cr, uid, model, column, value, context=None):
|
||||
|
||||
selection = column.selection
|
||||
if not isinstance(selection, (tuple, list)):
|
||||
# FIXME: Don't pass context to avoid translations?
|
||||
# Or just copy context & remove lang?
|
||||
selection = selection(model, cr, uid)
|
||||
for item, label in selection:
|
||||
labels = self._get_translations(
|
||||
cr, uid, ('selection', 'model', 'code'), label, context=context)
|
||||
labels.append(label)
|
||||
if value == unicode(item) or value in labels:
|
||||
return item, []
|
||||
raise ValueError(
|
||||
_(u"Value '%s' not found in selection field '%%(field)s'") % (
|
||||
value), {
|
||||
'moreinfo': [label or unicode(item) for item, label in selection
|
||||
if label or item]
|
||||
})
|
||||
|
||||
|
||||
def db_id_for(self, cr, uid, model, column, subfield, value, context=None):
|
||||
""" Finds a database id for the reference ``value`` in the referencing
|
||||
subfield ``subfield`` of the provided column of the provided model.
|
||||
|
||||
:param model: model to which the column belongs
|
||||
:param column: relational column for which references are provided
|
||||
:param subfield: a relational subfield allowing building of refs to
|
||||
existing records: ``None`` for a name_get/name_search,
|
||||
``id`` for an external id and ``.id`` for a database
|
||||
id
|
||||
:param value: value of the reference to match to an actual record
|
||||
:param context: OpenERP request context
|
||||
:return: a pair of the matched database identifier (if any), the
|
||||
translated user-readable name for the field and the list of
|
||||
warnings
|
||||
:rtype: (ID|None, unicode, list)
|
||||
"""
|
||||
if context is None: context = {}
|
||||
id = None
|
||||
warnings = []
|
||||
action = {'type': 'ir.actions.act_window', 'target': 'new',
|
||||
'view_mode': 'tree,form', 'view_type': 'form',
|
||||
'views': [(False, 'tree'), (False, 'form')],
|
||||
'help': _(u"See all possible values")}
|
||||
if subfield is None:
|
||||
action['res_model'] = column._obj
|
||||
elif subfield in ('id', '.id'):
|
||||
action['res_model'] = 'ir.model.data'
|
||||
action['domain'] = [('model', '=', column._obj)]
|
||||
|
||||
RelatedModel = self.pool[column._obj]
|
||||
if subfield == '.id':
|
||||
field_type = _(u"database id")
|
||||
try: tentative_id = int(value)
|
||||
except ValueError: tentative_id = value
|
||||
try:
|
||||
if RelatedModel.search(cr, uid, [('id', '=', tentative_id)],
|
||||
context=context):
|
||||
id = tentative_id
|
||||
except psycopg2.DataError:
|
||||
# type error
|
||||
raise ValueError(
|
||||
_(u"Invalid database id '%s' for the field '%%(field)s'") % value,
|
||||
{'moreinfo': action})
|
||||
elif subfield == 'id':
|
||||
field_type = _(u"external id")
|
||||
if '.' in value:
|
||||
module, xid = value.split('.', 1)
|
||||
else:
|
||||
module, xid = context.get('_import_current_module', ''), value
|
||||
ModelData = self.pool['ir.model.data']
|
||||
try:
|
||||
_model, id = ModelData.get_object_reference(
|
||||
cr, uid, module, xid)
|
||||
except ValueError: pass # leave id is None
|
||||
elif subfield is None:
|
||||
field_type = _(u"name")
|
||||
ids = RelatedModel.name_search(
|
||||
cr, uid, name=value, operator='=', context=context)
|
||||
if ids:
|
||||
if len(ids) > 1:
|
||||
warnings.append(orm.ImportWarning(
|
||||
_(u"Found multiple matches for field '%%(field)s' (%d matches)")
|
||||
% (len(ids))))
|
||||
id, _name = ids[0]
|
||||
else:
|
||||
raise Exception(_(u"Unknown sub-field '%s'") % subfield)
|
||||
|
||||
if id is None:
|
||||
raise ValueError(
|
||||
_(u"No matching record found for %(field_type)s '%(value)s' in field '%%(field)s'")
|
||||
% {'field_type': field_type, 'value': value},
|
||||
{'moreinfo': action})
|
||||
return id, field_type, warnings
|
||||
|
||||
def _referencing_subfield(self, record):
|
||||
""" Checks the record for the subfields allowing referencing (an
|
||||
existing record in an other table), errors out if it finds potential
|
||||
conflicts (multiple referencing subfields) or non-referencing subfields
|
||||
returns the name of the correct subfield.
|
||||
|
||||
:param record:
|
||||
:return: the record subfield to use for referencing and a list of warnings
|
||||
:rtype: str, list
|
||||
"""
|
||||
# Can import by name_get, external id or database id
|
||||
fieldset = set(record.iterkeys())
|
||||
if fieldset - REFERENCING_FIELDS:
|
||||
raise ValueError(
|
||||
_(u"Can not create Many-To-One records indirectly, import the field separately"))
|
||||
if len(fieldset) > 1:
|
||||
raise ValueError(
|
||||
_(u"Ambiguous specification for field '%(field)s', only provide one of name, external id or database id"))
|
||||
|
||||
# only one field left possible, unpack
|
||||
[subfield] = fieldset
|
||||
return subfield, []
|
||||
|
||||
def _str_to_many2one(self, cr, uid, model, column, values, context=None):
|
||||
# Should only be one record, unpack
|
||||
[record] = values
|
||||
|
||||
subfield, w1 = self._referencing_subfield(record)
|
||||
|
||||
reference = record[subfield]
|
||||
id, subfield_type, w2 = self.db_id_for(
|
||||
cr, uid, model, column, subfield, reference, context=context)
|
||||
return id, w1 + w2
|
||||
|
||||
def _str_to_many2many(self, cr, uid, model, column, value, context=None):
|
||||
[record] = value
|
||||
|
||||
subfield, warnings = self._referencing_subfield(record)
|
||||
|
||||
ids = []
|
||||
for reference in record[subfield].split(','):
|
||||
id, subfield_type, ws = self.db_id_for(
|
||||
cr, uid, model, column, subfield, reference, context=context)
|
||||
ids.append(id)
|
||||
warnings.extend(ws)
|
||||
return [REPLACE_WITH(ids)], warnings
|
||||
|
||||
def _str_to_one2many(self, cr, uid, model, column, records, context=None):
|
||||
commands = []
|
||||
warnings = []
|
||||
|
||||
if len(records) == 1 and exclude_ref_fields(records[0]) == {}:
|
||||
# only one row with only ref field, field=ref1,ref2,ref3 as in
|
||||
# m2o/m2m
|
||||
record = records[0]
|
||||
subfield, ws = self._referencing_subfield(record)
|
||||
warnings.extend(ws)
|
||||
# transform [{subfield:ref1,ref2,ref3}] into
|
||||
# [{subfield:ref1},{subfield:ref2},{subfield:ref3}]
|
||||
records = ({subfield:item} for item in record[subfield].split(','))
|
||||
|
||||
for record in records:
|
||||
id = None
|
||||
refs = only_ref_fields(record)
|
||||
# there are ref fields in the record
|
||||
if refs:
|
||||
subfield, w1 = self._referencing_subfield(refs)
|
||||
warnings.extend(w1)
|
||||
reference = record[subfield]
|
||||
id, subfield_type, w2 = self.db_id_for(
|
||||
cr, uid, model, column, subfield, reference, context=context)
|
||||
warnings.extend(w2)
|
||||
|
||||
writable = exclude_ref_fields(record)
|
||||
if id:
|
||||
commands.append(LINK_TO(id))
|
||||
commands.append(UPDATE(id, writable))
|
||||
else:
|
||||
commands.append(CREATE(writable))
|
||||
|
||||
return commands, warnings
|
|
@ -39,7 +39,7 @@
|
|||
<field name="arch" type="xml">
|
||||
<search string="Search modules">
|
||||
<field name="name" filter_domain="['|', '|', ('summary', 'ilike', self), ('shortdesc', 'ilike', self), ('name',
|
||||
'ilike', self)]"/>
|
||||
'ilike', self)]" string="Module"/>
|
||||
<filter name="app" icon="terp-check" string="Apps" domain="[('application', '=', 1)]"/>
|
||||
<filter name="extra" icon="terp-check" string="Extra" domain="[('application', '=', 0)]"/>
|
||||
<separator/>
|
||||
|
|
|
@ -106,8 +106,8 @@
|
|||
<field name="zip" class="oe_inline" placeholder="ZIP"/>
|
||||
<field name="city" class="oe_inline" placeholder="City"/>
|
||||
</div>
|
||||
<field name="state_id" placeholder="State" options='{"no_open": true}'/>
|
||||
<field name="country_id" placeholder="Country" options='{"no_open": true}'/>
|
||||
<field name="state_id" placeholder="State" options='{"no_open": True}'/>
|
||||
<field name="country_id" placeholder="Country" options='{"no_open": True}'/>
|
||||
</div>
|
||||
</group>
|
||||
<group name="bank" string="Information About the Bank">
|
||||
|
|
|
@ -291,6 +291,7 @@ class res_company(osv.osv):
|
|||
<frame id="first" x1="1.3cm" y1="3.0cm" height="%s" width="19.0cm"/>
|
||||
<stylesheet>
|
||||
<paraStyle name="main_footer" fontName="DejaVu Sans" fontSize="8.0" alignment="CENTER"/>
|
||||
<paraStyle name="main_header" fontName="DejaVu Sans" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
|
||||
</stylesheet>
|
||||
<pageGraphics>
|
||||
<!-- You Logo - Change X,Y,Width and Height -->
|
||||
|
@ -303,8 +304,9 @@ class res_company(osv.osv):
|
|||
<lines>1.3cm %s 20cm %s</lines>
|
||||
<drawRightString x="20cm" y="%s">[[ company.rml_header1 ]]</drawRightString>
|
||||
<drawString x="1.3cm" y="%s">[[ company.partner_id.name ]]</drawString>
|
||||
<drawString x="1.3cm" y="%s">[[ company.partner_id.street or '' ]]</drawString>
|
||||
<drawString x="1.3cm" y="%s">[[ company.partner_id.city or '' ]] - [[ company.partner_id.country_id and company.partner_id.country_id.name or '']]</drawString>
|
||||
<place x="1.3cm" y="%s" height="1.55cm" width="15.0cm">
|
||||
<para style="main_header">[[ display_address(company.partner_id) or '' ]]</para>
|
||||
</place>
|
||||
<drawString x="1.3cm" y="%s">Phone:</drawString>
|
||||
<drawRightString x="7cm" y="%s">[[ company.partner_id.phone or '' ]]</drawRightString>
|
||||
<drawString x="1.3cm" y="%s">Mail:</drawString>
|
||||
|
@ -328,8 +330,8 @@ class res_company(osv.osv):
|
|||
</pageTemplate>
|
||||
</header>"""
|
||||
|
||||
_header_a4 = _header_main % ('23.0cm', '27.6cm', '27.7cm', '27.7cm', '27.8cm', '27.2cm', '26.8cm', '26.4cm', '26.0cm', '26.0cm', '25.6cm', '25.6cm', '25.5cm', '25.5cm')
|
||||
_header_letter = _header_main % ('21.3cm', '25.9cm', '26.0cm', '26.0cm', '26.1cm', '25.5cm', '25.1cm', '24.7cm', '24.3cm', '24.3cm', '23.9cm', '23.9cm', '23.8cm', '23.8cm')
|
||||
_header_a4 = _header_main % ('23.0cm', '27.6cm', '27.7cm', '27.7cm', '27.8cm', '27.4cm', '25.8cm', '26.0cm', '26.0cm', '25.6cm', '25.6cm', '25.5cm', '25.5cm')
|
||||
_header_letter = _header_main % ('21.3cm', '25.9cm', '26.0cm', '26.0cm', '26.1cm', '25.5cm', '25.1cm', '24.3cm', '24.3cm', '23.9cm', '23.9cm', '23.8cm', '23.8cm')
|
||||
|
||||
def onchange_paper_format(self, cr, uid, ids, paper_format, context=None):
|
||||
if paper_format == 'us_letter':
|
||||
|
|
|
@ -46,10 +46,10 @@
|
|||
<field name="street2"/>
|
||||
<div>
|
||||
<field name="city" placeholder="City" style="width: 40%%"/>
|
||||
<field name="state_id" class="oe_no_button" placeholder="State" style="width: 24%%" options='{"no_open": true}'/>
|
||||
<field name="state_id" class="oe_no_button" placeholder="State" style="width: 24%%" options='{"no_open": True}'/>
|
||||
<field name="zip" placeholder="ZIP" style="width: 34%%"/>
|
||||
</div>
|
||||
<field name="country_id" placeholder="Country" class="oe_no_button" options='{"no_open": true}' on_change="on_change_country(country_id)"/>
|
||||
<field name="country_id" placeholder="Country" class="oe_no_button" options='{"no_open": True}' on_change="on_change_country(country_id)"/>
|
||||
</div>
|
||||
<label for="rml_header1"/>
|
||||
<div>
|
||||
|
|
|
@ -73,7 +73,7 @@
|
|||
<group>
|
||||
<field name="name"/>
|
||||
<field name="code"/>
|
||||
<field name="country_id" options='{"no_open": true}'/>
|
||||
<field name="country_id" options='{"no_open": True}'/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
|
|
|
@ -161,10 +161,10 @@
|
|||
<field name="street2"/>
|
||||
<div class="address_format">
|
||||
<field name="city" placeholder="City" style="width: 40%%"/>
|
||||
<field name="state_id" class="oe_no_button" placeholder="State" style="width: 37%%" options='{"no_open": true}'/>
|
||||
<field name="state_id" class="oe_no_button" placeholder="State" style="width: 37%%" options='{"no_open": True}'/>
|
||||
<field name="zip" placeholder="ZIP" style="width: 20%%"/>
|
||||
</div>
|
||||
<field name="country_id" placeholder="Country" class="oe_no_button" options='{"no_open": true}'/>
|
||||
<field name="country_id" placeholder="Country" class="oe_no_button" options='{"no_open": True}'/>
|
||||
</div>
|
||||
<field name="website" widget="url" placeholder="e.g. www.openerp.com"/>
|
||||
</group>
|
||||
|
@ -177,7 +177,7 @@
|
|||
<field name="email" widget="email"/>
|
||||
<field name="title" domain="[('domain', '=', 'contact')]"
|
||||
groups="base.group_no_one"
|
||||
options='{"no_open": true}' attrs="{'invisible': [('is_company','=', True)]}" />
|
||||
options='{"no_open": True}' attrs="{'invisible': [('is_company','=', True)]}" />
|
||||
</group>
|
||||
</group>
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<data noupdate="1">
|
||||
|
||||
<record model="res.groups" id="group_partner_manager">
|
||||
<field name="name">Partner Manager</field>
|
||||
<field name="name">Contact Creation</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.menu" id="menu_config_address_book">
|
||||
|
|
|
@ -341,14 +341,12 @@ class res_users(osv.osv):
|
|||
|
||||
def copy(self, cr, uid, id, default=None, context=None):
|
||||
user2copy = self.read(cr, uid, [id], ['login','name'])[0]
|
||||
if default is None:
|
||||
default = {}
|
||||
copy_pattern = _("%s (copy)")
|
||||
copydef = dict(login=(copy_pattern % user2copy['login']),
|
||||
name=(copy_pattern % user2copy['name']),
|
||||
)
|
||||
copydef.update(default)
|
||||
return super(res_users, self).copy(cr, uid, id, copydef, context)
|
||||
default = dict(default or {})
|
||||
if ('name' not in default) and ('partner_id' not in default):
|
||||
default['name'] = _("%s (copy)") % user2copy['name']
|
||||
if 'login' not in default:
|
||||
default['login'] = _("%s (copy)") % user2copy['login']
|
||||
return super(res_users, self).copy(cr, uid, id, default, context)
|
||||
|
||||
def context_get(self, cr, uid, context=None):
|
||||
user = self.browse(cr, SUPERUSER_ID, uid, context)
|
||||
|
|
|
@ -192,8 +192,7 @@ def init_logger():
|
|||
default_config = [
|
||||
'openerp.netsvc.rpc.request:INFO',
|
||||
'openerp.netsvc.rpc.response:INFO',
|
||||
'openerp.addons.web.common.http:INFO',
|
||||
'openerp.addons.web.common.openerplib:INFO',
|
||||
'openerp.addons.web.http:INFO',
|
||||
'openerp.sql_db:INFO',
|
||||
':INFO',
|
||||
]
|
||||
|
|
|
@ -22,102 +22,114 @@
|
|||
|
||||
""" Domain expression processing
|
||||
|
||||
The main duty of this module is to compile a domain expression into a SQL
|
||||
query. A lot of things should be documented here, but as a first step in the
|
||||
right direction, some tests in test_osv_expression.yml might give you some
|
||||
additional information.
|
||||
The main duty of this module is to compile a domain expression into a
|
||||
SQL query. A lot of things should be documented here, but as a first
|
||||
step in the right direction, some tests in test_osv_expression.yml
|
||||
might give you some additional information.
|
||||
|
||||
For legacy reasons, a domain uses an inconsistent two-levels abstract syntax
|
||||
(domains are regular Python data structures). At the first level, a domain
|
||||
is an expression made of terms (sometimes called leaves) and (domain) operators
|
||||
used in prefix notation. The available operators at this level are '!', '&',
|
||||
and '|'. '!' is a unary 'not', '&' is a binary 'and', and '|' is a binary 'or'.
|
||||
For instance, here is a possible domain. (<term> stands for an arbitrary term,
|
||||
more on this later.)
|
||||
For legacy reasons, a domain uses an inconsistent two-levels abstract
|
||||
syntax (domains are regular Python data structures). At the first
|
||||
level, a domain is an expression made of terms (sometimes called
|
||||
leaves) and (domain) operators used in prefix notation. The available
|
||||
operators at this level are '!', '&', and '|'. '!' is a unary 'not',
|
||||
'&' is a binary 'and', and '|' is a binary 'or'. For instance, here
|
||||
is a possible domain. (<term> stands for an arbitrary term, more on
|
||||
this later.)::
|
||||
|
||||
['&', '!', <term1>, '|', <term2>, <term3>]
|
||||
|
||||
It is equivalent to this pseudo code using infix notation:
|
||||
It is equivalent to this pseudo code using infix notation::
|
||||
|
||||
(not <term1>) and (<term2> or <term3>)
|
||||
|
||||
The second level of syntax deals with the term representation. A term is
|
||||
a triple of the form (left, operator, right). That is, a term uses an infix
|
||||
notation, and the available operators, and possible left and right operands
|
||||
differ with those of the previous level. Here is a possible term:
|
||||
The second level of syntax deals with the term representation. A term
|
||||
is a triple of the form (left, operator, right). That is, a term uses
|
||||
an infix notation, and the available operators, and possible left and
|
||||
right operands differ with those of the previous level. Here is a
|
||||
possible term::
|
||||
|
||||
('company_id.name', '=', 'OpenERP')
|
||||
|
||||
The left and right operand don't have the same possible values. The left
|
||||
operand is field name (related to the model for which the domain applies).
|
||||
Actually, the field name can use the dot-notation to traverse relationships.
|
||||
The right operand is a Python value whose type should match the used operator
|
||||
and field type. In the above example, a string is used because the name field
|
||||
of a company has type string, and because we use the '=' operator. When
|
||||
appropriate, a 'in' operator can be used, and thus the right operand should be
|
||||
a list.
|
||||
The left and right operand don't have the same possible values. The
|
||||
left operand is field name (related to the model for which the domain
|
||||
applies). Actually, the field name can use the dot-notation to
|
||||
traverse relationships. The right operand is a Python value whose
|
||||
type should match the used operator and field type. In the above
|
||||
example, a string is used because the name field of a company has type
|
||||
string, and because we use the '=' operator. When appropriate, a 'in'
|
||||
operator can be used, and thus the right operand should be a list.
|
||||
|
||||
Note: the non-uniform syntax could have been more uniform, but this would hide
|
||||
an important limitation of the domain syntax. Say that the term representation
|
||||
was ['=', 'company_id.name', 'OpenERP']. Used in a complete domain, this would
|
||||
look like:
|
||||
Note: the non-uniform syntax could have been more uniform, but this
|
||||
would hide an important limitation of the domain syntax. Say that the
|
||||
term representation was ['=', 'company_id.name', 'OpenERP']. Used in a
|
||||
complete domain, this would look like::
|
||||
|
||||
['!', ['=', 'company_id.name', 'OpenERP']]
|
||||
['!', ['=', 'company_id.name', 'OpenERP']]
|
||||
|
||||
and you would be tempted to believe something like this would be possible:
|
||||
and you would be tempted to believe something like this would be
|
||||
possible::
|
||||
|
||||
['!', ['=', 'company_id.name', ['&', ..., ...]]]
|
||||
['!', ['=', 'company_id.name', ['&', ..., ...]]]
|
||||
|
||||
That is, a domain could be a valid operand. But this is not the case. A domain
|
||||
is really limited to a two-level nature, and can not take a recursive form: a
|
||||
domain is not a valid second-level operand.
|
||||
That is, a domain could be a valid operand. But this is not the
|
||||
case. A domain is really limited to a two-level nature, and can not
|
||||
take a recursive form: a domain is not a valid second-level operand.
|
||||
|
||||
Unaccent - Accent-insensitive search
|
||||
|
||||
OpenERP will use the SQL function 'unaccent' when available for the 'ilike' and
|
||||
'not ilike' operators, and enabled in the configuration.
|
||||
Normally the 'unaccent' function is obtained from the PostgreSQL 'unaccent'
|
||||
contrib module[0].
|
||||
OpenERP will use the SQL function 'unaccent' when available for the
|
||||
'ilike' and 'not ilike' operators, and enabled in the configuration.
|
||||
Normally the 'unaccent' function is obtained from `the PostgreSQL
|
||||
'unaccent' contrib module
|
||||
<http://developer.postgresql.org/pgdocs/postgres/unaccent.html>`_.
|
||||
|
||||
.. todo: The following explanation should be moved in some external
|
||||
installation guide
|
||||
|
||||
..todo: The following explanation should be moved in some external installation
|
||||
guide
|
||||
The steps to install the module might differ on specific PostgreSQL
|
||||
versions. We give here some instruction for PostgreSQL 9.x on a
|
||||
Ubuntu system.
|
||||
|
||||
The steps to install the module might differ on specific PostgreSQL versions.
|
||||
We give here some instruction for PostgreSQL 9.x on a Ubuntu system.
|
||||
Ubuntu doesn't come yet with PostgreSQL 9.x, so an alternative package
|
||||
source is used. We use Martin Pitt's PPA available at
|
||||
`ppa:pitti/postgresql
|
||||
<https://launchpad.net/~pitti/+archive/postgresql>`_.
|
||||
|
||||
Ubuntu doesn't come yet with PostgreSQL 9.x, so an alternative package source
|
||||
is used. We use Martin Pitt's PPA available at ppa:pitti/postgresql[1]. See
|
||||
[2] for instructions. Basically:
|
||||
.. code-block:: sh
|
||||
|
||||
> sudo add-apt-repository ppa:pitti/postgresql
|
||||
> sudo apt-get update
|
||||
|
||||
Once the package list is up-to-date, you have to install PostgreSQL 9.0 and
|
||||
its contrib modules.
|
||||
Once the package list is up-to-date, you have to install PostgreSQL
|
||||
9.0 and its contrib modules.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
> sudo apt-get install postgresql-9.0 postgresql-contrib-9.0
|
||||
|
||||
When you want to enable unaccent on some database:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
> psql9 <database> -f /usr/share/postgresql/9.0/contrib/unaccent.sql
|
||||
|
||||
Here 'psql9' is an alias for the newly installed PostgreSQL 9.0 tool, together
|
||||
with the correct port if necessary (for instance if PostgreSQL 8.4 is running
|
||||
on 5432). (Other aliases can be used for createdb and dropdb.)
|
||||
Here :program:`psql9` is an alias for the newly installed PostgreSQL
|
||||
9.0 tool, together with the correct port if necessary (for instance if
|
||||
PostgreSQL 8.4 is running on 5432). (Other aliases can be used for
|
||||
createdb and dropdb.)
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
> alias psql9='/usr/lib/postgresql/9.0/bin/psql -p 5433'
|
||||
|
||||
You can check unaccent is working:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
> psql9 <database> -c"select unaccent('hélène')"
|
||||
|
||||
Finally, to instruct OpenERP to really use the unaccent function, you have to
|
||||
start the server specifying the --unaccent flag.
|
||||
|
||||
[0] http://developer.postgresql.org/pgdocs/postgres/unaccent.html
|
||||
[1] https://launchpad.net/~pitti/+archive/postgresql
|
||||
[2] https://launchpad.net/+help/soyuz/ppa-sources-list.html
|
||||
start the server specifying the ``--unaccent`` flag.
|
||||
|
||||
"""
|
||||
|
||||
|
@ -232,7 +244,7 @@ def is_leaf(element, internal=False):
|
|||
""" Test whether an object is a valid domain term.
|
||||
|
||||
:param internal: allow or not the 'inselect' internal operator in the term.
|
||||
This normally should be always left to False.
|
||||
This normally should be always left to False.
|
||||
"""
|
||||
INTERNAL_OPS = TERM_OPERATORS + ('inselect',)
|
||||
return (isinstance(element, tuple) or isinstance(element, list)) \
|
||||
|
|
|
@ -1580,19 +1580,32 @@ def field_to_dict(model, cr, user, field, context=None):
|
|||
|
||||
|
||||
class column_info(object):
|
||||
"""Struct containing details about an osv column, either one local to
|
||||
its model, or one inherited via _inherits.
|
||||
""" Struct containing details about an osv column, either one local to
|
||||
its model, or one inherited via _inherits.
|
||||
|
||||
:attr name: name of the column
|
||||
:attr column: column instance, subclass of osv.fields._column
|
||||
:attr parent_model: if the column is inherited, name of the model
|
||||
that contains it, None for local columns.
|
||||
:attr parent_column: the name of the column containing the m2o
|
||||
relationship to the parent model that contains
|
||||
this column, None for local columns.
|
||||
:attr original_parent: if the column is inherited, name of the original
|
||||
parent model that contains it i.e in case of multilevel
|
||||
inheritence, None for local columns.
|
||||
.. attribute:: name
|
||||
|
||||
name of the column
|
||||
|
||||
.. attribute:: column
|
||||
|
||||
column instance, subclass of :class:`_column`
|
||||
|
||||
.. attribute:: parent_model
|
||||
|
||||
if the column is inherited, name of the model that contains it,
|
||||
``None`` for local columns.
|
||||
|
||||
.. attribute:: parent_column
|
||||
|
||||
the name of the column containing the m2o relationship to the
|
||||
parent model that contains this column, ``None`` for local columns.
|
||||
|
||||
.. attribute:: original_parent
|
||||
|
||||
if the column is inherited, name of the original parent model that
|
||||
contains it i.e in case of multilevel inheritance, ``None`` for
|
||||
local columns.
|
||||
"""
|
||||
def __init__(self, name, column, parent_model=None, parent_column=None, original_parent=None):
|
||||
self.name = name
|
||||
|
@ -1601,5 +1614,10 @@ class column_info(object):
|
|||
self.parent_column = parent_column
|
||||
self.original_parent = original_parent
|
||||
|
||||
def __str__(self):
|
||||
return '%s(%s, %s, %s, %s, %s)' % (
|
||||
self.__name__, self.name, self.column,
|
||||
self.parent_model, self.parent_column, self.original_parent)
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
"""
|
||||
|
||||
import calendar
|
||||
import collections
|
||||
import copy
|
||||
import datetime
|
||||
import itertools
|
||||
|
@ -53,18 +54,21 @@ import simplejson
|
|||
import time
|
||||
import traceback
|
||||
import types
|
||||
|
||||
import psycopg2
|
||||
from lxml import etree
|
||||
import warnings
|
||||
|
||||
import fields
|
||||
import openerp
|
||||
import openerp.netsvc as netsvc
|
||||
import openerp.tools as tools
|
||||
from openerp.tools.config import config
|
||||
from openerp.tools.misc import CountingStream
|
||||
from openerp.tools.safe_eval import safe_eval as eval
|
||||
from openerp.tools.translate import _
|
||||
from openerp import SUPERUSER_ID
|
||||
from query import Query
|
||||
from openerp import SUPERUSER_ID
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
_schema = logging.getLogger(__name__ + '.schema')
|
||||
|
@ -1217,7 +1221,11 @@ class BaseModel(object):
|
|||
return {'datas': datas}
|
||||
|
||||
def import_data(self, cr, uid, fields, datas, mode='init', current_module='', noupdate=False, context=None, filename=None):
|
||||
"""Import given data in given module
|
||||
"""
|
||||
.. deprecated:: 7.0
|
||||
Use :meth:`~load` instead
|
||||
|
||||
Import given data in given module
|
||||
|
||||
This method is used when importing data via client menu.
|
||||
|
||||
|
@ -1245,7 +1253,7 @@ class BaseModel(object):
|
|||
* The last item is currently unused, with no specific semantics
|
||||
|
||||
:param fields: list of fields to import
|
||||
:param data: data to import
|
||||
:param datas: data to import
|
||||
:param mode: 'init' or 'update' for record creation
|
||||
:param current_module: module name
|
||||
:param noupdate: flag for record creation
|
||||
|
@ -1253,194 +1261,265 @@ class BaseModel(object):
|
|||
:returns: 4-tuple in the form (return_code, errored_resource, error_message, unused)
|
||||
:rtype: (int, dict or 0, str or 0, str or 0)
|
||||
"""
|
||||
if not context:
|
||||
context = {}
|
||||
context = dict(context) if context is not None else {}
|
||||
context['_import_current_module'] = current_module
|
||||
|
||||
fields = map(fix_import_export_id_paths, fields)
|
||||
ir_model_data_obj = self.pool.get('ir.model.data')
|
||||
|
||||
# mode: id (XML id) or .id (database id) or False for name_get
|
||||
def _get_id(model_name, id, current_module=False, mode='id'):
|
||||
if mode=='.id':
|
||||
id = int(id)
|
||||
obj_model = self.pool.get(model_name)
|
||||
ids = obj_model.search(cr, uid, [('id', '=', int(id))])
|
||||
if not len(ids):
|
||||
raise Exception(_("Database ID doesn't exist: %s : %s") %(model_name, id))
|
||||
elif mode=='id':
|
||||
if '.' in id:
|
||||
module, xml_id = id.rsplit('.', 1)
|
||||
else:
|
||||
module, xml_id = current_module, id
|
||||
record_id = ir_model_data_obj._get_id(cr, uid, module, xml_id)
|
||||
ir_model_data = ir_model_data_obj.read(cr, uid, [record_id], ['res_id'])
|
||||
if not ir_model_data:
|
||||
raise ValueError('No references to %s.%s' % (module, xml_id))
|
||||
id = ir_model_data[0]['res_id']
|
||||
else:
|
||||
obj_model = self.pool.get(model_name)
|
||||
ids = obj_model.name_search(cr, uid, id, operator='=', context=context)
|
||||
if not ids:
|
||||
raise ValueError('No record found for %s' % (id,))
|
||||
id = ids[0][0]
|
||||
return id
|
||||
def log(m):
|
||||
if m['type'] == 'error':
|
||||
raise Exception(m['message'])
|
||||
|
||||
# IN:
|
||||
# datas: a list of records, each record is defined by a list of values
|
||||
# prefix: a list of prefix fields ['line_ids']
|
||||
# position: the line to process, skip is False if it's the first line of the current record
|
||||
# OUT:
|
||||
# (res, position, warning, res_id) with
|
||||
# res: the record for the next line to process (including it's one2many)
|
||||
# position: the new position for the next line
|
||||
# res_id: the ID of the record if it's a modification
|
||||
def process_liness(self, datas, prefix, current_module, model_name, fields_def, position=0, skip=0):
|
||||
line = datas[position]
|
||||
row = {}
|
||||
warning = []
|
||||
data_res_id = False
|
||||
xml_id = False
|
||||
nbrmax = position+1
|
||||
|
||||
done = {}
|
||||
for i, field in enumerate(fields):
|
||||
res = False
|
||||
if i >= len(line):
|
||||
raise Exception(_('Please check that all your lines have %d columns.'
|
||||
'Stopped around line %d having %d columns.') % \
|
||||
(len(fields), position+2, len(line)))
|
||||
if not line[i]:
|
||||
continue
|
||||
|
||||
if field[:len(prefix)] != prefix:
|
||||
if line[i] and skip:
|
||||
return False
|
||||
continue
|
||||
field_name = field[len(prefix)]
|
||||
|
||||
#set the mode for m2o, o2m, m2m : xml_id/id/name
|
||||
if len(field) == len(prefix)+1:
|
||||
mode = False
|
||||
else:
|
||||
mode = field[len(prefix)+1]
|
||||
|
||||
# TODO: improve this by using csv.csv_reader
|
||||
def many_ids(line, relation, current_module, mode):
|
||||
res = []
|
||||
for db_id in line.split(config.get('csv_internal_sep')):
|
||||
res.append(_get_id(relation, db_id, current_module, mode))
|
||||
return [(6,0,res)]
|
||||
|
||||
# ID of the record using a XML ID
|
||||
if field_name == 'id':
|
||||
try:
|
||||
data_res_id = _get_id(model_name, line[i], current_module)
|
||||
except ValueError:
|
||||
pass
|
||||
xml_id = line[i]
|
||||
continue
|
||||
|
||||
# ID of the record using a database ID
|
||||
elif field_name == '.id':
|
||||
data_res_id = _get_id(model_name, line[i], current_module, '.id')
|
||||
continue
|
||||
|
||||
field_type = fields_def[field_name]['type']
|
||||
# recursive call for getting children and returning [(0,0,{})] or [(1,ID,{})]
|
||||
if field_type == 'one2many':
|
||||
if field_name in done:
|
||||
continue
|
||||
done[field_name] = True
|
||||
relation = fields_def[field_name]['relation']
|
||||
relation_obj = self.pool.get(relation)
|
||||
newfd = relation_obj.fields_get( cr, uid, context=context )
|
||||
pos = position
|
||||
|
||||
res = []
|
||||
|
||||
first = 0
|
||||
while pos < len(datas):
|
||||
res2 = process_liness(self, datas, prefix + [field_name], current_module, relation_obj._name, newfd, pos, first)
|
||||
if not res2:
|
||||
break
|
||||
(newrow, pos, w2, data_res_id2, xml_id2) = res2
|
||||
nbrmax = max(nbrmax, pos)
|
||||
warning += w2
|
||||
first += 1
|
||||
|
||||
if (not newrow) or not reduce(lambda x, y: x or y, newrow.values(), 0):
|
||||
break
|
||||
|
||||
res.append( (data_res_id2 and 1 or 0, data_res_id2 or 0, newrow) )
|
||||
|
||||
elif field_type == 'many2one':
|
||||
relation = fields_def[field_name]['relation']
|
||||
res = _get_id(relation, line[i], current_module, mode)
|
||||
|
||||
elif field_type == 'many2many':
|
||||
relation = fields_def[field_name]['relation']
|
||||
res = many_ids(line[i], relation, current_module, mode)
|
||||
|
||||
elif field_type == 'integer':
|
||||
res = line[i] and int(line[i]) or 0
|
||||
elif field_type == 'boolean':
|
||||
res = line[i].lower() not in ('0', 'false', 'off')
|
||||
elif field_type == 'float':
|
||||
res = line[i] and float(line[i]) or 0.0
|
||||
elif field_type == 'selection':
|
||||
for key, val in fields_def[field_name]['selection']:
|
||||
if tools.ustr(line[i]) in [tools.ustr(key), tools.ustr(val)]:
|
||||
res = key
|
||||
break
|
||||
if line[i] and not res:
|
||||
_logger.warning(
|
||||
_("key '%s' not found in selection field '%s'"),
|
||||
tools.ustr(line[i]), tools.ustr(field_name))
|
||||
warning.append(_("Key/value '%s' not found in selection field '%s'") % (
|
||||
tools.ustr(line[i]), tools.ustr(field_name)))
|
||||
|
||||
else:
|
||||
res = line[i]
|
||||
|
||||
row[field_name] = res or False
|
||||
|
||||
return row, nbrmax, warning, data_res_id, xml_id
|
||||
|
||||
fields_def = self.fields_get(cr, uid, context=context)
|
||||
|
||||
position = 0
|
||||
if config.get('import_partial') and filename:
|
||||
with open(config.get('import_partial'), 'rb') as partial_import_file:
|
||||
data = pickle.load(partial_import_file)
|
||||
position = data.get(filename, 0)
|
||||
|
||||
while position<len(datas):
|
||||
(res, position, warning, res_id, xml_id) = \
|
||||
process_liness(self, datas, [], current_module, self._name, fields_def, position=position)
|
||||
if len(warning):
|
||||
cr.rollback()
|
||||
return -1, res, 'Line ' + str(position) +' : ' + '!\n'.join(warning), ''
|
||||
|
||||
try:
|
||||
position = 0
|
||||
try:
|
||||
for res_id, xml_id, res, info in self._convert_records(cr, uid,
|
||||
self._extract_records(cr, uid, fields, datas,
|
||||
context=context, log=log),
|
||||
context=context, log=log):
|
||||
ir_model_data_obj._update(cr, uid, self._name,
|
||||
current_module, res, mode=mode, xml_id=xml_id,
|
||||
noupdate=noupdate, res_id=res_id, context=context)
|
||||
except Exception, e:
|
||||
return -1, res, 'Line ' + str(position) + ' : ' + tools.ustr(e), ''
|
||||
|
||||
if config.get('import_partial') and filename and (not (position%100)):
|
||||
with open(config.get('import_partial'), 'rb') as partial_import:
|
||||
data = pickle.load(partial_import)
|
||||
data[filename] = position
|
||||
with open(config.get('import_partial'), 'wb') as partial_import:
|
||||
pickle.dump(data, partial_import)
|
||||
if context.get('defer_parent_store_computation'):
|
||||
self._parent_store_compute(cr)
|
||||
cr.commit()
|
||||
position = info.get('rows', {}).get('to', 0) + 1
|
||||
if config.get('import_partial') and filename and (not (position%100)):
|
||||
with open(config.get('import_partial'), 'rb') as partial_import:
|
||||
data = pickle.load(partial_import)
|
||||
data[filename] = position
|
||||
with open(config.get('import_partial'), 'wb') as partial_import:
|
||||
pickle.dump(data, partial_import)
|
||||
if context.get('defer_parent_store_computation'):
|
||||
self._parent_store_compute(cr)
|
||||
cr.commit()
|
||||
except Exception, e:
|
||||
cr.rollback()
|
||||
return -1, {}, 'Line %d : %s' % (position + 1, tools.ustr(e)), ''
|
||||
|
||||
if context.get('defer_parent_store_computation'):
|
||||
self._parent_store_compute(cr)
|
||||
return position, 0, 0, 0
|
||||
|
||||
def load(self, cr, uid, fields, data, context=None):
|
||||
"""
|
||||
Attempts to load the data matrix, and returns a list of ids (or
|
||||
``False`` if there was an error and no id could be generated) and a
|
||||
list of messages.
|
||||
|
||||
The ids are those of the records created and saved (in database), in
|
||||
the same order they were extracted from the file. They can be passed
|
||||
directly to :meth:`~read`
|
||||
|
||||
:param fields: list of fields to import, at the same index as the corresponding data
|
||||
:type fields: list(str)
|
||||
:param data: row-major matrix of data to import
|
||||
:type data: list(list(str))
|
||||
:param dict context:
|
||||
:returns: {ids: list(int)|False, messages: [Message]}
|
||||
"""
|
||||
cr.execute('SAVEPOINT model_load')
|
||||
messages = []
|
||||
|
||||
fields = map(fix_import_export_id_paths, fields)
|
||||
ModelData = self.pool['ir.model.data']
|
||||
fg = self.fields_get(cr, uid, context=context)
|
||||
|
||||
mode = 'init'
|
||||
current_module = ''
|
||||
noupdate = False
|
||||
|
||||
ids = []
|
||||
for id, xid, record, info in self._convert_records(cr, uid,
|
||||
self._extract_records(cr, uid, fields, data,
|
||||
context=context, log=messages.append),
|
||||
context=context, log=messages.append):
|
||||
try:
|
||||
cr.execute('SAVEPOINT model_load_save')
|
||||
except psycopg2.InternalError, e:
|
||||
# broken transaction, exit and hope the source error was
|
||||
# already logged
|
||||
if not any(message['type'] == 'error' for message in messages):
|
||||
messages.append(dict(info, type='error',message=
|
||||
u"Unknown database error: '%s'" % e))
|
||||
break
|
||||
try:
|
||||
ids.append(ModelData._update(cr, uid, self._name,
|
||||
current_module, record, mode=mode, xml_id=xid,
|
||||
noupdate=noupdate, res_id=id, context=context))
|
||||
cr.execute('RELEASE SAVEPOINT model_load_save')
|
||||
except psycopg2.Warning, e:
|
||||
cr.execute('ROLLBACK TO SAVEPOINT model_load_save')
|
||||
messages.append(dict(info, type='warning', message=str(e)))
|
||||
except psycopg2.Error, e:
|
||||
# Failed to write, log to messages, rollback savepoint (to
|
||||
# avoid broken transaction) and keep going
|
||||
cr.execute('ROLLBACK TO SAVEPOINT model_load_save')
|
||||
messages.append(dict(
|
||||
info, type='error',
|
||||
**PGERROR_TO_OE[e.pgcode](self, fg, info, e)))
|
||||
if any(message['type'] == 'error' for message in messages):
|
||||
cr.execute('ROLLBACK TO SAVEPOINT model_load')
|
||||
ids = False
|
||||
return {'ids': ids, 'messages': messages}
|
||||
def _extract_records(self, cr, uid, fields_, data,
|
||||
context=None, log=lambda a: None):
|
||||
""" Generates record dicts from the data sequence.
|
||||
|
||||
The result is a generator of dicts mapping field names to raw
|
||||
(unconverted, unvalidated) values.
|
||||
|
||||
For relational fields, if sub-fields were provided the value will be
|
||||
a list of sub-records
|
||||
|
||||
The following sub-fields may be set on the record (by key):
|
||||
* None is the name_get for the record (to use with name_create/name_search)
|
||||
* "id" is the External ID for the record
|
||||
* ".id" is the Database ID for the record
|
||||
"""
|
||||
columns = dict((k, v.column) for k, v in self._all_columns.iteritems())
|
||||
# Fake columns to avoid special cases in extractor
|
||||
columns[None] = fields.char('rec_name')
|
||||
columns['id'] = fields.char('External ID')
|
||||
columns['.id'] = fields.integer('Database ID')
|
||||
|
||||
# m2o fields can't be on multiple lines so exclude them from the
|
||||
# is_relational field rows filter, but special-case it later on to
|
||||
# be handled with relational fields (as it can have subfields)
|
||||
is_relational = lambda field: columns[field]._type in ('one2many', 'many2many', 'many2one')
|
||||
get_o2m_values = itemgetter_tuple(
|
||||
[index for index, field in enumerate(fields_)
|
||||
if columns[field[0]]._type == 'one2many'])
|
||||
get_nono2m_values = itemgetter_tuple(
|
||||
[index for index, field in enumerate(fields_)
|
||||
if columns[field[0]]._type != 'one2many'])
|
||||
# Checks if the provided row has any non-empty non-relational field
|
||||
def only_o2m_values(row, f=get_nono2m_values, g=get_o2m_values):
|
||||
return any(g(row)) and not any(f(row))
|
||||
|
||||
index = 0
|
||||
while True:
|
||||
if index >= len(data): return
|
||||
|
||||
row = data[index]
|
||||
# copy non-relational fields to record dict
|
||||
record = dict((field[0], value)
|
||||
for field, value in itertools.izip(fields_, row)
|
||||
if not is_relational(field[0]))
|
||||
|
||||
# Get all following rows which have relational values attached to
|
||||
# the current record (no non-relational values)
|
||||
record_span = itertools.takewhile(
|
||||
only_o2m_values, itertools.islice(data, index + 1, None))
|
||||
# stitch record row back on for relational fields
|
||||
record_span = list(itertools.chain([row], record_span))
|
||||
for relfield in set(
|
||||
field[0] for field in fields_
|
||||
if is_relational(field[0])):
|
||||
column = columns[relfield]
|
||||
# FIXME: how to not use _obj without relying on fields_get?
|
||||
Model = self.pool[column._obj]
|
||||
|
||||
# get only cells for this sub-field, should be strictly
|
||||
# non-empty, field path [None] is for name_get column
|
||||
indices, subfields = zip(*((index, field[1:] or [None])
|
||||
for index, field in enumerate(fields_)
|
||||
if field[0] == relfield))
|
||||
|
||||
# return all rows which have at least one value for the
|
||||
# subfields of relfield
|
||||
relfield_data = filter(any, map(itemgetter_tuple(indices), record_span))
|
||||
record[relfield] = [subrecord
|
||||
for subrecord, _subinfo in Model._extract_records(
|
||||
cr, uid, subfields, relfield_data,
|
||||
context=context, log=log)]
|
||||
|
||||
yield record, {'rows': {
|
||||
'from': index,
|
||||
'to': index + len(record_span) - 1
|
||||
}}
|
||||
index += len(record_span)
|
||||
def _convert_records(self, cr, uid, records,
|
||||
context=None, log=lambda a: None):
|
||||
""" Converts records from the source iterable (recursive dicts of
|
||||
strings) into forms which can be written to the database (via
|
||||
self.create or (ir.model.data)._update)
|
||||
|
||||
:returns: a list of triplets of (id, xid, record)
|
||||
:rtype: list((int|None, str|None, dict))
|
||||
"""
|
||||
if context is None: context = {}
|
||||
Converter = self.pool['ir.fields.converter']
|
||||
columns = dict((k, v.column) for k, v in self._all_columns.iteritems())
|
||||
Translation = self.pool['ir.translation']
|
||||
field_names = dict(
|
||||
(f, (Translation._get_source(cr, uid, self._name + ',' + f, 'field',
|
||||
context.get('lang'))
|
||||
or column.string or f))
|
||||
for f, column in columns.iteritems())
|
||||
converters = dict(
|
||||
(k, Converter.to_field(cr, uid, self, column, context=context))
|
||||
for k, column in columns.iteritems())
|
||||
|
||||
def _log(base, field, exception):
|
||||
type = 'warning' if isinstance(exception, Warning) else 'error'
|
||||
record = dict(base, field=field, type=type,
|
||||
message=unicode(exception.args[0]) % base)
|
||||
if len(exception.args) > 1 and exception.args[1]:
|
||||
record.update(exception.args[1])
|
||||
log(record)
|
||||
|
||||
stream = CountingStream(records)
|
||||
for record, extras in stream:
|
||||
dbid = False
|
||||
xid = False
|
||||
converted = {}
|
||||
# name_get/name_create
|
||||
if None in record: pass
|
||||
# xid
|
||||
if 'id' in record:
|
||||
xid = record['id']
|
||||
# dbid
|
||||
if '.id' in record:
|
||||
try:
|
||||
dbid = int(record['.id'])
|
||||
except ValueError:
|
||||
# in case of overridden id column
|
||||
dbid = record['.id']
|
||||
if not self.search(cr, uid, [('id', '=', dbid)], context=context):
|
||||
log(dict(extras,
|
||||
type='error',
|
||||
record=stream.index,
|
||||
field='.id',
|
||||
message=_(u"Unknown database identifier '%s'") % dbid))
|
||||
dbid = False
|
||||
|
||||
for field, strvalue in record.iteritems():
|
||||
if field in (None, 'id', '.id'): continue
|
||||
if not strvalue:
|
||||
converted[field] = False
|
||||
continue
|
||||
|
||||
# In warnings and error messages, use translated string as
|
||||
# field name
|
||||
message_base = dict(
|
||||
extras, record=stream.index, field=field_names[field])
|
||||
try:
|
||||
converted[field], ws = converters[field](strvalue)
|
||||
|
||||
for w in ws:
|
||||
if isinstance(w, basestring):
|
||||
# wrap warning string in an ImportWarning for
|
||||
# uniform handling
|
||||
w = ImportWarning(w)
|
||||
_log(message_base, field, w)
|
||||
except ValueError, e:
|
||||
_log(message_base, field, e)
|
||||
|
||||
yield dbid, xid, converted, dict(extras, record=stream.index)
|
||||
|
||||
def get_invalid_fields(self, cr, uid):
|
||||
return list(self._invalids)
|
||||
|
||||
|
@ -4746,56 +4825,45 @@ class BaseModel(object):
|
|||
else:
|
||||
raise IndexError( _("Record #%d of %s not found, cannot copy!") %( id, self._name))
|
||||
|
||||
# TODO it seems fields_get can be replaced by _all_columns (no need for translation)
|
||||
fields = self.fields_get(cr, uid, context=context)
|
||||
for f in fields:
|
||||
ftype = fields[f]['type']
|
||||
|
||||
if self._log_access and f in LOG_ACCESS_COLUMNS:
|
||||
del data[f]
|
||||
# build a black list of fields that should not be copied
|
||||
blacklist = set(MAGIC_COLUMNS + ['parent_left', 'parent_right'])
|
||||
def blacklist_given_fields(obj):
|
||||
# blacklist the fields that are given by inheritance
|
||||
for other, field_to_other in obj._inherits.items():
|
||||
blacklist.add(field_to_other)
|
||||
if field_to_other in default:
|
||||
# all the fields of 'other' are given by the record: default[field_to_other],
|
||||
# except the ones redefined in self
|
||||
blacklist.update(set(self.pool.get(other)._all_columns) - set(self._columns))
|
||||
else:
|
||||
blacklist_given_fields(self.pool.get(other))
|
||||
blacklist_given_fields(self)
|
||||
|
||||
res = dict(default)
|
||||
for f, colinfo in self._all_columns.items():
|
||||
field = colinfo.column
|
||||
if f in default:
|
||||
data[f] = default[f]
|
||||
elif 'function' in fields[f]:
|
||||
del data[f]
|
||||
elif ftype == 'many2one':
|
||||
try:
|
||||
data[f] = data[f] and data[f][0]
|
||||
except:
|
||||
pass
|
||||
elif ftype == 'one2many':
|
||||
res = []
|
||||
rel = self.pool.get(fields[f]['relation'])
|
||||
if data[f]:
|
||||
# duplicate following the order of the ids
|
||||
# because we'll rely on it later for copying
|
||||
# translations in copy_translation()!
|
||||
data[f].sort()
|
||||
for rel_id in data[f]:
|
||||
# the lines are first duplicated using the wrong (old)
|
||||
# parent but then are reassigned to the correct one thanks
|
||||
# to the (0, 0, ...)
|
||||
d = rel.copy_data(cr, uid, rel_id, context=context)
|
||||
if d:
|
||||
res.append((0, 0, d))
|
||||
data[f] = res
|
||||
elif ftype == 'many2many':
|
||||
data[f] = [(6, 0, data[f])]
|
||||
pass
|
||||
elif f in blacklist:
|
||||
pass
|
||||
elif isinstance(field, fields.function):
|
||||
pass
|
||||
elif field._type == 'many2one':
|
||||
res[f] = data[f] and data[f][0]
|
||||
elif field._type == 'one2many':
|
||||
other = self.pool.get(field._obj)
|
||||
# duplicate following the order of the ids because we'll rely on
|
||||
# it later for copying translations in copy_translation()!
|
||||
lines = [other.copy_data(cr, uid, line_id, context=context) for line_id in sorted(data[f])]
|
||||
# the lines are duplicated using the wrong (old) parent, but then
|
||||
# are reassigned to the correct one thanks to the (0, 0, ...)
|
||||
res[f] = [(0, 0, line) for line in lines if line]
|
||||
elif field._type == 'many2many':
|
||||
res[f] = [(6, 0, data[f])]
|
||||
else:
|
||||
res[f] = data[f]
|
||||
|
||||
del data['id']
|
||||
|
||||
# make sure we don't break the current parent_store structure and
|
||||
# force a clean recompute!
|
||||
for parent_column in ['parent_left', 'parent_right']:
|
||||
data.pop(parent_column, None)
|
||||
# Remove _inherits field's from data recursively, missing parents will
|
||||
# be created by create() (so that copy() copy everything).
|
||||
def remove_ids(inherits_dict):
|
||||
for parent_table in inherits_dict:
|
||||
del data[inherits_dict[parent_table]]
|
||||
remove_ids(self.pool.get(parent_table)._inherits)
|
||||
remove_ids(self._inherits)
|
||||
return data
|
||||
return res
|
||||
|
||||
def copy_translations(self, cr, uid, old_id, new_id, context=None):
|
||||
if context is None:
|
||||
|
@ -4976,7 +5044,7 @@ class BaseModel(object):
|
|||
def is_transient(self):
|
||||
""" Return whether the model is transient.
|
||||
|
||||
See TransientModel.
|
||||
See :class:`TransientModel`.
|
||||
|
||||
"""
|
||||
return self._transient
|
||||
|
@ -5118,5 +5186,39 @@ class AbstractModel(BaseModel):
|
|||
_auto = False # don't create any database backend for AbstractModels
|
||||
_register = False # not visible in ORM registry, meant to be python-inherited only
|
||||
|
||||
def itemgetter_tuple(items):
|
||||
""" Fixes itemgetter inconsistency (useful in some cases) of not returning
|
||||
a tuple if len(items) == 1: always returns an n-tuple where n = len(items)
|
||||
"""
|
||||
if len(items) == 0:
|
||||
return lambda a: ()
|
||||
if len(items) == 1:
|
||||
return lambda gettable: (gettable[items[0]],)
|
||||
return operator.itemgetter(*items)
|
||||
class ImportWarning(Warning):
|
||||
""" Used to send warnings upwards the stack during the import process
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def convert_pgerror_23502(model, fields, info, e):
|
||||
m = re.match(r'^null value in column "(?P<field>\w+)" violates '
|
||||
r'not-null constraint\n',
|
||||
str(e))
|
||||
if not m or m.group('field') not in fields:
|
||||
return {'message': unicode(e)}
|
||||
field = fields[m.group('field')]
|
||||
return {
|
||||
'message': _(u"Missing required value for the field '%(field)s'") % {
|
||||
'field': field['string']
|
||||
},
|
||||
'field': m.group('field'),
|
||||
}
|
||||
|
||||
PGERROR_TO_OE = collections.defaultdict(
|
||||
# shape of mapped converters
|
||||
lambda: (lambda model, fvg, info, pgerror: {'message': unicode(pgerror)}), {
|
||||
# not_null_violation
|
||||
'23502': convert_pgerror_23502,
|
||||
})
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -73,15 +73,15 @@ class Query(object):
|
|||
:param connection: a tuple ``(lhs, table, lhs_col, col)``.
|
||||
The join corresponds to the SQL equivalent of::
|
||||
|
||||
``(lhs.lhs_col = table.col)``
|
||||
(lhs.lhs_col = table.col)
|
||||
|
||||
:param outer: True if a LEFT OUTER JOIN should be used, if possible
|
||||
(no promotion to OUTER JOIN is supported in case the JOIN
|
||||
was already present in the query, as for the moment
|
||||
implicit INNER JOINs are only connected from NON-NULL
|
||||
columns so it would not be correct (e.g. for
|
||||
``_inherits`` or when a domain criterion explicitly
|
||||
adds filtering)
|
||||
was already present in the query, as for the moment
|
||||
implicit INNER JOINs are only connected from NON-NULL
|
||||
columns so it would not be correct (e.g. for
|
||||
``_inherits`` or when a domain criterion explicitly
|
||||
adds filtering)
|
||||
"""
|
||||
(lhs, table, lhs_col, col) = connection
|
||||
lhs = _quote(lhs)
|
||||
|
@ -120,4 +120,4 @@ class Query(object):
|
|||
def __str__(self):
|
||||
return '<osv.Query: "SELECT ... FROM %s WHERE %s" with params: %r>' % self.get_sql()
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -138,6 +138,16 @@ class Cursor(object):
|
|||
sure you use psycopg2 v2.4.2 or newer if you use PostgreSQL 9.1 and
|
||||
the performance hit is a concern for you.
|
||||
|
||||
.. attribute:: cache
|
||||
|
||||
Cache dictionary with a "request" (-ish) lifecycle, only lives as
|
||||
long as the cursor itself does and proactively cleared when the
|
||||
cursor is closed.
|
||||
|
||||
This cache should *only* be used to store repeatable reads as it
|
||||
ignores rollbacks and savepoints, it should not be used to store
|
||||
*any* data which may be modified during the life of the cursor.
|
||||
|
||||
"""
|
||||
IN_MAX = 1000 # decent limit on size of IN queries - guideline = Oracle limit
|
||||
|
||||
|
@ -182,6 +192,8 @@ class Cursor(object):
|
|||
|
||||
self._default_log_exceptions = True
|
||||
|
||||
self.cache = {}
|
||||
|
||||
def __del__(self):
|
||||
if not self.__closed and not self._cnx.closed:
|
||||
# Oops. 'self' has not been closed explicitly.
|
||||
|
@ -279,6 +291,8 @@ class Cursor(object):
|
|||
if not self._obj:
|
||||
return
|
||||
|
||||
del self.cache
|
||||
|
||||
if self.sql_log:
|
||||
self.__closer = frame_codeinfo(currentframe(),3)
|
||||
self.print_log()
|
||||
|
|
|
@ -17,11 +17,12 @@ models = [
|
|||
('float', fields.float()),
|
||||
('decimal', fields.float(digits=(16, 3))),
|
||||
('string.bounded', fields.char('unknown', size=16)),
|
||||
('string.required', fields.char('unknown', size=None, required=True)),
|
||||
('string', fields.char('unknown', size=None)),
|
||||
('date', fields.date()),
|
||||
('datetime', fields.datetime()),
|
||||
('text', fields.text()),
|
||||
('selection', fields.selection([(1, "Foo"), (2, "Bar"), (3, "Qux")])),
|
||||
('selection', fields.selection([(1, "Foo"), (2, "Bar"), (3, "Qux"), (4, '')])),
|
||||
('selection.function', fields.selection(selection_fn)),
|
||||
# just relate to an integer
|
||||
('many2one', fields.many2one('export.integer')),
|
||||
|
@ -67,6 +68,12 @@ class One2ManyChild(orm.Model):
|
|||
def name_get(self, cr, uid, ids, context=None):
|
||||
return [(record.id, "%s:%s" % (self._name, record.value))
|
||||
for record in self.browse(cr, uid, ids, context=context)]
|
||||
def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100):
|
||||
return (self.name_get(cr, user,
|
||||
self.search(cr, user, [['value', operator, int(name.split(':')[1])]])
|
||||
, context=context)
|
||||
if isinstance(name, basestring) and name.split(':')[0] == self._name
|
||||
else [])
|
||||
|
||||
class One2ManyMultiple(orm.Model):
|
||||
_name = 'export.one2many.multiple'
|
||||
|
@ -116,3 +123,15 @@ class Many2ManyChild(orm.Model):
|
|||
, context=context)
|
||||
if isinstance(name, basestring) and name.split(':')[0] == self._name
|
||||
else [])
|
||||
|
||||
class SelectionWithDefault(orm.Model):
|
||||
_name = 'export.selection.withdefault'
|
||||
|
||||
_columns = {
|
||||
'const': fields.integer(),
|
||||
'value': fields.selection([(1, "Foo"), (2, "Bar")]),
|
||||
}
|
||||
_defaults = {
|
||||
'const': 4,
|
||||
'value': 2,
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import test_export, test_import
|
||||
from . import test_export, test_import, test_load
|
||||
|
||||
fast_suite = [
|
||||
]
|
||||
|
@ -8,6 +8,7 @@ fast_suite = [
|
|||
checks = [
|
||||
test_export,
|
||||
test_import,
|
||||
test_load,
|
||||
]
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -80,9 +80,9 @@ class test_ids_stuff(ImporterCase):
|
|||
model_name = 'export.integer'
|
||||
|
||||
def test_create_with_id(self):
|
||||
self.assertRaises(
|
||||
Exception, # dammit
|
||||
self.import_, ['.id', 'value'], [['42', '36']])
|
||||
self.assertEqual(
|
||||
self.import_(['.id', 'value'], [['42', '36']]),
|
||||
error(1, u"Unknown database identifier '42'"))
|
||||
def test_create_with_xid(self):
|
||||
self.assertEqual(
|
||||
self.import_(['id', 'value'], [['somexmlid', '42']]),
|
||||
|
@ -136,27 +136,25 @@ class test_boolean_field(ImporterCase):
|
|||
self.assertEqual(
|
||||
self.import_(['value'], [
|
||||
[u'0'],
|
||||
[u'off'],
|
||||
[u'no'],
|
||||
[u'false'],
|
||||
[u'FALSE'],
|
||||
[u'OFF'],
|
||||
[u''],
|
||||
]),
|
||||
ok(6))
|
||||
ok(5))
|
||||
self.assertEqual([
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
],
|
||||
values(self.read()))
|
||||
|
||||
def test_trues(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [
|
||||
['no'],
|
||||
['off'],
|
||||
['None'],
|
||||
['nil'],
|
||||
['()'],
|
||||
|
@ -164,10 +162,11 @@ class test_boolean_field(ImporterCase):
|
|||
['#f'],
|
||||
# Problem: OpenOffice (and probably excel) output localized booleans
|
||||
['VRAI'],
|
||||
[u'OFF'],
|
||||
]),
|
||||
ok(7))
|
||||
ok(8))
|
||||
self.assertEqual(
|
||||
[True] * 7,
|
||||
[True] * 8,
|
||||
values(self.read()))
|
||||
|
||||
class test_integer_field(ImporterCase):
|
||||
|
@ -226,21 +225,20 @@ class test_integer_field(ImporterCase):
|
|||
def test_out_of_range(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [[str(2**31)]]),
|
||||
error(1, "integer out of range\n", value=2**31))
|
||||
error(1, "integer out of range\n"))
|
||||
# auto-rollbacks if error is in process_liness, but not during
|
||||
# ir.model.data write. Can differentiate because former ends lines
|
||||
# error lines with "!"
|
||||
self.cr.rollback()
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [[str(-2**32)]]),
|
||||
error(1, "integer out of range\n", value=-2**32))
|
||||
error(1, "integer out of range\n"))
|
||||
|
||||
|
||||
def test_nonsense(self):
|
||||
# FIXME: shit error reporting, exceptions half the time, messages the other half
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
self.import_, ['value'], [['zorglub']])
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [['zorglub']]),
|
||||
error(1, u"'zorglub' does not seem to be an integer for field 'unknown'"))
|
||||
|
||||
class test_float_field(ImporterCase):
|
||||
model_name = 'export.float'
|
||||
|
@ -298,9 +296,9 @@ class test_float_field(ImporterCase):
|
|||
], values(self.read()))
|
||||
|
||||
def test_nonsense(self):
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
self.import_, ['value'], [['foobar']])
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [['foobar']]),
|
||||
error(1, u"'foobar' does not seem to be a number for field 'unknown'"))
|
||||
|
||||
class test_string_field(ImporterCase):
|
||||
model_name = 'export.string.bounded'
|
||||
|
@ -405,8 +403,6 @@ class test_selection(ImporterCase):
|
|||
'value': value
|
||||
})
|
||||
|
||||
# FIXME: can't import an exported selection field label if lang != en_US
|
||||
# (see test_export.test_selection.test_localized_export)
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [
|
||||
['toto'],
|
||||
|
@ -417,27 +413,23 @@ class test_selection(ImporterCase):
|
|||
self.assertEqual([3, 1, 2], values(self.read()))
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [['Foo']], context={'lang': 'fr_FR'}),
|
||||
error(1, "Key/value 'Foo' not found in selection field 'value'",
|
||||
value=False))
|
||||
ok(1))
|
||||
|
||||
def test_invalid(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [['Baz']]),
|
||||
error(1, "Key/value 'Baz' not found in selection field 'value'",
|
||||
# what the fuck?
|
||||
value=False))
|
||||
error(1, u"Value 'Baz' not found in selection field 'unknown'"))
|
||||
self.cr.rollback()
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [[42]]),
|
||||
error(1, "Key/value '42' not found in selection field 'value'",
|
||||
value=False))
|
||||
error(1, u"Value '42' not found in selection field 'unknown'"))
|
||||
|
||||
class test_selection_function(ImporterCase):
|
||||
model_name = 'export.selection.function'
|
||||
translations_fr = [
|
||||
("Corge", "toto"),
|
||||
("Grault", "titi"),
|
||||
("Whee", "tete"),
|
||||
("Wheee", "tete"),
|
||||
("Moog", "tutu"),
|
||||
]
|
||||
|
||||
|
@ -482,8 +474,7 @@ class test_selection_function(ImporterCase):
|
|||
['toto'],
|
||||
['tete'],
|
||||
], context={'lang': 'fr_FR'}),
|
||||
error(1, "Key/value 'toto' not found in selection field 'value'",
|
||||
value=False))
|
||||
ok(2))
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [['Wheee']], context={'lang': 'fr_FR'}),
|
||||
ok(1))
|
||||
|
@ -555,7 +546,6 @@ class test_m2o(ImporterCase):
|
|||
self.assertEqual(
|
||||
self.import_(['value'], [[name2]]),
|
||||
ok(1))
|
||||
# FIXME: is it really normal import does not care for name_search collisions?
|
||||
self.assertEqual([
|
||||
(integer_id1, name1)
|
||||
], values(self.read()))
|
||||
|
@ -569,35 +559,35 @@ class test_m2o(ImporterCase):
|
|||
integer_id2 = self.registry('export.integer').create(
|
||||
self.cr, openerp.SUPERUSER_ID, {'value': 36})
|
||||
|
||||
self.assertRaises(
|
||||
ValueError, # Because name_search all the things. Fallback schmallback
|
||||
self.import_, ['value'], [
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [
|
||||
# import by id, without specifying it
|
||||
[integer_id1],
|
||||
[integer_id2],
|
||||
[integer_id1],
|
||||
])
|
||||
]),
|
||||
error(1, u"No matching record found for name '%s' in field 'unknown'" % integer_id1))
|
||||
|
||||
def test_sub_field(self):
|
||||
""" Does not implicitly create the record, does not warn that you can't
|
||||
import m2o subfields (at all)...
|
||||
"""
|
||||
self.assertRaises(
|
||||
ValueError, # No record found for 42, name_searches the bloody thing
|
||||
self.import_, ['value/value'], [['42']])
|
||||
self.assertEqual(
|
||||
self.import_(['value/value'], [['42']]),
|
||||
error(1, u"Can not create Many-To-One records indirectly, import the field separately"))
|
||||
|
||||
def test_fail_noids(self):
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
self.import_, ['value'], [['nameisnoexist:3']])
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [['nameisnoexist:3']]),
|
||||
error(1, u"No matching record found for name 'nameisnoexist:3' in field 'unknown'"))
|
||||
self.cr.rollback()
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
self.import_, ['value/id'], [['noxidhere']]),
|
||||
self.assertEqual(
|
||||
self.import_(['value/id'], [['noxidhere']]),
|
||||
error(1, u"No matching record found for external id 'noxidhere' in field 'unknown'"))
|
||||
self.cr.rollback()
|
||||
self.assertRaises(
|
||||
Exception, # FIXME: Why can't you be a ValueError like everybody else?
|
||||
self.import_, ['value/.id'], [[66]])
|
||||
self.assertEqual(
|
||||
self.import_(['value/.id'], [[66]]),
|
||||
error(1, u"No matching record found for database id '66' in field 'unknown'"))
|
||||
|
||||
class test_m2m(ImporterCase):
|
||||
model_name = 'export.many2many'
|
||||
|
@ -635,12 +625,9 @@ class test_m2m(ImporterCase):
|
|||
self.assertEqual(values(b[2].value), [3, 44, 84])
|
||||
|
||||
def test_noids(self):
|
||||
try:
|
||||
self.import_(['value/.id'], [['42']])
|
||||
self.fail("Should have raised an exception")
|
||||
except Exception, e:
|
||||
self.assertIs(type(e), Exception,
|
||||
"test should be fixed on exception subclass")
|
||||
self.assertEqual(
|
||||
self.import_(['value/.id'], [['42']]),
|
||||
error(1, u"No matching record found for database id '42' in field 'unknown'"))
|
||||
|
||||
def test_xids(self):
|
||||
M2O_o = self.registry('export.many2many.other')
|
||||
|
@ -662,9 +649,9 @@ class test_m2m(ImporterCase):
|
|||
self.assertEqual(values(b[0].value), [3, 44])
|
||||
self.assertEqual(values(b[2].value), [44, 84])
|
||||
def test_noxids(self):
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
self.import_, ['value/id'], [['noxidforthat']])
|
||||
self.assertEqual(
|
||||
self.import_(['value/id'], [['noxidforthat']]),
|
||||
error(1, u"No matching record found for external id 'noxidforthat' in field 'unknown'"))
|
||||
|
||||
def test_names(self):
|
||||
M2O_o = self.registry('export.many2many.other')
|
||||
|
@ -689,9 +676,9 @@ class test_m2m(ImporterCase):
|
|||
self.assertEqual(values(b[2].value), [3, 9])
|
||||
|
||||
def test_nonames(self):
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
self.import_, ['value'], [['wherethem2mhavenonames']])
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [['wherethem2mhavenonames']]),
|
||||
error(1, u"No matching record found for name 'wherethem2mhavenonames' in field 'unknown'"))
|
||||
|
||||
def test_import_to_existing(self):
|
||||
M2O_o = self.registry('export.many2many.other')
|
||||
|
@ -717,13 +704,13 @@ class test_o2m(ImporterCase):
|
|||
model_name = 'export.one2many'
|
||||
|
||||
def test_name_get(self):
|
||||
# FIXME: bloody hell why can't this just name_create the record?
|
||||
self.assertRaises(
|
||||
IndexError,
|
||||
self.import_,
|
||||
['const', 'value'],
|
||||
[['5', u'Java is a DSL for taking large XML files'
|
||||
u' and converting them to stack traces']])
|
||||
s = u'Java is a DSL for taking large XML files and converting them to' \
|
||||
u' stack traces'
|
||||
self.assertEqual(
|
||||
self.import_(
|
||||
['const', 'value'],
|
||||
[['5', s]]),
|
||||
error(1, u"No matching record found for name '%s' in field 'unknown'" % s))
|
||||
|
||||
def test_single(self):
|
||||
self.assertEqual(
|
||||
|
@ -813,14 +800,11 @@ class test_o2m(ImporterCase):
|
|||
]),
|
||||
ok(2))
|
||||
|
||||
# No record values alongside id => o2m resolution skipped altogether,
|
||||
# creates 2 records => remove/don't import columns sideshow columns,
|
||||
# get completely different semantics
|
||||
b, b1 = self.browse()
|
||||
[b] = self.browse()
|
||||
self.assertEqual(b.const, 42)
|
||||
self.assertEqual(values(b.value), [])
|
||||
self.assertEqual(b1.const, 4)
|
||||
self.assertEqual(values(b1.value), [])
|
||||
# automatically forces link between core record and o2ms
|
||||
self.assertEqual(values(b.value), [109, 262])
|
||||
self.assertEqual(values(b.value, field='parent_id'), [b, b])
|
||||
|
||||
def test_link_2(self):
|
||||
O2M_c = self.registry('export.one2many.child')
|
||||
|
@ -838,21 +822,10 @@ class test_o2m(ImporterCase):
|
|||
]),
|
||||
ok(2))
|
||||
|
||||
(b,) = self.browse()
|
||||
# if an id (db or xid) is provided, expectations that objects are
|
||||
# *already* linked and emits UPDATE (1, id, {}).
|
||||
# Noid => CREATE (0, ?, {})
|
||||
# TODO: xid ignored aside from getting corresponding db id?
|
||||
[b] = self.browse()
|
||||
self.assertEqual(b.const, 42)
|
||||
self.assertEqual(values(b.value), [])
|
||||
|
||||
# FIXME: updates somebody else's records?
|
||||
self.assertEqual(
|
||||
O2M_c.read(self.cr, openerp.SUPERUSER_ID, id1),
|
||||
{'id': id1, 'str': 'Bf', 'value': 1, 'parent_id': False})
|
||||
self.assertEqual(
|
||||
O2M_c.read(self.cr, openerp.SUPERUSER_ID, id2),
|
||||
{'id': id2, 'str': 'Me', 'value': 2, 'parent_id': False})
|
||||
self.assertEqual(values(b.value), [1, 2])
|
||||
self.assertEqual(values(b.value, field='parent_id'), [b, b])
|
||||
|
||||
class test_o2m_multiple(ImporterCase):
|
||||
model_name = 'export.one2many.multiple'
|
||||
|
@ -866,16 +839,10 @@ class test_o2m_multiple(ImporterCase):
|
|||
['', '14', ''],
|
||||
]),
|
||||
ok(4))
|
||||
# Oh yeah, that's the stuff
|
||||
(b, b1, b2) = self.browse()
|
||||
self.assertEqual(values(b.child1), [11])
|
||||
self.assertEqual(values(b.child2), [21])
|
||||
|
||||
self.assertEqual(values(b1.child1), [12])
|
||||
self.assertEqual(values(b1.child2), [22])
|
||||
|
||||
self.assertEqual(values(b2.child1), [13, 14])
|
||||
self.assertEqual(values(b2.child2), [23])
|
||||
[b] = self.browse()
|
||||
self.assertEqual(values(b.child1), [11, 12, 13, 14])
|
||||
self.assertEqual(values(b.child2), [21, 22, 23])
|
||||
|
||||
def test_multi(self):
|
||||
self.assertEqual(
|
||||
|
@ -888,11 +855,10 @@ class test_o2m_multiple(ImporterCase):
|
|||
['', '', '23'],
|
||||
]),
|
||||
ok(6))
|
||||
# What the actual fuck?
|
||||
(b, b1) = self.browse()
|
||||
|
||||
[b] = self.browse()
|
||||
self.assertEqual(values(b.child1), [11, 12, 13, 14])
|
||||
self.assertEqual(values(b.child2), [21])
|
||||
self.assertEqual(values(b1.child2), [22, 23])
|
||||
self.assertEqual(values(b.child2), [21, 22, 23])
|
||||
|
||||
def test_multi_fullsplit(self):
|
||||
self.assertEqual(
|
||||
|
@ -906,12 +872,11 @@ class test_o2m_multiple(ImporterCase):
|
|||
['', '', '23'],
|
||||
]),
|
||||
ok(7))
|
||||
# oh wow
|
||||
(b, b1) = self.browse()
|
||||
|
||||
[b] = self.browse()
|
||||
self.assertEqual(b.const, 5)
|
||||
self.assertEqual(values(b.child1), [11, 12, 13, 14])
|
||||
self.assertEqual(b1.const, 36)
|
||||
self.assertEqual(values(b1.child2), [21, 22, 23])
|
||||
self.assertEqual(values(b.child2), [21, 22, 23])
|
||||
|
||||
# function, related, reference: written to db as-is...
|
||||
# => function uses @type for value coercion/conversion
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2,12 +2,12 @@
|
|||
# > PYTHONPATH=. python2 openerp/tests/test_misc.py
|
||||
|
||||
import unittest2
|
||||
from ..tools import misc
|
||||
|
||||
class test_misc(unittest2.TestCase):
|
||||
class append_content_to_html(unittest2.TestCase):
|
||||
""" Test some of our generic utility functions """
|
||||
|
||||
def test_append_to_html(self):
|
||||
from openerp.tools import append_content_to_html
|
||||
test_samples = [
|
||||
('<!DOCTYPE...><HTML encoding="blah">some <b>content</b></HtMl>', '--\nYours truly', True,
|
||||
'<!DOCTYPE...><html encoding="blah">some <b>content</b>\n<pre>--\nYours truly</pre>\n</html>'),
|
||||
|
@ -15,7 +15,37 @@ class test_misc(unittest2.TestCase):
|
|||
'<html><body>some <b>content</b>\n\n\n<p>--</p>\n<p>Yours truly</p>\n\n\n</body></html>'),
|
||||
]
|
||||
for html, content, flag, expected in test_samples:
|
||||
self.assertEqual(append_content_to_html(html,content,flag), expected, 'append_content_to_html is broken')
|
||||
self.assertEqual(misc.append_content_to_html(html,content,flag), expected, 'append_content_to_html is broken')
|
||||
|
||||
class test_countingstream(unittest2.TestCase):
|
||||
def test_empty_stream(self):
|
||||
s = misc.CountingStream(iter([]))
|
||||
self.assertEqual(s.index, -1)
|
||||
self.assertIsNone(next(s, None))
|
||||
self.assertEqual(s.index, 0)
|
||||
|
||||
def test_single(self):
|
||||
s = misc.CountingStream(xrange(1))
|
||||
self.assertEqual(s.index, -1)
|
||||
self.assertEqual(next(s, None), 0)
|
||||
self.assertIsNone(next(s, None))
|
||||
self.assertEqual(s.index, 1)
|
||||
|
||||
def test_full(self):
|
||||
s = misc.CountingStream(xrange(42))
|
||||
for _ in s:
|
||||
pass
|
||||
self.assertEqual(s.index, 42)
|
||||
|
||||
def test_repeated(self):
|
||||
""" Once the CountingStream has stopped iterating, the index should not
|
||||
increase anymore (the internal state should not be allowed to change)
|
||||
"""
|
||||
s = misc.CountingStream(iter([]))
|
||||
self.assertIsNone(next(s, None))
|
||||
self.assertEqual(s.index, 0)
|
||||
self.assertIsNone(next(s, None))
|
||||
self.assertEqual(s.index, 0)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest2.main()
|
||||
unittest2.main()
|
||||
|
|
|
@ -6,6 +6,85 @@ import common
|
|||
UID = common.ADMIN_USER_ID
|
||||
DB = common.DB
|
||||
|
||||
class TestInherits(common.TransactionCase):
|
||||
""" test the behavior of the orm for models that use _inherits;
|
||||
specifically: res.users, that inherits from res.partner
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestInherits, self).setUp()
|
||||
self.partner = self.registry('res.partner')
|
||||
self.user = self.registry('res.users')
|
||||
|
||||
def test_create(self):
|
||||
""" creating a user should automatically create a new partner """
|
||||
partners_before = self.partner.search(self.cr, UID, [])
|
||||
foo_id = self.user.create(self.cr, UID, {'name': 'Foo', 'login': 'foo', 'password': 'foo'})
|
||||
foo = self.user.browse(self.cr, UID, foo_id)
|
||||
|
||||
self.assertNotIn(foo.partner_id.id, partners_before)
|
||||
|
||||
def test_create_with_ancestor(self):
|
||||
""" creating a user with a specific 'partner_id' should not create a new partner """
|
||||
par_id = self.partner.create(self.cr, UID, {'name': 'Foo'})
|
||||
partners_before = self.partner.search(self.cr, UID, [])
|
||||
foo_id = self.user.create(self.cr, UID, {'partner_id': par_id, 'login': 'foo', 'password': 'foo'})
|
||||
partners_after = self.partner.search(self.cr, UID, [])
|
||||
|
||||
self.assertEqual(set(partners_before), set(partners_after))
|
||||
|
||||
foo = self.user.browse(self.cr, UID, foo_id)
|
||||
self.assertEqual(foo.name, 'Foo')
|
||||
self.assertEqual(foo.partner_id.id, par_id)
|
||||
|
||||
def test_read(self):
|
||||
""" inherited fields should be read without any indirection """
|
||||
foo_id = self.user.create(self.cr, UID, {'name': 'Foo', 'login': 'foo', 'password': 'foo'})
|
||||
foo_values, = self.user.read(self.cr, UID, [foo_id])
|
||||
partner_id = foo_values['partner_id'][0]
|
||||
partner_values, = self.partner.read(self.cr, UID, [partner_id])
|
||||
self.assertEqual(foo_values['name'], partner_values['name'])
|
||||
|
||||
foo = self.user.browse(self.cr, UID, foo_id)
|
||||
self.assertEqual(foo.name, foo.partner_id.name)
|
||||
|
||||
def test_copy(self):
|
||||
""" copying a user should automatically copy its partner, too """
|
||||
foo_id = self.user.create(self.cr, UID, {'name': 'Foo', 'login': 'foo', 'password': 'foo'})
|
||||
foo_before, = self.user.read(self.cr, UID, [foo_id])
|
||||
bar_id = self.user.copy(self.cr, UID, foo_id, {'login': 'bar', 'password': 'bar'})
|
||||
foo_after, = self.user.read(self.cr, UID, [foo_id])
|
||||
|
||||
self.assertEqual(foo_before, foo_after)
|
||||
|
||||
foo, bar = self.user.browse(self.cr, UID, [foo_id, bar_id])
|
||||
self.assertEqual(bar.login, 'bar')
|
||||
self.assertNotEqual(foo.id, bar.id)
|
||||
self.assertNotEqual(foo.partner_id.id, bar.partner_id.id)
|
||||
|
||||
def test_copy_with_ancestor(self):
|
||||
""" copying a user with 'parent_id' in defaults should not duplicate the partner """
|
||||
foo_id = self.user.create(self.cr, UID, {'name': 'Foo', 'login': 'foo', 'password': 'foo'})
|
||||
par_id = self.partner.create(self.cr, UID, {'name': 'Bar'})
|
||||
|
||||
foo_before, = self.user.read(self.cr, UID, [foo_id])
|
||||
partners_before = self.partner.search(self.cr, UID, [])
|
||||
bar_id = self.user.copy(self.cr, UID, foo_id, {'partner_id': par_id, 'login': 'bar'})
|
||||
foo_after, = self.user.read(self.cr, UID, [foo_id])
|
||||
partners_after = self.partner.search(self.cr, UID, [])
|
||||
|
||||
self.assertEqual(foo_before, foo_after)
|
||||
self.assertEqual(set(partners_before), set(partners_after))
|
||||
|
||||
foo, bar = self.user.browse(self.cr, UID, [foo_id, bar_id])
|
||||
self.assertNotEqual(foo.id, bar.id)
|
||||
self.assertEqual(bar.partner_id.id, par_id)
|
||||
self.assertEqual(bar.login, 'bar', "login is given from copy parameters")
|
||||
self.assertEqual(bar.password, foo.password, "password is given from original record")
|
||||
self.assertEqual(bar.name, 'Bar', "name is given from specific partner")
|
||||
|
||||
|
||||
|
||||
CREATE = lambda values: (0, False, values)
|
||||
UPDATE = lambda id, values: (1, id, values)
|
||||
DELETE = lambda id: (2, id, False)
|
||||
|
@ -19,6 +98,7 @@ def sorted_by_id(list_of_dicts):
|
|||
return sorted(list_of_dicts, key=lambda d: d.get('id'))
|
||||
|
||||
class TestO2MSerialization(common.TransactionCase):
|
||||
""" test the orm method 'write' on one2many fields """
|
||||
|
||||
def setUp(self):
|
||||
super(TestO2MSerialization, self).setUp()
|
||||
|
|
|
@ -182,7 +182,7 @@ class configmanager(object):
|
|||
group.add_option('--log-handler', action="append", default=[':INFO'], my_default=[':INFO'], metavar="PREFIX:LEVEL", help='setup a handler at LEVEL for a given PREFIX. An empty PREFIX indicates the root logger. This option can be repeated. Example: "openerp.orm:DEBUG" or "werkzeug:CRITICAL" (default: ":INFO")')
|
||||
group.add_option('--log-request', action="append_const", dest="log_handler", const="openerp.netsvc.rpc.request:DEBUG", help='shortcut for --log-handler=openerp.netsvc.rpc.request:DEBUG')
|
||||
group.add_option('--log-response', action="append_const", dest="log_handler", const="openerp.netsvc.rpc.response:DEBUG", help='shortcut for --log-handler=openerp.netsvc.rpc.response:DEBUG')
|
||||
group.add_option('--log-web', action="append_const", dest="log_handler", const="openerp.addons.web.common.http:DEBUG", help='shortcut for --log-handler=openerp.addons.web.common.http:DEBUG')
|
||||
group.add_option('--log-web', action="append_const", dest="log_handler", const="openerp.addons.web.http:DEBUG", help='shortcut for --log-handler=openerp.addons.web.http:DEBUG')
|
||||
group.add_option('--log-sql', action="append_const", dest="log_handler", const="openerp.sql_db:DEBUG", help='shortcut for --log-handler=openerp.sql_db:DEBUG')
|
||||
# For backward-compatibility, map the old log levels to something
|
||||
# quite close.
|
||||
|
|
|
@ -1221,4 +1221,38 @@ class mute_logger(object):
|
|||
with self:
|
||||
return func(*args, **kwargs)
|
||||
return deco
|
||||
|
||||
_ph = object()
|
||||
class CountingStream(object):
|
||||
""" Stream wrapper counting the number of element it has yielded. Similar
|
||||
role to ``enumerate``, but for use when the iteration process of the stream
|
||||
isn't fully under caller control (the stream can be iterated from multiple
|
||||
points including within a library)
|
||||
|
||||
``start`` allows overriding the starting index (the index before the first
|
||||
item is returned).
|
||||
|
||||
On each iteration (call to :meth:`~.next`), increases its :attr:`~.index`
|
||||
by one.
|
||||
|
||||
.. attribute:: index
|
||||
|
||||
``int``, index of the last yielded element in the stream. If the stream
|
||||
has ended, will give an index 1-past the stream
|
||||
"""
|
||||
def __init__(self, stream, start=-1):
|
||||
self.stream = iter(stream)
|
||||
self.index = start
|
||||
self.stopped = False
|
||||
def __iter__(self):
|
||||
return self
|
||||
def next(self):
|
||||
if self.stopped: raise StopIteration()
|
||||
self.index += 1
|
||||
val = next(self.stream, _ph)
|
||||
if val is _ph:
|
||||
self.stopped = True
|
||||
raise StopIteration()
|
||||
return val
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
Loading…
Reference in New Issue