[MERGE] from trunk

bzr revid: rco@openerp.com-20121219130750-3ljqo3dyz05zww08
This commit is contained in:
Raphael Collet 2012-12-19 14:07:50 +01:00
commit fd5dee2267
160 changed files with 30998 additions and 6719 deletions

View File

@ -1,19 +0,0 @@
#! /usr/bin/env python
# -*- coding: UTF-8 -*-
import os
import sys
if __name__ == "__main__":
print '-' * 70
print "DEPRECATED: you are starting the OpenERP server with its old path,"
print "please use the new executable (available in the parent directory)."
print '-' * 70
# Change to the parent directory ...
os.chdir(os.path.normpath(os.path.dirname(__file__)))
os.chdir('..')
# ... and execute the new executable.
os.execv('openerp-server', sys.argv)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

3
debian/control vendored
View File

@ -19,10 +19,12 @@ Depends:
python-docutils,
python-feedparser,
python-gdata,
python-jinja2,
python-ldap,
python-libxslt1,
python-lxml,
python-mako,
python-mock,
python-openid,
python-psutil,
python-psycopg2,
@ -33,6 +35,7 @@ Depends:
python-reportlab,
python-simplejson,
python-tz,
python-unittest2,
python-vatnumber,
python-vobject,
python-webdav,

View File

@ -135,6 +135,9 @@ The view describes how the edition form or the data tree/list appear on screen.
A form can be called by an action opening in 'Tree' mode. The form view is generally opened from the list mode (like if the user pushes on 'switch view').
.. _domain:
.. _domains:
The domain
----------

View File

@ -141,10 +141,6 @@ for users who do not belong to the authorized groups:
.. note:: The tests related to this feature are in ``openerp/tests/test_acl.py``.
.. warning:: At the time of writing the implementation of this feature is partial
and does not yet restrict read/write RPC access to the field.
The corresponding test is written already but currently disabled.
Workflow transition rules
+++++++++++++++++++++++++

View File

@ -10,3 +10,4 @@ Miscellanous
06_misc_need_action_specs.rst
06_misc_user_img_specs.rst
06_misc_import.rst
06_misc_auto_join.rst

75
doc/06_misc_auto_join.rst Normal file
View File

@ -0,0 +1,75 @@
.. _performing_joins_in_select:
Perfoming joins in select
=========================
.. versionadded:: 7.0
Starting with OpenERP 7.0, an ``auto_join`` attribute is added on *many2one* and
*one2many* fields. The purpose is to allow the automatic generation of joins in
select queries. This attribute is set to False by default, therefore not changing
the default behavior. Please note that we consider this feature as still experimental
and should be used only if you understand its limitations and targets.
Without ``_auto_join``, the behavior of expression.parse() is the same as before.
Leafs holding a path beginning with many2one or one2many fields perform a search
on the relational table. The result is then used to replace the leaf content.
For example, if you have on res.partner a domain like ``[('bank_ids.name',
'like', 'foo')]`` with bank_ids linking to res.partner.bank, 3 queries will be
performed :
- 1 on res_partner_bank, with domain ``[('name', '=', 'foo')]``, that returns a
list of res.partner.bank ids (bids)
- 1 on res_partner, with a domain ``['bank_ids', 'in', bids)]``, that returns a
list of res.partner ids (pids)
- 1 on res_partner, with a domain ``[('id', 'in', pids)]``
When the ``auto_join`` attribute is True on a relational field, the destination
table will be joined to produce only one query.
- the relational table is accessed using an alias: ``'"res_partner_bank"
as res_partner__bank_ids``. The alias is generated using the relational field
name. This allows to have multiple joins with different join conditions on the
same table, depending on the domain.
- there is a join condition between the destination table and the main table:
``res_partner__bank_ids."partner_id"=res_partner."id"``
- the condition is then written on the relational table:
``res_partner__bank_ids."name" = 'foo'``
This manipulation is performed in expression.parse(). It checks leafs that
contain a path, i.e. any domain containing a '.'. It then checks whether the
first item of the path is a *many2one* or *one2many* field with the ``auto_join``
attribute set. If set, it adds a join query and recursively analyzes the
remaining of the leaf, using the same behavior. If the remaining path also holds
a path with auto_join fields, it will add all tables and add every necessary
join conditions.
Chaining joins allows to reduce the number of queries performed, and to avoid
having too long equivalent leaf replacement in domains. Indeed, the internal
queries produced by this behavior can be very costly, because they were generally
select queries without limit that could lead to huge ('id', 'in', [...])
leafs to analyze and execute.
Some limitations exist on this feature that limits its current use as of version
7.0. **This feature is therefore considered as experimental, and used
to speedup some precise bottlenecks in OpenERP**.
List of known issues and limitations:
- using ``auto_join`` bypasses the business logic; no name search is performed,
only direct matches between ids using join conditions
- ir.rules are not taken into account when analyzing and adding the join
conditions
List of already-supported corner cases :
- one2many fields having a domain attribute. Static domains as well as dynamic
domain are supported
- auto_join leading to functional searchable fields
Typical use in OpenERP 7.0:
- in mail module: notification_ids field on mail_message, allowing to speedup
the display of the various mailboxes
- in mail module: message_ids field on mail_thread, allowing to speedup the
display of needaction counters and documents having unread messages

View File

@ -76,9 +76,9 @@ This phase also generates the ``rows`` indexes for any
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.
This second phase takes the record dicts, extracts the :term:`database
ID` and :term:`external ID` 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
@ -141,14 +141,14 @@ 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
If the specified field is a :term:`external ID` (``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).
If the specified field is a :term:`database ID` (``m2o/.id``), the
process is the same as for external ids (on database identifiers
instead of external ones).
Many to Many field
~~~~~~~~~~~~~~~~~~
@ -161,11 +161,11 @@ 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.
:term:`external ID` or :term:`database ID` the :term:`database ID` 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.
If a :term:`database ID` 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.

View File

@ -27,5 +27,15 @@ OpenERP Server API
api_core.rst
api_models.rst
Concepts
''''''''
.. glossary::
Database ID
The primary key of a record in a PostgreSQL table (or a
virtual version thereof), usually varies from one database to
the next.
External ID

View File

@ -40,7 +40,6 @@ import service
import sql_db
import test
import tools
import wizard
import workflow
# backward compatilbility
# TODO: This is for the web addons, can be removed later.

View File

@ -3,7 +3,7 @@
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
# Copyright (C) 2010 OpenERP s.a. (<http://openerp.com>).
# Copyright (C) 2010, 2012 OpenERP s.a. (<http://openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@ -85,7 +85,6 @@ The kernel of OpenERP, needed for all installation.
'res/ir_property_view.xml',
'security/base_security.xml',
'security/ir.model.access.csv',
'security/ir.model.access-1.csv', # res.partner.address is deprecated; it is still there for backward compability only and will be removed in next version
],
'demo': [
'base_demo.xml',

View File

@ -149,7 +149,6 @@ CREATE TABLE res_users (
active boolean default True,
login varchar(64) NOT NULL UNIQUE,
password varchar(64) default null,
lang varchar(64) default '',
-- No FK references below, will be added later by ORM
-- (when the destination rows exist)
company_id int,
@ -316,13 +315,29 @@ CREATE TABLE ir_module_module_dependency (
primary key(id)
);
CREATE TABLE res_company (
CREATE TABLE res_partner (
id serial NOT NULL,
name character varying(64) not null,
parent_id integer references res_company on delete set null,
name character varying(128),
lang varchar(64),
company_id int,
primary key(id)
);
CREATE TABLE res_currency (
id serial PRIMARY KEY,
name VARCHAR(32) NOT NULL
);
CREATE TABLE res_company (
id serial PRIMARY KEY,
name character varying(128) not null,
parent_id integer references res_company on delete set null,
partner_id integer not null references res_partner,
currency_id integer not null references res_currency
);
CREATE TABLE res_lang (
id serial PRIMARY KEY,
name VARCHAR(64) NOT NULL UNIQUE,
@ -375,16 +390,24 @@ CREATE TABLE ir_model_relation (
module integer NOT NULL references ir_module_module on delete restrict,
model integer NOT NULL references ir_model on delete restrict,
name character varying(128) NOT NULL
);
);
---------------------------------
-- Users
---------------------------------
insert into res_users (id,login,password,active,company_id,partner_id) VALUES (1,'admin','admin',true,1,1);
insert into ir_model_data (name,module,model,noupdate,res_id) VALUES ('user_root','base','res.users',true,1);
insert into res_users (id,login,password,active,company_id,partner_id,lang) values (1,'admin','admin',True,1,1,'en_US');
insert into ir_model_data (name,module,model,noupdate,res_id) values ('user_root','base','res.users',True,1);
insert into res_partner (id, name, lang, company_id) VALUES (1, 'Your Company', 'en_US', 1);
insert into ir_model_data (name,module,model,noupdate,res_id) VALUES ('main_partner','base','res.partner',true,1);
-- Compatibility purpose, to remove V6.0
insert into ir_model_data (name,module,model,noupdate,res_id) values ('user_admin','base','res.users',True,1);
insert into res_currency (id, name) VALUES (1, 'EUR');
insert into ir_model_data (name,module,model,noupdate,res_id) VALUES ('EUR','base','res.currency',true,1);
insert into res_company (id, name, partner_id, currency_id) VALUES (1, 'Your Company', 1, 1);
insert into ir_model_data (name,module,model,noupdate,res_id) VALUES ('main_company','base','res.company',true,1);
select setval('res_company_id_seq', 2);
select setval('res_users_id_seq', 2);
select setval('res_partner_id_seq', 2);
select setval('res_currency_id_seq', 2);

View File

@ -32,6 +32,7 @@
<record id="main_partner" model="res.partner" context="{'default_is_company': True}">
<field name="name">Your Company</field>
<field name="company_id" eval="None"/>
<field name="image" eval="False"/>
<field name="customer" eval="False"/>
<field name="is_company" eval="True"/>
<field name="street"></field>

View File

@ -7,6 +7,74 @@
<field name="customer" eval="False"/>
<field name="email">demo@example.com</field>
</record>
<record id="main_partner" model="res.partner">
<field name="image">iVBORw0KGgoAAAANSUhEUgAAALQAAAAuCAYAAACBMDMXAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A
/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDCAo7GWN31l0AAA1fSURBVHja
7Zx5dFXFHcc/eQk7KBiUTVGRRezA8ahYamgRFbWAcmyPe+uGSrW1FrFqF9u61bZWm1Kx1lgVpHVp
3ShVVBTcBYSyDHHBulEUhVRBRJJA0j/m95rJZOa++zYS2vs95xLevLkzc+d+72++v99v7oMECRIk
SJAgQYIECRIkSJAgQYIECQqB9skUFA4luZ6ooRzoA/QGPgWqlfn7/4aBwJHAEUA/oANwA3C/Vaen
/N3gnPs14ErgaGB9QscdSGgNewHj5TgC6Oyp9h6wylTnUQULdsI52U2Oj4GaiHoVwC3AcM93a4DB
QHfgAeAwoBFYAVwjZe2AamA/ma8jA6SeAowDtgH18neb9Rmg1DrK5Ggn1r+dlH8ObAE+A24D5su5
/YCZVtvu30an/XQf7eXYJNe7BlhMvHs+DPhNRJ8pGbd9Lem/24C10t/bMpebsrHEAzXco6FBQ6Mc
72qYoeEaDZdoqNKwSMMWq06jhuc1jNxJiHww8ILcwEaZuHnANz0P/qFAg1XXd9wKvB/4bgZwvnxf
AawTsu/uGddlwKtCxsYCHZOs9vsBS4APCtT2QuCYGIReBnxUgP4+Aa4DukRaaG2Wzl8D35KnA7Eo
l4v1bfCcs4c87fYF1QMXK/h9GybzaOBpsQw+PAucC6yWzw8CJ+TZZwPwE7kZ+wBzgVpZ/WoCq+kM
ecBcrBDS18pRJ39LgF5yfBHoKvUnAH/3tHMg8A9P+RZgmvRRAwwAFHAG0NFTf5vM6Ysx5uFY4DFP
+QYxCq8DG4Eh0uaEQDuzAnNjiKnhRcfaPqShWwyLXqLhaufcRg3faKNk3gV4N4Yl+Fz0bgdgeYz6
f5KlfVtEnanWOMqFMEuBHoGxTgq0c3FMKfWW1D84ot7HnvbXBOr2F0PgG9O/gE4xxtUhcP7iQP3j
ga2Bc071EXKASAqbjPN12Hr52ijV8KbTxgbtX1JbGzOyXOLWigXMVCf98A8RvfhhoF6ZNZZ9RH4s
Bnb1jHVCHoQGeFzq94uo81oWhEZkUkg6fCnmuD7JgtCI0+3r7+6UQ8TOwEPy5KWxHjjdJzFCULAd
+IVTXA5UtjEydw8uU2HUyTLow/sit74rcqKv1J0iJJoo0Y8tUr8vcJR1/jtC2qHyoLnINxKyVm78
RxF1su1jfcR9PTiLNrLBTYHy4a7VvcPjtV+vzI3KFjNFx9k4TRuHqq1gRIZIT4M4TDeKZu4D7CtO
zUjReD8SP2M8cJI4jA8A35eyPpaunA2cjPE1TgWeEX1o4xXgFOA44ETnu9o8r3eatFkfUSeXPpYH
yrvFPD/bPj/AHyIuL7Os8wSZbByHblYuM6egTpsw3iAPiRa1EULv7SHwCglpLRBn8BPPeZ2B74im
rXO+SwFnAXfJ3E0HrnCs4mfAvcB9gXHNEX29scDXu0yOQmNdlkQvBNYAB7j92frtp76JVfktc+94
CD00jmMp9d5ULQnj1h0EbFXROi+EOw+Exy6FASWwsRLeWGwcjkiUwujr4Y5x0Khafv2cRBNKgc+v
g6pnYfDj/mW+MaKbtibPouDTyltltSkWenrKlpZZ1vkQT4U78uz0XU/Z/hHkbC9L9cXibMwEzvTU
GwX8QEJR5VI2WZmoQhyntauE4c6Wp7wM4E7zUFyojIWMM747gXM89Z4GLpIQZ++JUHsjjFHwUisR
bprM0+lFav9wT9k1GbR6Pugmss3FC2kLfWZgGZmbZ8c+bTQ0QJZREuayv+/qIeL1wLc92ncSGQit
Tabph8D3MIH4hRJ9SHv9ewH3aRimTIgr0/jae/oYIpJhoBOaGkfrEfqrGXRzPhiGSd03I5ZEIoqF
SZ6yB4C5KW2s01hfBWUcmXzQ31NW5hAgpY1jtcBD9lVWvaHAStGuPhkyTJtlPkTmgZhA/8/EcgxR
8GXR0fc7+nhCzPEtcvoYLaQd6BnCm61E5nJgT2JIqRywPyabajt/DwqfivUA7Ss+iRu9OT9NrsPw
xzzfKEDn/QMeapoAe4jjNFb6G+wjtDb7HeYBm2WJv18mzrYMnYRIr3vIPAIjA7piQopHK5FDCrZr
uFsiFM30mTO+1R5/YKHVxxlAlTgr9Z4lcVkRSXuO3Mc6uT77OoZhsnm1Beqri0RuTpSVLn2dS0Rm
zM7gG2SLMZjsZAlmm8BVjn5+DRN6/Xea0KG9Fu8VIYrQjNDypJViUq4rMOnO3azvq7WRA08Joc9O
x8M1POFZ6uo9ZO4LPGzJl4dVS23fxflcHRhfDU1ZvLo0SbWJOU/FkPovMsF3We3VWW0WA8Pxb5LC
GUO+eASTqXOxUqJXjUW4tmnG7njl7M8x+Y46e/nvlYVDFxuSJu8eiHzYkZXNymQSu9A85VsvVnu2
jOU8J7nzsaftDZ6yKgyp0/idp44tudbT5BTa49vFGd8yBbXaWKpLxOovtOSNjZdV8ZZggEdlBdps
WeISWfEmilRqV4B+7gkQepgs+X8owrVdIM57bwljLpdjCZ4IXFnAW8yb0AG5AcayIsu9HRwf7Dh6
K4DTRDON9ITvXD1bp5xthLLl9VjbkiiTzLDrfEUmDEwGb7IyxHDH58Y8F2mjTacBxyhLfnjCWPOK
rJOfAH4b+G6WWNCOBejnXrknx3m+uwGzyei9Al/b83LEQgr//orNSjRJHjgksOw9GeFguJLnWmB8
YCwHxHC6zqL5HpQqh8xjxTtOiV4foUzq3wfl8eTvBipVcy2domU2tNiEjsIqTKa3QwEt5qZAKK2K
VkYqECssxFN2lqdsftr6xSD0OGCmatqymSn896RD1hLL8v63/3RoTcPNEpbsJuG4Q1W0zrUJvV10
dZknPKUcr/9Tojfa7AgspHBvxKzF7NH24Wg8cfkdTehXPeWleernAZgQlm9ZCmGI83kL8MtA+50x
O9O8UkYwWuSK7USM1Sb8ls7mnQj0VEZmbMlwWV+wVzDx8M/3bNpy5caCAoQ/88XX8Sc/csVtONLN
wk1E7+YrKsoChO5fAOtc4rHOT0Wc40qI6cq/jwJMksNuf6Nngke4MkrCTT8GXlLNw1uZHtAUcJBV
tKtES3xzV+F8for/PTQC54mf42rzXcU5nNBaFtq3zHbKde+y3Hw389iASVVHRURcQs+O6MaVEtOU
2fBjw400PK3gMgXPZ0NmwaE0DycSWj0w8eC2op996IlxlvPFakySyofxmBBmqxD6nwGRPyiP5c21
8Jc5UQAXIx2Z8yGBjS3ahM5OcCxvZYzx1+QxT+Ocz0sVvOwZWy9MEiiNTcrKdrYRzCHeq1FxcCPm
DRsfKmnaOrvjCC3Wymc9L8rBOvel5buDdylz4VEY5Xyeq8JB+tMcj/3SQBRkkOfhzTT+kpiEnh+o
V+GJMLQldMVsuo96uDvGLAPjG0zC7yP0IP57pL72O+VEaPl7Ky0tzkk6xlZPiwydMO/RlVvF9wGT
Y5zuEuHZiLq2F12pPMF8IWafDKR0zxkLLNWOsylW9yCn+nMx5YaWf8o0XKmbz00uKMnz/FHiN9Vk
kCQudoswCMsinP2JYoDiyCAXvXImtHjq59E8m5XC/DzBHjHI3AsTPTjcchquAk6NsZ+5FLMN1MaL
gbqThVwNmJTnVF89se5vO8V76pYrARqGaxO+e0wcSzfbeKxDpEbCgX73wewtLwdrebB750nIXM/v
iElcnRJDfvUM8KRHxDlXE977c7MTIXLRDv9eonJyiLaVWSTQ2ujf6ZbTUAEs18bJe9KVAaJnz8W8
M5e2iK+KZp4TcwwH4mwTBa7ScJOVSu6CeWVpOmZb5xnKJDai8JzHMZyrTcjpbem3Qm7048DwQBza
tezVykMIbUjjWvLj5JgBTFH+dH02ODlQfqlYwjrrqBcrtzfGKJVE+BPt5f6N9ji/aVyAyRSuxbwB
b2Or8OAZzyrSQxzjKcDfaHLeO2Ik6vERq9GFovk/JHNY1b+ECXmuxOxPsPP/myRMsxITlRiE2RDT
yfJ6rwVmZfNCrTYvlIbStpsxKfj9ZAKqgEsikjN2u70lghNlWaqBqSqw71u21q6n+Z6UW5W5uW7d
AzyaeQ0mVp3vvvI9MSns0QXS0tdhwpfI3Ga7tXU8Zv+Ii1vwzI2F20UJVJBFOltwWxz5WuZZrj8D
rtDGqpyE0dFDxZKNshy4GiH4HGC2Mv/PVdfZqBWLUYJJoJQCfwX+rPw/SEJAdqzTxgGqFNnQXuTC
aszGlnnAjAwhvH3lvGWy8lRjUuU+pH9T4yB56B8BflWg3/vrLku6pumHZNI/pZD+2az0z365Rz1N
P0CTPh622v4U+KPUSx91lvz0tbk2MM7LZTz1YsW3Csc6ypGOdH1k9Vnn9G33WWb9/6WcLHSExUth
HKZtwDpVmO2IaDM5fZ3oyu0iez6IY41j9FEqS+8Glc3voGXfTwrYXZklMkEroKQ1O9fGAr7lRgpa
8d27BDs5Uq3cvxsV2E5x3+xIkBC6qHD18yrV0oNOkGCntdBLkluSYKcktGTN3ID7K8ktSbCzWugx
Hqc0IXSCnZbQRzqf68k9lp0gQasT+iiPQ7g1uSUJ8kFZK+nn/ph9Fo2Y1PZKmv96UYIEOeE/+J4k
BZrmED0AAAAASUVORK5CYII=
</field>
</record>
<record id="user_demo" model="res.users">
<field name="partner_id" ref="base.partner_demo"/>
<field name="login">demo</field>
@ -14,7 +82,7 @@
<field name="signature">--
Mr Demo</field>
<field name="company_id" ref="main_company"/>
<field name="groups_id" eval="[(6,0,[ref('base.group_user')])]"/>
<field name="groups_id" eval="[(6,0,[ref('base.group_user'), ref('base.group_partner_manager')])]"/>
<field name="image">/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcHBw8LCwkMEQ8SEhEP
ERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/2wBDAQUFBQcGBw4ICA4eFBEUHh4eHh4e
Hh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh7/wAARCACEAIQDASIA

File diff suppressed because it is too large Load Diff

View File

@ -8,14 +8,14 @@ msgstr ""
"Project-Id-Version: openobject-server\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-12-03 16:01+0000\n"
"PO-Revision-Date: 2012-08-20 15:47+0000\n"
"Last-Translator: OpenERP Administrators <Unknown>\n"
"PO-Revision-Date: 2012-12-17 20:41+0000\n"
"Last-Translator: David Bowers <sales@skitzotek.com>\n"
"Language-Team: English (United Kingdom) <en_GB@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-12-04 05:02+0000\n"
"X-Generator: Launchpad (build 16335)\n"
"X-Launchpad-Export-Date: 2012-12-18 04:58+0000\n"
"X-Generator: Launchpad (build 16372)\n"
#. module: base
#: model:ir.module.module,description:base.module_account_check_writing
@ -48,7 +48,7 @@ msgid ""
"The second argument of the many2many field %s must be a SQL table !You used "
"%s, which is not a valid SQL table name."
msgstr ""
"The second argument of the many2many field %s must be a SQL table !You used "
"The second argument of the many2many field %s must be a SQL table! You used "
"%s, which is not a valid SQL table name."
#. module: base
@ -101,7 +101,7 @@ msgstr ""
msgid ""
"Model name on which the method to be called is located, e.g. 'res.partner'."
msgstr ""
"Model name on which the method to be called is located, e.g. 'res.partner'."
"Model name in which the method to be called is located, e.g. 'res.partner'."
#. module: base
#: view:ir.module.module:0
@ -123,6 +123,17 @@ msgid ""
" * Product Attributes\n"
" "
msgstr ""
"\n"
"A module which adds manufacturers and attributes to the product form.\n"
"=====================================================================\n"
"\n"
"You can now define the following for a product:\n"
"-----------------------------------------------\n"
" * Manufacturer\n"
" * Manufacturer Product Name\n"
" * Manufacturer Product Code\n"
" * Product Attributes\n"
" "
#. module: base
#: field:ir.actions.client,params:0
@ -226,6 +237,35 @@ msgid ""
"* Planned Revenue by Stage and User (graph)\n"
"* Opportunities by Stage (graph)\n"
msgstr ""
"\n"
"The generic OpenERP Customer Relationship Management\n"
"=====================================================\n"
"\n"
"This application enables a group of people to intelligently and efficiently "
"manage leads, opportunities, meetings and phone calls.\n"
"\n"
"It manages key tasks such as communication, identification, prioritization, "
"assignment, resolution and notification.\n"
"\n"
"OpenERP ensures that all cases are successfully tracked by users, customers "
"and suppliers. It can automatically send reminders, escalate a request, "
"trigger specific methods and many other actions based on your own enterprise "
"rules.\n"
"\n"
"The greatest thing about this system is that users don't need to do anything "
"special. The CRM module has an email gateway for the synchronization "
"interface between mails and OpenERP. That way, users can just send emails to "
"the request tracker.\n"
"\n"
"OpenERP will take care of thanking them for their message, automatically "
"routing it to the appropriate staff and make sure all future correspondence "
"gets to the right place.\n"
"\n"
"\n"
"Dashboard for CRM will include:\n"
"-------------------------------\n"
"* Planned Revenue by Stage and User (graph)\n"
"* Opportunities by Stage (graph)\n"
#. module: base
#: code:addons/base/ir/ir_model.py:397
@ -235,7 +275,7 @@ msgid ""
"them through Python code, preferably through a custom addon!"
msgstr ""
"Properties of base fields cannot be altered in this manner! Please modify "
"them through Python code, preferably through a custom addon!"
"them through Python code, preferably by a custom addon!"
#. module: base
#: code:addons/osv.py:130
@ -318,6 +358,7 @@ msgid ""
"The internal user that is in charge of communicating with this contact if "
"any."
msgstr ""
"The internal user who is in charge of communicating with this contact if any."
#. module: base
#: view:res.partner:0
@ -625,6 +666,19 @@ msgid ""
"* Use emails to automatically confirm and send acknowledgements for any "
"event registration\n"
msgstr ""
"\n"
"Organization and management of Events.\n"
"======================================\n"
"\n"
"The event module allows you to efficiently organise events and all related "
"tasks: planning, registration tracking,\n"
"attendance, etc.\n"
"\n"
"Key Features\n"
"------------\n"
"* Manage your Events and Registrations\n"
"* Use emails to automatically confirm and send acknowledgements for any "
"event registration\n"
#. module: base
#: selection:base.language.install,lang:0
@ -860,7 +914,7 @@ msgstr "Eritrea"
#. module: base
#: sql_constraint:res.company:0
msgid "The company name must be unique !"
msgstr "The company name must be unique !"
msgstr "The company name must be unique!"
#. module: base
#: model:ir.ui.menu,name:base.menu_base_action_rule_admin
@ -884,6 +938,9 @@ msgid ""
"image, with aspect ratio preserved. Use this field anywhere a small image is "
"required."
msgstr ""
"Small-sized image of this contact. It is automatically resized to 64x64 "
"pixels with the aspect ratio preserved. Use this field anywhere a small "
"image is required."
#. module: base
#: help:ir.actions.server,mobile:0
@ -892,7 +949,7 @@ msgid ""
"invoice, then `object.invoice_address_id.mobile` is the field which gives "
"the correct mobile number"
msgstr ""
"Provides fields that be used to fetch the mobile number, e.g. you select the "
"Provides fields used to fetch the mobile number, e.g. you select the "
"invoice, then `object.invoice_address_id.mobile` is the field which gives "
"the correct mobile number"
@ -988,8 +1045,6 @@ msgid ""
"Provide the field name that the record id refers to for the write operation. "
"If it is empty it will refer to the active id of the object."
msgstr ""
"Provide the field name that the record id refers to for the write operation. "
"If it is empty it will refer to the active id of the object."
#. module: base
#: help:ir.actions.report.xml,report_type:0
@ -1089,6 +1144,13 @@ msgid ""
"actions(Sign in/Sign out) performed by them.\n"
" "
msgstr ""
"\n"
"This module aims to manage employees' attendances.\n"
"==================================================\n"
"\n"
"Keeps account of the attendances of employees based on the\n"
"actions (Sign in/Sign out) performed by them.\n"
" "
#. module: base
#: model:res.country,name:base.nu
@ -1148,7 +1210,7 @@ msgid ""
msgstr ""
"Expression containing a value specification. \n"
"When Formula type is selected, this field may be a Python expression that "
"can use the same values as for the condition field on the server action.\n"
"can use the same values as the condition field on the server action.\n"
"If Value type is selected, the value will be used directly without "
"evaluation."
@ -1234,7 +1296,7 @@ msgstr "Guam (USA)"
#. module: base
#: sql_constraint:res.country:0
msgid "The name of the country must be unique !"
msgstr "The country name must be unique !"
msgstr "The country name must be unique!"
#. module: base
#: field:ir.module.module,installed_version:0
@ -1382,9 +1444,9 @@ msgid ""
"decimal number [00,53]. All days in a new year preceding the first Monday "
"are considered to be in week 0."
msgstr ""
"%W - Week number of the year (Monday as the first day of the week) as a "
"decimal number [00,53]. All days in a new year preceding the first Monday "
"are considered to be in week 0."
"%W - Week number of the year (Monday being the first day of the week) as an "
"integer [00,53]. All days in a new year preceding the first Monday are "
"considered to be in week 0."
#. module: base
#: code:addons/base/module/wizard/base_language_install.py:53
@ -1724,8 +1786,8 @@ msgid ""
"'%s' contains too many dots. XML ids should not contain dots ! These are "
"used to refer to other modules data, as in module.reference_id"
msgstr ""
"'%s' contains too many dots. XML ids should not contain dots ! These are "
"used to refer to other modules data, as in module.reference_id"
"'%s' contains too many dots. XML ids should not contain dots! These are used "
"to refer to other modules' data, as in module.reference_id"
#. module: base
#: model:ir.module.module,summary:base.module_sale
@ -1872,8 +1934,8 @@ msgid ""
"simplified payment mode encoding, automatic picking lists generation and "
"more."
msgstr ""
"Get the most out of your points of sales with fast sale encoding, simplified "
"payment mode encoding, automatic picking lists generation and more."
"Get the most out of your points of sale with fast sale encoding, simplified "
"payment mode encoding, automatic pick list generation and more."
#. module: base
#: code:addons/base/ir/ir_fields.py:165
@ -2021,6 +2083,14 @@ msgid ""
"with the effect of creating, editing and deleting either ways.\n"
" "
msgstr ""
"\n"
"Synchronization of project task work entries with timesheet entries.\n"
"====================================================================\n"
"\n"
"This module lets you transfer the entries under tasks defined for Project\n"
"Management to the Timesheet line entries for a particular date and user\n"
"with the effect of creating, editing and deleting either way.\n"
" "
#. module: base
#: model:ir.model,name:base.model_ir_model_access
@ -2132,6 +2202,25 @@ msgid ""
"* *Before Delivery*: A Draft invoice is created and must be paid before "
"delivery\n"
msgstr ""
"\n"
"Manage sales quotations and orders\n"
"==================================\n"
"\n"
"This module makes the link between the sales and warehouse management "
"applications.\n"
"\n"
"Preferences\n"
"-----------\n"
"* Shipping: Choice of delivery at once or partial delivery\n"
"* Invoicing: choose how invoices will be paid\n"
"* Incoterms: International Commercial terms\n"
"\n"
"You can choose flexible invoicing methods:\n"
"\n"
"* *On Demand*: Invoices are created manually from Sales Orders when needed\n"
"* *On Delivery Order*: Invoices are generated from picking (delivery)\n"
"* *Before Delivery*: A Draft invoice is created and must be paid before "
"delivery\n"
#. module: base
#: field:ir.ui.menu,complete_name:0
@ -2155,9 +2244,9 @@ msgid ""
"decimal number [00,53]. All days in a new year preceding the first Sunday "
"are considered to be in week 0."
msgstr ""
"%U - Week number of the year (Sunday as the first day of the week) as a "
"decimal number [00,53]. All days in a new year preceding the first Sunday "
"are considered to be in week 0."
"%U - Week number of the year (Sunday being the first day of the week) as an "
"integer [00,53]. All days in a new year preceding the first Sunday are "
"considered to be in week 0."
#. module: base
#: view:base.language.export:0
@ -2316,6 +2405,28 @@ msgid ""
"* Maximal difference between timesheet and attendances\n"
" "
msgstr ""
"\n"
"Record and validate timesheets and attendance easily\n"
"====================================================\n"
"\n"
"This application supplies a new screen enabling you to manage both "
"attendance (Sign in/Sign out) and your work encoding (timesheet) by period. "
"Timesheet entries are made by employees each day. At the end of the defined "
"period, employees validate their sheet and the manager must then approve "
"their team's entries. Periods are defined in the company forms and you can "
"set them to run monthly or weekly.\n"
"\n"
"The complete timesheet validation process is:\n"
"---------------------------------------------\n"
"* Draft sheet\n"
"* Confirmation at the end of the period by the employee\n"
"* Validation by the project manager\n"
"\n"
"The validation can be configured in the company:\n"
"------------------------------------------------\n"
"* Period size (Day, Week, Month)\n"
"* Maximal difference between timesheet and attendances\n"
" "
#. module: base
#: code:addons/base/ir/ir_fields.py:342
@ -2371,7 +2482,7 @@ msgstr "Belize"
#. module: base
#: help:ir.actions.report.xml,header:0
msgid "Add or not the corporate RML header"
msgstr "Add or not the corporate RML header"
msgstr "Optionally add the corporate RML header"
#. module: base
#: model:res.country,name:base.ge
@ -2410,6 +2521,32 @@ msgid ""
"\n"
" "
msgstr ""
"\n"
" \n"
"Belgian localization for in/outgoing invoices (prereq to account_coda):\n"
"=========================================================================\n"
" - Rename 'reference' field labels to 'Communication'\n"
" - Add support for Belgian Structured Communication\n"
"\n"
"A Structured Communication can be generated automatically on outgoing "
"invoices according to the following algorithms:\n"
"-----------------------------------------------------------------------------"
"----------------------------------------\n"
" 1) Random : +++RRR/RRRR/RRRDD+++\n"
" **R..R =** Random Digits, **DD =** Check Digits\n"
" 2) Date : +++DOY/YEAR/SSSDD+++\n"
" **DOY =** Day of the Year, **SSS =** Sequence Number, **DD =** Check "
"Digits\n"
" 3) Customer Reference +++RRR/RRRR/SSSDDD+++\n"
" **R..R =** Customer Reference without non-numeric characters, **SSS "
"=** Sequence Number, **DD =** Check Digits \n"
" \n"
"The preferred type of Structured Communication and associated Algorithm can "
"be\n"
"specified on the Partner records. A 'random' Structured Communication will\n"
"generated if no algorithm is specified on the Partner record. \n"
"\n"
" "
#. module: base
#: model:res.country,name:base.pl
@ -2454,8 +2591,8 @@ msgid ""
"order in Object, and you can have loop on the sales order line. Expression = "
"`object.order_line`."
msgstr ""
"Enter the field/expression that will return the list. E.g. select the sale "
"order in Object, and you can have loop on the sales order line. Expression = "
"Enter the field/expression that will return the list. E.g. selecting the "
"sale order in Object, you can loop on the sales order line: Expression = "
"`object.order_line`."
#. module: base
@ -2546,6 +2683,27 @@ msgid ""
"Print product labels with barcode.\n"
" "
msgstr ""
"\n"
"This is the base module for managing products and pricelists in OpenERP.\n"
"========================================================================\n"
"\n"
"Products support variants, various pricing methods, supplier information,\n"
"made to stock/order, different units of measures, packaging and other "
"properties.\n"
"\n"
"Pricelists support:\n"
"-------------------\n"
" * Multiple-levels of discount (by product, category, quantities)\n"
" * Compute price based on different criteria:\n"
" * Other pricelist\n"
" * Cost price\n"
" * List price\n"
" * Supplier price\n"
"\n"
"Set pricelist preferences by product and/or partner.\n"
"\n"
"Print product labels with barcode.\n"
" "
#. module: base
#: model:ir.module.module,description:base.module_account_analytic_default
@ -2563,6 +2721,18 @@ msgid ""
" * Date\n"
" "
msgstr ""
"\n"
"Set default values for your analytic accounts.\n"
"==============================================\n"
"\n"
"Allows to automatically select analytic accounts based on criteria:\n"
"---------------------------------------------------------------------\n"
" * Product\n"
" * Partner\n"
" * User\n"
" * Company\n"
" * Date\n"
" "
#. module: base
#: field:res.company,rml_header1:0
@ -2887,6 +3057,29 @@ msgid ""
"* Purchase Analysis\n"
" "
msgstr ""
"\n"
"Manage goods requirement by Purchase Orders easily\n"
"==================================================\n"
"\n"
"Purchase management enables you to track your suppliers' price quotations "
"and convert them into purchase orders if necessary.\n"
"OpenERP has several methods of monitoring invoices and tracking the receipt "
"of ordered goods. You can handle partial deliveries in OpenERP with the "
"ability to keep track of items yet to be delivered from orders and to issue "
"reminders automatically.\n"
"\n"
"OpenERPs replenishment-management rules enable the system to generate draft "
"purchase orders automatically. Alternatively you can configure it to run a "
"lean process driven entirely by current production needs.\n"
"\n"
"Dashboard / Reports for Purchase Management will include:\n"
"---------------------------------------------------------\n"
"* Request for Quotations\n"
"* Purchase Orders Waiting Approval \n"
"* Monthly Purchases by Category\n"
"* Receptions Analysis\n"
"* Purchase Analysis\n"
" "
#. module: base
#: model:ir.model,name:base.model_ir_actions_act_window_close
@ -3016,6 +3209,15 @@ msgid ""
" * ``base_stage``: stage management\n"
" "
msgstr ""
"\n"
"This module handles state and stage. It is derived from the crm_base and "
"crm_case classes of crm.\n"
"============================================================================="
"======================\n"
"\n"
" * ``base_state``: state management\n"
" * ``base_stage``: stage management\n"
" "
#. module: base
#: model:ir.module.module,shortdesc:base.module_web_linkedin
@ -3312,6 +3514,8 @@ msgid ""
"For more details about translating OpenERP in your language, please refer to "
"the"
msgstr ""
"For more details about translating OpenERP into your language, please refer "
"to the"
#. module: base
#: field:res.partner,image:0
@ -3563,19 +3767,11 @@ msgid ""
"Canadian accounting charts and localizations.\n"
" "
msgstr ""
"\n"
"This is the module to manage the English and French - Canadian accounting "
"chart in OpenERP.\n"
"============================================================================="
"==============\n"
"\n"
"Canadian accounting charts and localisations.\n"
" "
#. module: base
#: view:base.module.import:0
msgid "Select module package to import (.zip file):"
msgstr "Select module package to import (.zip file):"
msgstr "Select module package to import (*.zip file):"
#. module: base
#: view:ir.filters:0
@ -3674,6 +3870,14 @@ msgid ""
"easily\n"
"keep track and order all your purchase orders.\n"
msgstr ""
"\n"
"This module allows you to manage your Purchase Requisition.\n"
"===========================================================\n"
"\n"
"When a purchase order is created, you now have the opportunity to save the\n"
"related requisition. By regrouping, this new object will allow you to "
"easily\n"
"keep track of and order all your purchase orders.\n"
#. module: base
#: help:ir.mail_server,smtp_host:0
@ -3798,13 +4002,13 @@ msgstr "If not set, acts as a default value for new resources"
#: code:addons/orm.py:4213
#, python-format
msgid "Recursivity Detected."
msgstr "Recursivity detected."
msgstr "Recursion detected."
#. module: base
#: code:addons/base/module/module.py:345
#, python-format
msgid "Recursion error in modules dependencies !"
msgstr "Recursion error in modules dependencies !"
msgstr "Recursion error in modules dependencies!"
#. module: base
#: model:ir.module.module,description:base.module_analytic_user_function
@ -3947,7 +4151,7 @@ msgstr "12. %w ==> 5 ( Friday is the 6th day)"
#. module: base
#: constraint:res.partner.category:0
msgid "Error ! You can not create recursive categories."
msgstr "Error ! You can not create recursive categories."
msgstr "Error! You can not create recursive categories."
#. module: base
#: view:res.lang:0

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -7,14 +7,14 @@ msgstr ""
"Project-Id-Version: OpenERP Server 5.0.4\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2012-12-03 16:01+0000\n"
"PO-Revision-Date: 2012-12-03 11:15+0000\n"
"PO-Revision-Date: 2012-12-06 10:07+0000\n"
"Last-Translator: Quentin THEURET <Unknown>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-12-04 04:55+0000\n"
"X-Generator: Launchpad (build 16335)\n"
"X-Launchpad-Export-Date: 2012-12-07 04:34+0000\n"
"X-Generator: Launchpad (build 16341)\n"
#. module: base
#: model:ir.module.module,description:base.module_account_check_writing
@ -26,7 +26,7 @@ msgid ""
msgstr ""
"\n"
"Module pour l'écriture et l'impression de chèques.\n"
"================================================\n"
"==================================================\n"
" "
#. module: base
@ -129,10 +129,10 @@ msgid ""
msgstr ""
"\n"
"Ce module ajoute des fabricants et des attributs sur le formulaire produit.\n"
"==========================================================\n"
"===========================================================================\n"
"\n"
"Vous pouvez maintenant définir pour un produit:\n"
"-------------------------------------------------------------------\n"
"-----------------------------------------------\n"
" * un fabricant\n"
" * le nom du produit chez le fabricant\n"
" * le code produit chez le fabricant\n"
@ -153,7 +153,7 @@ msgid ""
msgstr ""
"\n"
"Ce module ajoute les utilisateurs Google dans res.user.\n"
"========================================\n"
"=======================================================\n"
#. module: base
#: help:res.partner,employee:0
@ -212,7 +212,7 @@ msgid ""
msgstr ""
"\n"
"Génère vos factures à partir des dépenses et des feuilles de temps.\n"
"=====================================================\n"
"===================================================================\n"
"\n"
"Module pour générer les factures basées sur les coûts (RH, dépenses…).\n"
"\n"
@ -253,8 +253,8 @@ msgid ""
"* Opportunities by Stage (graph)\n"
msgstr ""
"\n"
"Le module générique de Gestion de la Relation Client d'OpenERP\n"
"===================================================\n"
"Le module générique de Gestion de la Relation Client d'OpenERP (CRM)\n"
"====================================================================\n"
"Cette application permet à un groupe de personnes de gérer intelligemment et "
"efficacement les pistes, les opportunités, les réunions et les appels "
"téléphoniques.\n"
@ -277,8 +277,8 @@ msgstr ""
"automatiquement à l'équipe appropriée et s'assurer que les correspondances "
"futures se fassent au bon endroit.\n"
"\n"
"Le tableau de bord GRC inclura :\n"
"--------------------------------------------\n"
"Le tableau de bord CRM inclura :\n"
"--------------------------------\n"
"* Le revenu planifié par étape et par utilisateur (graphique)\n"
"* Les opportunités par étape (graphique)\n"
@ -364,7 +364,7 @@ msgid ""
msgstr ""
"\n"
"Comptabilité chilienne et mise en place des taxes pour le Chili\n"
"================================================\n"
"===============================================================\n"
"Plan comptable chilien et taxes en accord avec la législation en vigueur.\n"
"\n"
" "
@ -418,10 +418,10 @@ msgid ""
msgstr ""
"\n"
"Ajoute des informations de date additionnelles au commandes de vente.\n"
"==========================================================\n"
"=====================================================================\n"
"\n"
"Vous pouvez ajouter les dates suivantes à un bon de commande:\n"
"-----------------------------------------------------------\n"
"-------------------------------------------------------------\n"
" * Date de la demande\n"
" * Date de confirmation\n"
" * Date Effective\n"
@ -486,7 +486,7 @@ msgid ""
msgstr ""
"\n"
"Installateur invisible pour la base de connaissances\n"
"==========================================\n"
"====================================================\n"
"\n"
"Rend disponible la configuration de lapplication des connaissances depuis "
"l'endroit où vous\n"
@ -514,7 +514,7 @@ msgstr ""
"Permet d'ajouter des méthodes de livraison dans les commandes de vente et "
"dans les livraisons.\n"
"============================================================================="
"\n"
"=================\n"
"\n"
"Vous pouvez définir vos propres transporteurs et grilles de prix de "
"livraison. Quand vous créez \n"
@ -701,7 +701,7 @@ msgid ""
msgstr ""
"\n"
"Module de définition des comptes analytiques.\n"
"========================================\n"
"=============================================\n"
"\n"
"Dans OpenERP, les comptes analytiques sont liés à des comptes financiers "
"mais sont traités\n"
@ -734,14 +734,14 @@ msgid ""
msgstr ""
"\n"
"Gestion et organisation d'événements\n"
"=================================\n"
"====================================\n"
"\n"
"Le module 'event' vous permet d'organiser efficacement des événements et "
"toutes les tâches liées : planification, suivi des inscriptions,\n"
"des présences, etc.\n"
"\n"
"Fonctionnalités clés\n"
"----------------------------\n"
"--------------------\n"
"* Gestion des événements et des inscriptions\n"
"* Utilisation d'e-mails pour automatiquement confirmer et envoyer des "
"accusés de réception pour chaque inscription à un événement\n"
@ -941,10 +941,10 @@ msgid ""
msgstr ""
"\n"
"Comptabilité et gestion financière\n"
"================================\n"
"==================================\n"
"\n"
"Le module de comptabilité et de finance couvre :\n"
"---------------------------------------------------------------------\n"
"------------------------------------------------\n"
" * Comptabilité générale\n"
" * Comptabilité de coût / comptabilité analytique\n"
" * Comptabilité de tiers\n"
@ -955,8 +955,7 @@ msgstr ""
" * Lettrages par partenaire\n"
"\n"
"Il créé également un tableau de bord pour les comptables qui inclut :\n"
"-----------------------------------------------------------------------------"
"------------------\n"
"---------------------------------------------------------------------\n"
" * Une liste des factures clients à approuver\n"
" * L'analyse de la société\n"
" * Un graphe de la trésorerie\n"
@ -1008,7 +1007,7 @@ msgid ""
msgstr ""
"\n"
"Module LinkedIn pour OpenERP Web.\n"
"===============================\n"
"=================================\n"
"Ce module fournit l'intégration de LinkedIn avec OpenERP.\n"
" "
@ -1153,6 +1152,33 @@ msgid ""
"also possible in order to automatically create a meeting when a holiday "
"request is accepted by setting up a type of meeting in Leave Type.\n"
msgstr ""
"\n"
"Gère les absences et les demandes de congés\n"
"===========================================\n"
"\n"
"Cette application contrôle la programmation des congés de votre société. Il "
"permet aux employés de faire des demandes de congés. Ensuite, les managers "
"peuvent examiner les demandes et les approuver ou les rejeter. De cette "
"façon, vous pouvez contrôler le planning de l'ensemble des congés de votre "
"société ou de votre département.\n"
"\n"
"Vous pouvez configurer plusieurs sortes d'absences (maladie, congés payés, "
"congés sans soldes…) et allouer rapidement ces absences à un employé ou un "
"département en utilisant les demandes de congés. Un employée peut aussi "
"faire une demande de congés pour plus de jours en ajoutant une allocation. "
"Cela va augmenter le total des jours disponibles pour ce type de congés (si "
"la demande est accepté).\n"
"\n"
"Vous pouvez suivre les absences de différentes façons grâce aux rapports "
"suivants : \n"
"\n"
"* Résumé des absences\n"
"* Absences par département\n"
"* Analyse des absences\n"
"\n"
"Une synchronisation avec les agendas internes (Réunions du module CRM) est "
"aussi possible dans le but de créer automatiquement une réunion quand une "
"demande de congés est accepté en ajoutant une type de réunion Absence.\n"
#. module: base
#: selection:base.language.install,lang:0
@ -1257,6 +1283,13 @@ msgid ""
" * the Tax Code Chart for Luxembourg\n"
" * the main taxes used in Luxembourg"
msgstr ""
"\n"
"Ceci est le module de base pour gérer le plan de comptes pour le Luxembourg\n"
"===========================================================================\n"
"\n"
" * le plan de compte KLUWER\n"
" * le plan de codes de taxes pour le Luxembourg\n"
" * les taxes principales utilisées au Luxembourg"
#. module: base
#: model:res.country,name:base.om
@ -1281,7 +1314,7 @@ msgid ""
msgstr ""
"\n"
"Module de pointage des heures pour les employés.\n"
"==========================================\n"
"================================================\n"
"\n"
"Comptabilise les temps de présence des employés sur la base des\n"
"pointages (Entrée / Sorties) réalisés.\n"
@ -1725,7 +1758,7 @@ msgstr ""
"dans la configuration du serveur.\n"
"\n"
"Paramètres de configuration du serveur :\n"
"-------------------------------\n"
"----------------------------------------\n"
"[webdav]:\n"
"+++++++++ \n"
" * enable = True ; Sert le webdav à travers les serveurs http(s)\n"
@ -1801,7 +1834,7 @@ msgstr ""
"Ce module ajoute un menu pour les réclamations et les fonctionnalités pour "
"votre portail si les modules claim et portal sont installés.\n"
"============================================================================="
"=======================================\n"
"==========================================================\n"
" "
#. module: base
@ -1895,7 +1928,7 @@ msgid ""
msgstr ""
"\n"
"Le module de base pour gérer les repas.\n"
"====================================\n"
"=======================================\n"
"\n"
"Des sociétés commandent des sandwiches, des pizzas et d'autres repas, à des "
"fournisseurs, pour leurs employés pour leur offrir plus de commodités.\n"
@ -2114,7 +2147,7 @@ msgstr ""
"Ce module fournit le plan comptable standard pour l'Autriche qui est basé "
"sur le modèle BMF.gv.at.\n"
"============================================================================="
"======================== \n"
"===================== \n"
"Veuillez garder à l'esprit que vous devez le revoir et l'adapter avec votre "
"comptable avant de l'utiliser dans un environnement de production.\n"
@ -2137,7 +2170,7 @@ msgid ""
msgstr ""
"\n"
"Droits d'accès à la comptabilité\n"
"==========================\n"
"================================\n"
"Cela donne à l'administrateur les droits d'accès à toutes les "
"fonctionnalités comptables telles que les mouvements comptables et le plan "
"de comptes.\n"
@ -2234,7 +2267,7 @@ msgstr ""
"Ce module ajoute les outils de partage générique pour votre base de données "
"OpenERP actuelle.\n"
"============================================================================="
"============\n"
"================\n"
"\n"
"Il ajoute spécifiquement un bouton 'Partager' qui est disponible dans le "
"client web pour\n"
@ -2342,7 +2375,8 @@ msgstr ""
"\n"
"Synchronisation les entrées dans les tâches avec les entrées dans les "
"feuilles de temps.\n"
"=========================================================================\n"
"============================================================================="
"===========\n"
"\n"
"Ce module vous permet de transférer les entrées sous les tâches définies "
"pour la gestion de\n"
@ -2421,7 +2455,7 @@ msgid ""
msgstr ""
"\n"
"Module de base pour gérer le plan comptable pour l'Équateur dans OpenERP.\n"
"==============================================================\n"
"=========================================================================\n"
"\n"
"Module pour la comptabilité équatorienne\n"
" "
@ -2472,13 +2506,13 @@ msgid ""
msgstr ""
"\n"
"Gestion des devis et des commandes de vente\n"
"=========================================\n"
"===========================================\n"
"\n"
"Ce module fait le lien entre les ventes et les applications de gestion des "
"entrepôts.\n"
"\n"
"Préférences\n"
"-------------------\n"
"-----------\n"
"* Expédition : Choix de la livraison en une fois ou une livraison partielle\n"
"* Facturation : Choisir comment les factures vont être payées\n"
"* Incoterms : International Commercial terms\n"
@ -2677,6 +2711,29 @@ msgid ""
"* Maximal difference between timesheet and attendances\n"
" "
msgstr ""
"\n"
"Enregistrer et valider facilement les feuilles de temps et les présences\n"
"========================================================================\n"
"\n"
"Cette application fournie un nouvel écran vous permettant de gérer à la fois "
"les présences (entrées/sorties) et l'enregistrement du travail (feuille de "
"temps) par période. Les saisies dans la feuille de temps sont faites par les "
"employés chaque jour. À la fin de la période définie, les employés valident "
"leurs feuilles de temps et le responsable doit approuver les saisies de son "
"équipe. Les périodes sont définis dans le formulaire de la société et vous "
"pouvez les paramétrer sur une base mensuelle ou hebdomadaire.\n"
"\n"
"Le processus complet de la validation des feuilles de temps est :\n"
"-----------------------------------------------------------------\n"
"* Feuille en brouillon\n"
"* Confirmation de la feuille à la fin de la période par les employées\n"
"* Validation par le responsable du projet\n"
"\n"
"La validation peut être paramétrée dans la société :\n"
"----------------------------------------------------\n"
"* Durée de la période (jour, semaine, mois)\n"
"* Différence maximale entre la feuille de temps et les présences\n"
" "
#. module: base
#: code:addons/base/ir/ir_fields.py:342
@ -2844,7 +2901,7 @@ msgid ""
msgstr ""
"\n"
"Gestion de Support (Helpdesk).\n"
"====================\n"
"==============================\n"
"\n"
"Les dossiers et le traitement des réclamations sont bien suivis et tracés "
"avec le Helpdesk.\n"
@ -2932,11 +2989,25 @@ msgid ""
" * Date\n"
" "
msgstr ""
"\n"
"Définir des valeurs par défaut pour les comptes analytiques.\n"
"============================================================\n"
"\n"
"Permet de sélectionner automatiquement les comptes analytiques selon des "
"critères :\n"
"-----------------------------------------------------------------------------"
"------\n"
" * Article\n"
" * Partenaire\n"
" * Utilisateur\n"
" * Société\n"
" * Date\n"
" "
#. module: base
#: field:res.company,rml_header1:0
msgid "Company Slogan"
msgstr ""
msgstr "Slogan de la société"
#. module: base
#: model:res.country,name:base.bb
@ -2960,7 +3031,7 @@ msgstr ""
#. module: base
#: model:ir.module.module,shortdesc:base.module_auth_oauth_signup
msgid "Signup with OAuth2 Authentication"
msgstr ""
msgstr "Se connecter avec l'authentification OAuth2"
#. module: base
#: selection:ir.model,state:0
@ -3021,6 +3092,10 @@ msgid ""
"==================================\n"
" "
msgstr ""
"\n"
"États-Unis d'Amérique - Plan comptable.\n"
"=======================================\n"
" "
#. module: base
#: field:ir.actions.act_url,target:0
@ -3055,7 +3130,7 @@ msgstr "Nom du raccourci"
#. module: base
#: field:res.partner,contact_address:0
msgid "Complete Address"
msgstr ""
msgstr "Adresse complète"
#. module: base
#: help:ir.actions.act_window,limit:0
@ -3127,7 +3202,7 @@ msgstr "ID de lenregistrement"
#. module: base
#: view:ir.filters:0
msgid "My Filters"
msgstr ""
msgstr "Mes filtres"
#. module: base
#: field:ir.actions.server,email:0
@ -3141,12 +3216,16 @@ msgid ""
"Module to attach a google document to any model.\n"
"================================================\n"
msgstr ""
"\n"
"Module pour attacher des documents Google à n'importe quel objet.\n"
"=================================================================\n"
#. module: base
#: code:addons/base/ir/ir_fields.py:334
#, python-format
msgid "Found multiple matches for field '%%(field)s' (%d matches)"
msgstr ""
"Plusieurs résultats ont été trouvés pour le champ '%%(field)s' (%d résultats)"
#. module: base
#: selection:base.language.install,lang:0
@ -3180,7 +3259,7 @@ msgstr "Arguments envoyés au client avec la vue"
#. module: base
#: model:ir.module.module,summary:base.module_contacts
msgid "Contacts, People and Companies"
msgstr ""
msgstr "Contacts, personnes et sociétés"
#. module: base
#: model:res.country,name:base.tt
@ -3213,7 +3292,7 @@ msgstr "Responsable"
#: code:addons/base/ir/ir_model.py:718
#, python-format
msgid "Sorry, you are not allowed to access this document."
msgstr ""
msgstr "Désolé, vous n'êtes pas autorisé à accéder à ce document."
#. module: base
#: model:res.country,name:base.py
@ -3329,7 +3408,7 @@ msgstr ""
#: code:addons/base/ir/ir_fields.py:175
#, python-format
msgid "'%s' does not seem to be an integer for field '%%(field)s'"
msgstr ""
msgstr "'%s' ne semble pas être un entier pour le champ '%%(field)s'"
#. module: base
#: model:ir.module.category,description:base.module_category_report_designer
@ -3390,7 +3469,7 @@ msgstr ""
#. module: base
#: model:ir.module.module,shortdesc:base.module_web_linkedin
msgid "LinkedIn Integration"
msgstr ""
msgstr "Intégration avec LinkedIn"
#. module: base
#: code:addons/orm.py:2021
@ -3439,6 +3518,8 @@ msgid ""
"the user will have an access to the sales configuration as well as statistic "
"reports."
msgstr ""
"l'utilisateur aura un accès à la configuration des ventes ainsi qu'aux "
"rapports statistiques."
#. module: base
#: model:res.country,name:base.nz
@ -3585,7 +3666,7 @@ msgstr "Arménie"
#. module: base
#: model:ir.module.module,summary:base.module_hr_evaluation
msgid "Periodical Evaluations, Appraisals, Surveys"
msgstr ""
msgstr "Évaluations périodiques, appréciations, sondages"
#. module: base
#: model:ir.actions.act_window,name:base.ir_property_form
@ -3625,6 +3706,31 @@ msgid ""
" payslip interface, but not in the payslip report\n"
" "
msgstr ""
"\n"
"Règles pour la paie française.\n"
"==============================\n"
"\n"
" - Configuration du module hr_payroll pour la localisation française\n"
" - Toutes les principales règles de contribution pour la fiche de paie "
"française, pour les cadres et les non-cadres\n"
" - Nouveau rapport des feuilles de paie\n"
"\n"
"Reste à faire :\n"
"---------------\n"
" - Intégration avec le module de gestion des congés pour la déduction des "
"congés et la gestion des indemnités\n"
" - Intégration avec le module hr_payroll_account pour la création des "
"lignes d'écritures \n"
" depuis la feuille de paie\n"
" - Continuer l'intégration des contributions. Seules les principales "
"contributions sont \n"
" actuellement implémentées.\n"
" - Retravailler le rapport sous webkit\n"
" - Les lignes de paie avec appears_in_payslip = False doivent apparaître "
"dans\n"
" l'interface des fiches de paie, mais pas dans le rapport des fiches de "
"paie.\n"
" "
#. module: base
#: model:res.country,name:base.se
@ -3634,7 +3740,7 @@ msgstr "Suède"
#. module: base
#: field:ir.actions.report.xml,report_file:0
msgid "Report File"
msgstr ""
msgstr "Fichier de rapport"
#. module: base
#: selection:ir.actions.act_window.view,view_mode:0
@ -3667,7 +3773,7 @@ msgstr ""
#: code:addons/orm.py:3839
#, python-format
msgid "Missing document(s)"
msgstr ""
msgstr "Document(s) manquant(s)"
#. module: base
#: model:ir.model,name:base.model_res_partner_bank_type
@ -3682,6 +3788,8 @@ msgid ""
"For more details about translating OpenERP in your language, please refer to "
"the"
msgstr ""
"Pour plus de détails concernant la traduction d'OpenERP dans votre langue, "
"veuillez vous référer à"
#. module: base
#: field:res.partner,image:0
@ -3807,6 +3915,28 @@ msgid ""
"database,\n"
" but in the servers rootpad like /server/bin/filestore.\n"
msgstr ""
"\n"
"Ceci est un système complet de gestion de documents.\n"
"=====================================================\n"
"\n"
" * Authentification des utilisateurs\n"
" * Indexation des documents ; les fichiers .pptx et .docx ne sont pas "
"supportés sous Windows.\n"
" * Tableau de bord des documents qui inclut :\n"
" * Nouveaux fichiers (liste)\n"
" * Fichiers par type de ressources (graphique)\n"
" * Fichiers par partenaire (graphique)\n"
" * Taille des fichiers par mois (graphique)\n"
"\n"
"ATTENTION :\n"
"-----------\n"
"\n"
" - Quand vous installez ce module en production dans une société qui a "
"déjà des fichiers PDF\n"
" enregistrés dans la base de données, ils seront tous perdus.\n"
" - Après l'installation de ce module les PDF ne seront plus enregistrés "
"dans la base de données,\n"
" mais à la racine du serveur dans /server/bin/filestore.\n"
#. module: base
#: help:res.currency,name:0
@ -3898,7 +4028,7 @@ msgstr "Finlandais / Suomi"
#. module: base
#: view:ir.config_parameter:0
msgid "System Properties"
msgstr ""
msgstr "Propriétés système"
#. module: base
#: field:ir.sequence,prefix:0
@ -3955,7 +4085,7 @@ msgstr "Personnel"
#. module: base
#: field:base.language.export,modules:0
msgid "Modules To Export"
msgstr ""
msgstr "Modules à exporter"
#. module: base
#: model:res.country,name:base.mt
@ -3968,6 +4098,8 @@ msgstr "Malte"
msgid ""
"Only users with the following access level are currently allowed to do that"
msgstr ""
"Seuls les utilisateurs avec les niveaux d'accès suivants sont actuellement "
"autorisé à faire cela"
#. module: base
#: field:ir.actions.server,fields_lines:0
@ -4128,7 +4260,7 @@ msgstr "De droite à gauche"
#. module: base
#: model:res.country,name:base.sx
msgid "Sint Maarten (Dutch part)"
msgstr ""
msgstr "Saint-Martin (partie néerlandaise)"
#. module: base
#: view:ir.actions.act_window:0

15072
openerp/addons/base/i18n/hi.po Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -7,14 +7,14 @@ msgstr ""
"Project-Id-Version: OpenERP Server 5.0.4\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2012-12-03 16:01+0000\n"
"PO-Revision-Date: 2012-08-20 15:27+0000\n"
"Last-Translator: Dorin <dhongu@gmail.com>\n"
"PO-Revision-Date: 2012-12-18 18:59+0000\n"
"Last-Translator: Fekete Mihai <mihai@erpsystems.ro>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-12-04 04:59+0000\n"
"X-Generator: Launchpad (build 16335)\n"
"X-Launchpad-Export-Date: 2012-12-19 05:14+0000\n"
"X-Generator: Launchpad (build 16378)\n"
#. module: base
#: model:ir.module.module,description:base.module_account_check_writing
@ -24,6 +24,10 @@ msgid ""
"================================================\n"
" "
msgstr ""
"\n"
"Modul pentru Verifica Scrisul si Verifica Imprimarea.\n"
"================================================\n"
" "
#. module: base
#: model:res.country,name:base.sh
@ -59,7 +63,7 @@ msgstr "Alcatuirea vizualizarii"
#. module: base
#: model:ir.module.module,summary:base.module_sale_stock
msgid "Quotation, Sale Orders, Delivery & Invoicing Control"
msgstr ""
msgstr "Cotatie, Ordine de vanzari, Controlul Livrarii & Facturarii"
#. module: base
#: selection:ir.sequence,implementation:0
@ -88,25 +92,25 @@ msgstr ""
#. module: base
#: model:ir.module.module,summary:base.module_point_of_sale
msgid "Touchscreen Interface for Shops"
msgstr ""
msgstr "Interfata cu Ecran tactil pentru Magazine"
#. module: base
#: model:ir.module.module,shortdesc:base.module_l10n_in_hr_payroll
msgid "Indian Payroll"
msgstr ""
msgstr "Stat de plata indian"
#. module: base
#: help:ir.cron,model:0
msgid ""
"Model name on which the method to be called is located, e.g. 'res.partner'."
msgstr ""
"Numele modelului unde este localizata metoda care urmeaza a fi efectuata, de "
"exemplu 'res.partener'."
"Numele modelului unde este localizata metoda care va fi numita, de exemplu "
"'res.partener'."
#. module: base
#: view:ir.module.module:0
msgid "Created Views"
msgstr "Afisari Create"
msgstr "Vizualizari Create"
#. module: base
#: model:ir.module.module,description:base.module_product_manufacturer
@ -123,6 +127,17 @@ msgid ""
" * Product Attributes\n"
" "
msgstr ""
"\n"
"Un modul care adauga producatori si atribute la formularul produsului.\n"
"====================================================================\n"
"\n"
"Acum puteti defini urmatoarele pentru un produs:\n"
"-----------------------------------------------\n"
" * Producator\n"
" * Numele Producatorului Produsului\n"
" * Codul Producatorului Produsului\n"
" * Atributele Produsului\n"
" "
#. module: base
#: field:ir.actions.client,params:0
@ -136,11 +151,14 @@ msgid ""
"The module adds google user in res user.\n"
"========================================\n"
msgstr ""
"\n"
"Acest modul adauga utilizatorul google la utilizatorul res.\n"
"========================================\n"
#. module: base
#: help:res.partner,employee:0
msgid "Check this box if this contact is an Employee."
msgstr ""
msgstr "Bifati aceasta casuta daca acest contact este un Angajat."
#. module: base
#: help:ir.model.fields,domain:0
@ -171,7 +189,7 @@ msgstr "Fereastra tinta"
#. module: base
#: field:ir.actions.report.xml,report_rml:0
msgid "Main Report File Path"
msgstr ""
msgstr "Ruta Principala Fisier Raport"
#. module: base
#: model:ir.module.module,shortdesc:base.module_sale_analytic_plans
@ -192,6 +210,16 @@ msgid ""
"revenue\n"
"reports."
msgstr ""
"\n"
"Genereaza Facturi din Cheltuieli, Inregistrari ale Fiselor de Pontaj.\n"
"========================================================\n"
"\n"
"Modul pentru generarea facturilor pe baza costurilor (resurse umane, "
"cheltuieli, ...).\n"
"\n"
"Puteti defini liste de preturi in contul analitic, puteti face unele raporte "
"teoretice\n"
"ale veniturilor."
#. module: base
#: model:ir.module.module,description:base.module_crm
@ -226,6 +254,36 @@ msgid ""
"* Planned Revenue by Stage and User (graph)\n"
"* Opportunities by Stage (graph)\n"
msgstr ""
"\n"
"Managementul general OpenERP al Relatiilor cu Clientii\n"
"=====================================================\n"
"\n"
"Aceasta aplicatie permite unui grup de persoane sa gestioneze in mod "
"inteligent si eficient piste, oportunitati, intalniri si apeluri "
"telefonice.\n"
"\n"
"Gestioneaza sarcini cheie precum comunicarea, identificarea, prioritizarea, "
"numirea, rezolutia si instiintarea.\n"
"\n"
"OpenERP se asigura ca toate cazurile sunt urmarite cu succes de catre "
"utilizatori, clienti si furnizori. Poate sa trimita automat memento-uri, sa "
"prioritizeze cererea, sa declanseze metode specifice si multe alte actiuni "
"pe baza regulilor companiei.\n"
"\n"
"Cel mai bun lucru legat de acest sistem este ca utilizatorii nu trebuie sa "
"faca nimic special. Modulul MRC are un email gateway pentru interfata de "
"sincronizare intre email-uri si OpenERP. In acest fel, utilizatorii pot pur "
"si simplu sa trimita email-uri catre programul care tine evidenta "
"cererilor.\n"
"\n"
"OpenERP le va multumi pentru mesaj, directionandu-l automat catre personalul "
"adecvat si se va asigura ca toata corespondenta viitoare sa ajunga in locul "
"potrivit.\n"
"\n"
"Panoul pentru MRC va include:\n"
"-------------------------------\n"
"* Venitul Planificat dupa Etapa si Utilizator (grafic)\n"
"* Oportunitati dupa Etapa (grafic)\n"
#. module: base
#: code:addons/base/ir/ir_model.py:397

View File

@ -7,14 +7,14 @@ msgstr ""
"Project-Id-Version: OpenERP Server 5.0.4\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2012-12-03 16:01+0000\n"
"PO-Revision-Date: 2012-12-01 11:25+0000\n"
"Last-Translator: Chertykov Denis <chertykov@gmail.com>\n"
"PO-Revision-Date: 2012-12-05 09:43+0000\n"
"Last-Translator: Denis Karataev <dskarataev@gmail.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-12-04 04:59+0000\n"
"X-Generator: Launchpad (build 16335)\n"
"X-Launchpad-Export-Date: 2012-12-06 04:38+0000\n"
"X-Generator: Launchpad (build 16341)\n"
#. module: base
#: model:ir.module.module,description:base.module_account_check_writing
@ -1638,7 +1638,7 @@ msgstr "Настройка нижнего колонтитула отчета"
#. module: base
#: field:ir.translation,comments:0
msgid "Translation comments"
msgstr ""
msgstr "Комментарии перевода"
#. module: base
#: model:ir.module.module,description:base.module_lunch
@ -5663,6 +5663,9 @@ msgid ""
" <p>You should try others search criteria.</p>\n"
" "
msgstr ""
"<p><b>Модули не найдены!</b></p>\n"
" <p>Попробуйте поискать по другим критериям.</p>\n"
" "
#. module: base
#: model:ir.model,name:base.model_ir_module_module
@ -7490,6 +7493,11 @@ msgid ""
"use the same timezone that is otherwise used to pick and render date and "
"time values: your computer's timezone."
msgstr ""
"Часовой пояс партнера, используемый для вывода правильной даты и времени "
"внутри распечатываемых отчетов. Важно установить значение этого поля. Вы "
"должны использовать тот же часовой пояс который есть, в противном случае "
"значения даты и времени будут вычисляться на основе часового пояса "
"установленного на компьютере."
#. module: base
#: model:ir.module.module,shortdesc:base.module_account_analytic_default
@ -8407,6 +8415,8 @@ msgid ""
"Translation features are unavailable until you install an extra OpenERP "
"translation."
msgstr ""
"Возможности перевода не доступны до тех пор, пока вы не установите "
"расширенный OpenERP перевод."
#. module: base
#: model:ir.module.module,description:base.module_l10n_nl

File diff suppressed because it is too large Load Diff

View File

@ -7,14 +7,14 @@ msgstr ""
"Project-Id-Version: OpenERP Server 5.0.4\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2012-12-03 16:01+0000\n"
"PO-Revision-Date: 2012-12-04 02:43+0000\n"
"PO-Revision-Date: 2012-12-06 18:17+0000\n"
"Last-Translator: ccdos <ccdos@163.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-12-04 05:03+0000\n"
"X-Generator: Launchpad (build 16335)\n"
"X-Launchpad-Export-Date: 2012-12-07 04:34+0000\n"
"X-Generator: Launchpad (build 16341)\n"
#. module: base
#: model:ir.module.module,description:base.module_account_check_writing
@ -88,7 +88,7 @@ msgstr "帮助您管理项目、跟踪任务、生成计划等"
#. module: base
#: model:ir.module.module,summary:base.module_point_of_sale
msgid "Touchscreen Interface for Shops"
msgstr "商店的触摸屏接口"
msgstr "触摸屏接口的POS 系统"
#. module: base
#: model:ir.module.module,shortdesc:base.module_l10n_in_hr_payroll
@ -896,6 +896,14 @@ msgid ""
" </p>\n"
" "
msgstr ""
"<p class=\"oe_view_nocontent_create\">\n"
" 点击此处在你的地址本中增加一个新的联系人。\n"
" </p><p>\n"
" Openerp可以帮助你轻松的跟踪所有与客户相关的活动\n"
" 例如讨论,商业机会的历史记录,\n"
" 文档,等等。\n"
" </p>\n"
" "
#. module: base
#: model:ir.module.module,description:base.module_web_linkedin
@ -2645,6 +2653,18 @@ msgid ""
" * Date\n"
" "
msgstr ""
"\n"
"为你的分析帐目设定缺省值。\n"
"==============================================\n"
"\n"
"基于以下规则时,允许自动选择分析帐目:\n"
"---------------------------------------------------------------------\n"
" * 产品\n"
" * 合作伙伴\n"
" * 用户\n"
" * 公司\n"
" * 日期\n"
" "
#. module: base
#: field:res.company,rml_header1:0
@ -4230,7 +4250,7 @@ msgstr "共享任意文档"
#. module: base
#: model:ir.module.module,summary:base.module_crm
msgid "Leads, Opportunities, Phone Calls"
msgstr "线索,上级,电话"
msgstr "线索,商机,电话"
#. module: base
#: view:res.lang:0
@ -7666,6 +7686,10 @@ msgid ""
"==========================================\n"
" "
msgstr ""
"\n"
"CRM 线索和商机的待办事项\n"
"==========================================\n"
" "
#. module: base
#: view:ir.mail_server:0
@ -9849,7 +9873,7 @@ msgstr "将要安装的模块"
#: model:ir.module.module,shortdesc:base.module_base
#: field:res.currency,base:0
msgid "Base"
msgstr "本位币"
msgstr "base 模块"
#. module: base
#: field:ir.model.data,model:0

View File

@ -22,17 +22,16 @@
import logging
import os
import re
import time
import tools
import netsvc
from osv import fields,osv
from report.report_sxw import report_sxw, report_rml
from tools.config import config
from tools.safe_eval import safe_eval as eval
from tools.translate import _
from socket import gethostname
import time
from openerp import SUPERUSER_ID
from openerp import netsvc, tools
from openerp.osv import fields, osv
from openerp.report.report_sxw import report_sxw, report_rml
from openerp.tools.config import config
from openerp.tools.safe_eval import safe_eval as eval
from openerp.tools.translate import _
_logger = logging.getLogger(__name__)
@ -42,7 +41,7 @@ class actions(osv.osv):
_order = 'name'
_columns = {
'name': fields.char('Name', size=64, required=True),
'type': fields.char('Action Type', required=True, size=32,readonly=True),
'type': fields.char('Action Type', required=True, size=32),
'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.',
@ -671,7 +670,7 @@ class actions_server(osv.osv):
context['object'] = obj
for i in expr:
context['active_id'] = i.id
result = self.run(cr, uid, [action.loop_action.id], context)
self.run(cr, uid, [action.loop_action.id], context)
if action.state == 'object_write':
res = {}
@ -716,8 +715,6 @@ class actions_server(osv.osv):
expr = exp.value
res[exp.col1.name] = expr
obj_pool = None
res_id = False
obj_pool = self.pool.get(action.srcmodel_id.model)
res_id = obj_pool.create(cr, uid, res)
if action.record_id:
@ -736,7 +733,7 @@ class actions_server(osv.osv):
model = action.copy_object.split(',')[0]
cid = action.copy_object.split(',')[1]
obj_pool = self.pool.get(model)
res_id = obj_pool.copy(cr, uid, int(cid), res)
obj_pool.copy(cr, uid, int(cid), res)
return False

View File

@ -502,6 +502,19 @@
<menuitem id="menu_ir_actions_todo" name="Configuration Wizards" parent="menu_custom" sequence="20" groups="base.group_no_one"/>
<menuitem id="menu_ir_actions_todo_form" action="act_ir_actions_todo_form" parent="menu_ir_actions_todo"/>
<record id="action_run_ir_action_todo" model="ir.actions.server">
<field name="name">Run Remaining Action Todo</field>
<field name="condition">True</field>
<field name="type">ir.actions.server</field>
<field name="model_id" ref="model_res_config"/>
<field name="state">code</field>
<field name="code">
config = self.next(cr, uid, [], context=context) or {}
if config.get('type') not in ('ir.actions.act_window_close',):
action = config
</field>
</record>
</data>
</openerp>

View File

@ -18,12 +18,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import itertools
from osv import fields,osv
from osv.orm import except_orm
import tools
from openerp.osv import fields,osv
class ir_attachment(osv.osv):
def check(self, cr, uid, ids, mode, context=None, values=None):

View File

@ -22,11 +22,12 @@
Store database-specific configuration parameters
"""
from osv import osv,fields
import uuid
import datetime
from tools import misc, config
from openerp import SUPERUSER_ID
from openerp.osv import osv, fields
from openerp.tools import misc, config
"""
A dictionary holding some configuration parameters to be initialized when the database is created.

View File

@ -18,24 +18,18 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import calendar
import time
import logging
import threading
import psycopg2
from datetime import datetime
from dateutil.relativedelta import relativedelta
import netsvc
import openerp
import pooler
import tools
from openerp.cron import WAKE_UP_NOW
from osv import fields, osv
from tools import DEFAULT_SERVER_DATETIME_FORMAT
from tools.safe_eval import safe_eval as eval
from tools.translate import _
from openerp import netsvc
from openerp.osv import fields, osv
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
from openerp.tools.safe_eval import safe_eval as eval
from openerp.tools.translate import _
_logger = logging.getLogger(__name__)
@ -142,130 +136,6 @@ class ir_cron(osv.osv):
except Exception, e:
self._handle_callback_exception(cr, uid, model_name, method_name, args, job_id, e)
def _run_job(self, cr, job, now):
""" Run a given job taking care of the repetition.
The cursor has a lock on the job (aquired by _run_jobs_multithread()) and this
method is run in a worker thread (spawned by _run_jobs_multithread())).
:param job: job to be run (as a dictionary).
:param now: timestamp (result of datetime.now(), no need to call it multiple time).
"""
try:
nextcall = datetime.strptime(job['nextcall'], DEFAULT_SERVER_DATETIME_FORMAT)
numbercall = job['numbercall']
ok = False
while nextcall < now and numbercall:
if numbercall > 0:
numbercall -= 1
if not ok or job['doall']:
self._callback(cr, job['user_id'], job['model'], job['function'], job['args'], job['id'])
if numbercall:
nextcall += _intervalTypes[job['interval_type']](job['interval_number'])
ok = True
addsql = ''
if not numbercall:
addsql = ', active=False'
cr.execute("UPDATE ir_cron SET nextcall=%s, numbercall=%s"+addsql+" WHERE id=%s",
(nextcall.strftime(DEFAULT_SERVER_DATETIME_FORMAT), numbercall, job['id']))
if numbercall:
# Reschedule our own main cron thread if necessary.
# This is really needed if this job runs longer than its rescheduling period.
nextcall = calendar.timegm(nextcall.timetuple())
openerp.cron.schedule_wakeup(nextcall, cr.dbname)
finally:
cr.commit()
cr.close()
openerp.cron.release_thread_slot()
def _run_jobs_multithread(self):
# TODO remove 'check' argument from addons/base_action_rule/base_action_rule.py
""" Process the cron jobs by spawning worker threads.
This selects in database all the jobs that should be processed. It then
tries to lock each of them and, if it succeeds, spawns a thread to run
the cron job (if it doesn't succeed, it means the job was already
locked to be taken care of by another thread).
The cursor used to lock the job in database is given to the worker
thread (which has to close it itself).
"""
db = self.pool.db
cr = db.cursor()
db_name = db.dbname
try:
jobs = {} # mapping job ids to jobs for all jobs being processed.
now = datetime.now()
# Careful to compare timestamps with 'UTC' - everything is UTC as of v6.1.
cr.execute("""SELECT * FROM ir_cron
WHERE numbercall != 0
AND active AND nextcall <= (now() at time zone 'UTC')
ORDER BY priority""")
for job in cr.dictfetchall():
if not openerp.cron.get_thread_slots():
break
jobs[job['id']] = job
task_cr = db.cursor()
try:
# Try to grab an exclusive lock on the job row from within the task transaction
acquired_lock = False
task_cr.execute("""SELECT *
FROM ir_cron
WHERE id=%s
FOR UPDATE NOWAIT""",
(job['id'],), log_exceptions=False)
acquired_lock = True
except psycopg2.OperationalError, e:
if e.pgcode == '55P03':
# Class 55: Object not in prerequisite state; 55P03: lock_not_available
_logger.debug('Another process/thread is already busy executing job `%s`, skipping it.', job['name'])
continue
else:
# Unexpected OperationalError
raise
finally:
if not acquired_lock:
# we're exiting due to an exception while acquiring the lot
task_cr.close()
# Got the lock on the job row, now spawn a thread to execute it in the transaction with the lock
task_thread = threading.Thread(target=self._run_job, name=job['name'], args=(task_cr, job, now))
# force non-daemon task threads (the runner thread must be daemon, and this property is inherited by default)
task_thread.setDaemon(False)
openerp.cron.take_thread_slot()
task_thread.start()
_logger.debug('Cron execution thread for job `%s` spawned', job['name'])
# Find next earliest job ignoring currently processed jobs (by this and other cron threads)
find_next_time_query = """SELECT min(nextcall) AS min_next_call
FROM ir_cron WHERE numbercall != 0 AND active"""
if jobs:
cr.execute(find_next_time_query + " AND id NOT IN %s", (tuple(jobs.keys()),))
else:
cr.execute(find_next_time_query)
next_call = cr.dictfetchone()['min_next_call']
if next_call:
next_call = calendar.timegm(time.strptime(next_call, DEFAULT_SERVER_DATETIME_FORMAT))
else:
# no matching cron job found in database, re-schedule arbitrarily in 1 day,
# this delay will likely be modified when running jobs complete their tasks
next_call = time.time() + (24*3600)
openerp.cron.schedule_wakeup(next_call, db_name)
except Exception, ex:
_logger.warning('Exception in cron:', exc_info=True)
finally:
cr.commit()
cr.close()
def _process_job(self, cr, job):
""" Run a given job taking care of the repetition.
@ -356,7 +226,7 @@ class ir_cron(osv.osv):
_logger.warning('Tried to poll an undefined table on database %s.', db_name)
else:
raise
except Exception, ex:
except Exception:
_logger.warning('Exception in cron:', exc_info=True)
finally:
@ -365,19 +235,6 @@ class ir_cron(osv.osv):
return False
def update_running_cron(self, cr):
""" Schedule as soon as possible a wake-up for this database. """
# Verify whether the server is already started and thus whether we need to commit
# immediately our changes and restart the cron agent in order to apply the change
# immediately. The commit() is needed because as soon as the cron is (re)started it
# will query the database with its own cursor, possibly before the end of the
# current transaction.
# This commit() is not an issue in most cases, but we must absolutely avoid it
# when the server is only starting or loading modules (hence the test on pool._init).
if not self.pool._init:
cr.commit()
openerp.cron.schedule_wakeup(WAKE_UP_NOW, self.pool.db.dbname)
def _try_lock(self, cr, uid, ids, context=None):
"""Try to grab a dummy exclusive write-lock to the rows with the given ids,
to make sure a following write() or unlink() will not block due
@ -393,20 +250,16 @@ class ir_cron(osv.osv):
def create(self, cr, uid, vals, context=None):
res = super(ir_cron, self).create(cr, uid, vals, context=context)
self.update_running_cron(cr)
return res
def write(self, cr, uid, ids, vals, context=None):
self._try_lock(cr, uid, ids, context)
res = super(ir_cron, self).write(cr, uid, ids, vals, context=context)
self.update_running_cron(cr)
return res
def unlink(self, cr, uid, ids, context=None):
self._try_lock(cr, uid, ids, context)
res = super(ir_cron, self).unlink(cr, uid, ids, context=context)
self.update_running_cron(cr)
return res
ir_cron()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -19,7 +19,7 @@
#
##############################################################################
from osv import fields,osv
from openerp.osv import fields, osv
class ir_default(osv.osv):
_name = 'ir.default'

View File

@ -19,7 +19,7 @@
#
##############################################################################
from osv import fields,osv
from openerp.osv import fields,osv
class ir_exports(osv.osv):

View File

@ -1,5 +1,4 @@
# -*- coding: utf-8 -*-
import collections
import datetime
import functools
import operator

View File

@ -20,13 +20,10 @@
##############################################################################
from openerp import exceptions
from osv import osv, fields
from tools.translate import _
from openerp.osv import osv, fields
from openerp.tools.translate import _
class ir_filters(osv.osv):
'''
Filters
'''
_name = 'ir.filters'
_description = 'Filters'

View File

@ -31,8 +31,7 @@ import re
import smtplib
import threading
from osv import osv
from osv import fields
from openerp.osv import osv, fields
from openerp.tools.translate import _
from openerp.tools import html2text
import openerp.tools as tools
@ -228,7 +227,7 @@ class ir_mail_server(osv.osv):
:param int port: SMTP port to connect to
:param user: optional username to authenticate with
:param password: optional password to authenticate with
:param string encryption: optional: ``'ssl'`` | ``'starttls'``
:param string encryption: optional, ``'ssl'`` | ``'starttls'``
:param bool smtp_debug: toggle debugging of SMTP sessions (all i/o
will be output in logs)
"""

View File

@ -158,9 +158,10 @@ class ir_model(osv.osv):
if context is None: context = {}
if isinstance(ids, (int, long)):
ids = [ids]
if not context.get(MODULE_UNINSTALL_FLAG) and \
any(model.state != 'manual' for model in self.browse(cr, user, ids, context)):
raise except_orm(_('Error'), _("Model '%s' contains module data and cannot be removed!") % (model.name,))
if not context.get(MODULE_UNINSTALL_FLAG):
for model in self.browse(cr, user, ids, context):
if model.state != 'manual':
raise except_orm(_('Error'), _("Model '%s' contains module data and cannot be removed!") % (model.name,))
self._drop_table(cr, user, ids, context)
res = super(ir_model, self).unlink(cr, user, ids, context)
@ -256,7 +257,7 @@ class ir_model_fields(osv.osv):
'selection': "",
'domain': "[]",
'name': 'x_',
'state': lambda self,cr,uid,ctx={}: (ctx and ctx.get('manual',False)) and 'manual' or 'base',
'state': lambda self,cr,uid,ctx=None: (ctx and ctx.get('manual',False)) and 'manual' or 'base',
'on_delete': 'set null',
'select_level': '0',
'size': 64,
@ -271,7 +272,7 @@ class ir_model_fields(osv.osv):
except Exception:
_logger.warning('Invalid selection list definition for fields.selection', exc_info=True)
raise except_orm(_('Error'),
_("The Selection Options expression is not a valid Pythonic expression." \
_("The Selection Options expression is not a valid Pythonic expression."
"Please provide an expression in the [('key','Label'), ...] format."))
check = True
@ -514,7 +515,7 @@ class ir_model_constraint(Model):
# double-check we are really going to delete all the owners of this schema element
cr.execute("""SELECT id from ir_model_constraint where name=%s""", (data.name,))
external_ids = [x[0] for x in cr.fetchall()]
if (set(external_ids)-ids_set):
if set(external_ids)-ids_set:
# as installed modules have defined this element we must not delete it!
continue
@ -567,13 +568,12 @@ class ir_model_relation(Model):
ids.reverse()
for data in self.browse(cr, uid, ids, context):
model = data.model
model_obj = self.pool.get(model)
name = openerp.tools.ustr(data.name)
# double-check we are really going to delete all the owners of this schema element
cr.execute("""SELECT id from ir_model_relation where name = %s""", (data.name,))
external_ids = [x[0] for x in cr.fetchall()]
if (set(external_ids)-ids_set):
if set(external_ids)-ids_set:
# as installed modules have defined this element we must not delete it!
continue
@ -585,7 +585,7 @@ class ir_model_relation(Model):
# drop m2m relation tables
for table in to_drop_table:
cr.execute('DROP TABLE %s CASCADE'% (table),)
cr.execute('DROP TABLE %s CASCADE'% table,)
_logger.info('Dropped table %s', table)
cr.commit()
@ -862,7 +862,7 @@ class ir_model_data(osv.osv):
res = self.read(cr, uid, data_id, ['model', 'res_id'])
if not res['res_id']:
raise ValueError('No such external ID currently defined in the system: %s.%s' % (module, xml_id))
return (res['model'], res['res_id'])
return res['model'], res['res_id']
def get_object(self, cr, uid, module, xml_id, context=None):
"""Returns a browsable record for the given module name and xml_id or raise ValueError if not found"""
@ -903,7 +903,7 @@ class ir_model_data(osv.osv):
# records created during module install should not display the messages of OpenChatter
context = dict(context, install_mode=True)
if xml_id and ('.' in xml_id):
assert len(xml_id.split('.'))==2, _("'%s' contains too many dots. XML ids should not contain dots ! These are used to refer to other modules data, as in module.reference_id") % (xml_id)
assert len(xml_id.split('.'))==2, _("'%s' contains too many dots. XML ids should not contain dots ! These are used to refer to other modules data, as in module.reference_id") % xml_id
module, xml_id = xml_id.split('.')
if (not xml_id) and (not self.doinit):
return False
@ -1073,7 +1073,6 @@ class ir_model_data(osv.osv):
if model == 'ir.model.fields')
ir_model_relation = self.pool.get('ir.model.relation')
relation_ids = ir_model_relation.search(cr, uid, [('module', 'in', modules_to_remove)])
ir_module_module = self.pool.get('ir.module.module')
modules_to_remove_ids = ir_module_module.search(cr, uid, [('name', 'in', modules_to_remove)])
relation_ids = ir_model_relation.search(cr, uid, [('module', 'in', modules_to_remove_ids)])

View File

@ -246,8 +246,8 @@
<tree string="External Identifiers">
<field name="complete_name"/>
<field name="display_name"/>
<field name="res_id"/>
<field name="model" groups="base.group_no_one"/>
<field name="res_id"/>
</tree>
</field>
</record>
@ -255,12 +255,10 @@
<field name="model">ir.model.data</field>
<field name="arch" type="xml">
<search string="External Identifiers">
<field name="name"
filter_domain="['|', '|', ('name','ilike',self), ('model','ilike',self), ('module','ilike',self)]"
string="External Identifier"/>
<filter icon="terp-camera_test"
string="Updatable"
domain="[('noupdate', '=', False)]"/>
<field name="name" filter_domain="[('name','ilike',self)]" string="External Identifier"/>
<filter string="Updatable" domain="[('noupdate', '=', False)]"/>
<field name="module"/>
<field name="model"/>
<field name="res_id"/>
<field name="noupdate"/>
<group expand="0" string="Group By...">

View File

@ -19,10 +19,11 @@
#
##############################################################################
from osv import osv
from openerp.osv import osv
class ir_needaction_mixin(osv.AbstractModel):
'''Mixin class for objects using the need action feature.
"""Mixin class for objects using the need action feature.
Need action feature can be used by models that have to be able to
signal that an action is required on a particular record. If in
@ -36,7 +37,7 @@ class ir_needaction_mixin(osv.AbstractModel):
This class also offers several global services:
- ``_needaction_count``: returns the number of actions uid has to perform
'''
"""
_name = 'ir.needaction_mixin'
_needaction = True
@ -55,9 +56,10 @@ class ir_needaction_mixin(osv.AbstractModel):
# "Need action" API
#------------------------------------------------------
def _needaction_count(self, cr, uid, domain=[], context=None):
def _needaction_count(self, cr, uid, domain=None, context=None):
""" Get the number of actions uid has to perform. """
dom = self._needaction_domain_get(cr, uid, context=context)
if not dom:
return 0
return self.search(cr, uid, (domain or []) +dom, context=context, count=True)
res = self.search(cr, uid, (domain or []) + dom, limit=100, order='id DESC', context=context)
return len(res)

View File

@ -18,15 +18,13 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from osv import fields, osv, expression
import time
from operator import itemgetter
from functools import partial
import tools
from tools.safe_eval import safe_eval as eval
from tools.misc import unquote as unquote
from openerp import SUPERUSER_ID
from openerp import tools
from openerp.osv import fields, osv, expression
from openerp.tools.safe_eval import safe_eval as eval
from openerp.tools.misc import unquote as unquote
class ir_rule(osv.osv):
_name = 'ir.rule'
@ -52,7 +50,7 @@ class ir_rule(osv.osv):
eval_context = self._eval_context(cr, uid)
for rule in self.browse(cr, uid, ids, context):
if rule.domain_force:
res[rule.id] = expression.normalize(eval(rule.domain_force, eval_context))
res[rule.id] = expression.normalize_domain(eval(rule.domain_force, eval_context))
else:
res[rule.id] = []
return res
@ -130,7 +128,7 @@ class ir_rule(osv.osv):
for rule in self.browse(cr, SUPERUSER_ID, rule_ids):
# read 'domain' as UID to have the correct eval context for the rule.
rule_domain = self.read(cr, uid, rule.id, ['domain'])['domain']
dom = expression.normalize(rule_domain)
dom = expression.normalize_domain(rule_domain)
for group in rule.groups:
if group in user.groups_id:
group_domains.setdefault(group, []).append(dom)

View File

@ -22,10 +22,9 @@
import logging
import time
from osv import osv, fields
from tools.translate import _
import openerp
from openerp.osv import osv
from openerp.tools.translate import _
_logger = logging.getLogger(__name__)
@ -140,7 +139,7 @@ class ir_sequence(openerp.osv.osv.osv):
values = self._add_missing_default_values(cr, uid, values, context)
values['id'] = super(ir_sequence, self).create(cr, uid, values, context)
if values['implementation'] == 'standard':
f = self._create_sequence(cr, values['id'], values['number_increment'], values['number_next'])
self._create_sequence(cr, values['id'], values['number_increment'], values['number_next'])
return values['id']
def unlink(self, cr, uid, ids, context=None):

View File

@ -19,12 +19,12 @@
#
##############################################################################
import tools
import logging
from openerp import tools
import openerp.modules
from openerp.osv import fields, osv
from tools.translate import _
from openerp.tools.translate import _
_logger = logging.getLogger(__name__)
@ -134,7 +134,7 @@ class ir_translation_import_cursor(object):
""" % (self._parent_table, self._table_name, self._parent_table, find_expr))
if self._debug:
cr.execute('SELECT COUNT(*) FROM ONLY %s' % (self._parent_table))
cr.execute('SELECT COUNT(*) FROM ONLY %s' % self._parent_table)
c1 = cr.fetchone()[0]
cr.execute('SELECT COUNT(*) FROM ONLY %s AS irt, %s AS ti WHERE %s' % \
(self._parent_table, self._table_name, find_expr))
@ -217,11 +217,11 @@ class ir_translation(osv.osv):
def _get_ids(self, cr, uid, name, tt, lang, ids):
translations = dict.fromkeys(ids, False)
if ids:
cr.execute('select res_id,value ' \
'from ir_translation ' \
'where lang=%s ' \
'and type=%s ' \
'and name=%s ' \
cr.execute('select res_id,value '
'from ir_translation '
'where lang=%s '
'and type=%s '
'and name=%s '
'and res_id IN %s',
(lang,tt,name,tuple(ids)))
for res_id, value in cr.fetchall():
@ -237,10 +237,10 @@ class ir_translation(osv.osv):
self._get_ids.clear_cache(self, uid, name, tt, lang, res_id)
self._get_source.clear_cache(self, uid, name, tt, lang)
cr.execute('delete from ir_translation ' \
'where lang=%s ' \
'and type=%s ' \
'and name=%s ' \
cr.execute('delete from ir_translation '
'where lang=%s '
'and type=%s '
'and name=%s '
'and res_id IN %s',
(lang,tt,name,tuple(ids),))
for id in ids:

View File

@ -23,11 +23,11 @@
import base64
import re
import threading
from tools.safe_eval import safe_eval as eval
import tools
from openerp.tools.safe_eval import safe_eval as eval
from openerp import tools
import openerp.modules
from osv import fields, osv
from tools.translate import _
from openerp.osv import fields, osv
from openerp.tools.translate import _
from openerp import SUPERUSER_ID
def one_in(setA, setB):
@ -43,14 +43,17 @@ class ir_ui_menu(osv.osv):
def __init__(self, *args, **kwargs):
self.cache_lock = threading.RLock()
self.clear_cache()
r = super(ir_ui_menu, self).__init__(*args, **kwargs)
self._cache = {}
super(ir_ui_menu, self).__init__(*args, **kwargs)
self.pool.get('ir.model.access').register_cache_clearing_method(self._name, 'clear_cache')
return r
def clear_cache(self):
with self.cache_lock:
# radical but this doesn't frequently happen
if self._cache:
# Normally this is done by openerp.tools.ormcache
# but since we do not use it, set it by ourself.
self.pool._any_cache_cleared = True
self._cache = {}
def _filter_visible_menus(self, cr, uid, ids, context=None):
@ -62,7 +65,7 @@ class ir_ui_menu(osv.osv):
modelaccess = self.pool.get('ir.model.access')
user_groups = set(self.pool.get('res.users').read(cr, SUPERUSER_ID, uid, ['groups_id'])['groups_id'])
result = []
for menu in self.browse(cr, SUPERUSER_ID, ids, context=context):
for menu in self.browse(cr, uid, ids, context=context):
# this key works because user access rights are all based on user's groups (cfr ir_model_access.check)
key = (cr.dbname, menu.id, tuple(user_groups))
if key in self._cache:
@ -140,7 +143,7 @@ class ir_ui_menu(osv.osv):
return res
def _get_full_name(self, cr, uid, ids, name=None, args=None, context=None):
if context == None:
if context is None:
context = {}
res = {}
for elmt in self.browse(cr, uid, ids, context=context):
@ -164,9 +167,22 @@ class ir_ui_menu(osv.osv):
self.clear_cache()
return super(ir_ui_menu, self).write(*args, **kwargs)
def unlink(self, *args, **kwargs):
def unlink(self, cr, uid, ids, context=None):
# Detach children and promote them to top-level, because it would be unwise to
# cascade-delete submenus blindly. We also can't use ondelete=set null because
# that is not supported when _parent_store is used (would silently corrupt it).
# TODO: ideally we should move them under a generic "Orphans" menu somewhere?
if isinstance(ids, (int, long)):
ids = [ids]
local_context = dict(context or {})
local_context['ir.ui.menu.full_list'] = True
direct_children_ids = self.search(cr, uid, [('parent_id', 'in', ids)], context=local_context)
if direct_children_ids:
self.write(cr, uid, direct_children_ids, {'parent_id': False})
result = super(ir_ui_menu, self).unlink(cr, uid, ids, context=context)
self.clear_cache()
return super(ir_ui_menu, self).unlink(*args, **kwargs)
return result
def copy(self, cr, uid, id, default=None, context=None):
ir_values_obj = self.pool.get('ir.values')
@ -178,7 +194,7 @@ class ir_ui_menu(osv.osv):
next_num=int(concat[0])+1
datas['name']=rex.sub(('(%d)'%next_num),datas['name'])
else:
datas['name']=datas['name']+'(1)'
datas['name'] += '(1)'
self.write(cr,uid,[res],{'name':datas['name']})
ids = ir_values_obj.search(cr, uid, [
('model', '=', 'ir.ui.menu'),
@ -265,17 +281,33 @@ class ir_ui_menu(osv.osv):
return res
def _get_needaction(self, cr, uid, ids, field_names, args, context=None):
def _get_needaction_enabled(self, cr, uid, ids, field_names, args, context=None):
""" needaction_enabled: tell whether the menu has a related action
that uses the needaction mechanism. """
res = dict.fromkeys(ids, False)
for menu in self.browse(cr, uid, ids, context=context):
if menu.action and menu.action.type in ('ir.actions.act_window', 'ir.actions.client') and menu.action.res_model:
obj = self.pool.get(menu.action.res_model)
if obj and obj._needaction:
res[menu.id] = True
return res
def get_needaction_data(self, cr, uid, ids, context=None):
""" Return for each menu entry of ids :
- if it uses the needaction mechanism (needaction_enabled)
- the needaction counter of the related action, taking into account
the action domain
"""
res = {}
for menu in self.browse(cr, uid, ids, context=context):
res[menu.id] = {
'needaction_enabled': False,
'needaction_counter': False,
}
if menu.action and menu.action.type in ('ir.actions.act_window','ir.actions.client') and menu.action.res_model:
if menu.action and menu.action.type in ('ir.actions.act_window', 'ir.actions.client') and menu.action.res_model:
obj = self.pool.get(menu.action.res_model)
if obj and obj._needaction:
if menu.action.type=='ir.actions.act_window':
if menu.action.type == 'ir.actions.act_window':
dom = menu.action.domain and eval(menu.action.domain, {'uid': uid}) or []
else:
dom = eval(menu.action.params_store or '{}', {'uid': uid}).get('domain')
@ -286,8 +318,10 @@ class ir_ui_menu(osv.osv):
_columns = {
'name': fields.char('Menu', size=64, required=True, translate=True),
'sequence': fields.integer('Sequence'),
'child_id' : fields.one2many('ir.ui.menu', 'parent_id','Child IDs'),
'parent_id': fields.many2one('ir.ui.menu', 'Parent Menu', select=True),
'child_id': fields.one2many('ir.ui.menu', 'parent_id', 'Child IDs'),
'parent_id': fields.many2one('ir.ui.menu', 'Parent Menu', select=True, ondelete="restrict"),
'parent_left': fields.integer('Parent Left', select=True),
'parent_right': fields.integer('Parent Right', select=True),
'groups_id': fields.many2many('res.groups', 'ir_ui_menu_group_rel',
'menu_id', 'gid', 'Groups', help="If you have groups, the visibility of this menu will be based on these groups. "\
"If this field is empty, OpenERP will compute visibility based on the related object's read access."),
@ -296,11 +330,14 @@ class ir_ui_menu(osv.osv):
'icon': fields.selection(tools.icons, 'Icon', size=64),
'icon_pict': fields.function(_get_icon_pict, type='char', size=32),
'web_icon': fields.char('Web Icon File', size=128),
'web_icon_hover':fields.char('Web Icon File (hover)', size=128),
'web_icon_hover': fields.char('Web Icon File (hover)', size=128),
'web_icon_data': fields.function(_get_image_icon, string='Web Icon Image', type='binary', readonly=True, store=True, multi='icon'),
'web_icon_hover_data':fields.function(_get_image_icon, string='Web Icon Image (hover)', type='binary', readonly=True, store=True, multi='icon'),
'needaction_enabled': fields.function(_get_needaction, string='Target model uses the need action mechanism', type='boolean', help='If the menu entry action is an act_window action, and if this action is related to a model that uses the need_action mechanism, this field is set to true. Otherwise, it is false.', multi='_get_needaction'),
'needaction_counter': fields.function(_get_needaction, string='Number of actions the user has to perform', type='integer', help='If the target model uses the need action mechanism, this field gives the number of actions the current user has to perform.', multi='_get_needaction'),
'web_icon_hover_data': fields.function(_get_image_icon, string='Web Icon Image (hover)', type='binary', readonly=True, store=True, multi='icon'),
'needaction_enabled': fields.function(_get_needaction_enabled,
type='boolean',
store=True,
string='Target model uses the need action mechanism',
help='If the menu entry action is an act_window action, and if this action is related to a model that uses the need_action mechanism, this field is set to true. Otherwise, it is false.'),
'action': fields.function(_action, fnct_inv=_action_inv,
type='reference', string='Action',
selection=[
@ -317,13 +354,14 @@ class ir_ui_menu(osv.osv):
return _('Error ! You can not create recursive Menu.')
_constraints = [
(osv.osv._check_recursion, _rec_message , ['parent_id'])
(osv.osv._check_recursion, _rec_message, ['parent_id'])
]
_defaults = {
'icon' : 'STOCK_OPEN',
'icon_pict': ('stock', ('STOCK_OPEN','ICON_SIZE_MENU')),
'sequence' : 10,
'icon': 'STOCK_OPEN',
'icon_pict': ('stock', ('STOCK_OPEN', 'ICON_SIZE_MENU')),
'sequence': 10,
}
_order = "sequence,id"
_parent_store = True
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -19,14 +19,15 @@
#
##############################################################################
from osv import fields,osv
from lxml import etree
from tools import graph
from tools.safe_eval import safe_eval as eval
import tools
from tools.view_validation import valid_view
import os
import logging
from lxml import etree
import os
from openerp import tools
from openerp.osv import fields,osv
from openerp.tools import graph
from openerp.tools.safe_eval import safe_eval as eval
from openerp.tools.view_validation import valid_view
_logger = logging.getLogger(__name__)
@ -254,7 +255,7 @@ class view(osv.osv):
if label:
for lbl in eval(label):
if t.has_key(tools.ustr(lbl)) and tools.ustr(t[lbl])=='False':
label_string = label_string + ' '
label_string += ' '
else:
label_string = label_string + " " + tools.ustr(t[lbl])
labels[str(t['id'])] = (a['id'],label_string)

View File

@ -50,15 +50,11 @@
<field name="arch" type="xml">
<search string="Views">
<field name="name" filter_domain="['|', ('name','ilike',self), ('model','ilike',self)]" string="View"/>
<filter icon="terp-stock_zoom"
string="Search"
domain="[('type', '=', 'search')]"/>
<filter icon="gtk-indent"
string="Tree"
domain="[('type', '=', 'tree')]"/>
<filter icon="gtk-new"
string="Form"
domain="[('type', '=','form')]"/>
<filter string="Form" domain="[('type', '=','form')]"/>
<filter string="Tree" domain="[('type', '=', 'tree')]"/>
<filter string="Kanban" domain="[('type', '=', 'kanban')]"/>
<filter string="Search" domain="[('type', '=', 'search')]"/>
<field name="model"/>
<field name="inherit_id"/>
<field name="type"/>
<group expand="0" string="Group By...">

View File

@ -18,11 +18,10 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from osv import osv,fields
from osv.orm import except_orm
import pickle
from tools.translate import _
from openerp.osv import osv, fields
from openerp.osv.orm import except_orm
EXCLUDED_FIELDS = set((
'report_sxw_content', 'report_rml_content', 'report_sxw', 'report_rml',
@ -307,10 +306,10 @@ class ir_values(osv.osv):
ORDER BY v.user_id, u.company_id"""
params = ('default', model, uid, uid)
if condition:
query = query % 'AND v.key2 = %s'
query %= 'AND v.key2 = %s'
params += (condition[:200],)
else:
query = query % 'AND v.key2 is NULL'
query %= 'AND v.key2 is NULL'
cr.execute(query, params)
# keep only the highest priority default for each field
@ -417,7 +416,7 @@ class ir_values(osv.osv):
continue
# keep only the first action registered for each action name
results[action['name']] = (action['id'], action['name'], action_def)
except except_orm, e:
except except_orm:
continue
return sorted(results.values())

View File

@ -18,7 +18,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from osv import fields,osv
from openerp.osv import fields, osv
class wizard_model_menu(osv.osv_memory):
_name = 'wizard.ir.model.menu.create'

View File

@ -18,13 +18,11 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import logging
import time, os
import netsvc
import report,pooler,tools
from operator import itemgetter
import os
from openerp import report, tools
_logger = logging.getLogger(__name__)
@ -77,8 +75,10 @@ def graph_get(cr, graph, wkf_ids, nested, workitem, processed_subflows):
for t in transitions:
if not t['act_to'] in activities:
continue
args = {}
args['label'] = str(t['condition']).replace(' or ', '\\nor ').replace(' and ', '\\nand ')
args = {
'label': str(t['condition']).replace(' or ', '\\nor ')
.replace(' and ','\\nand ')
}
if t['signal']:
args['label'] += '\\n'+str(t['signal'])
args['style'] = 'bold'
@ -94,20 +94,19 @@ def graph_get(cr, graph, wkf_ids, nested, workitem, processed_subflows):
activity_from = actfrom[t['act_from']][1].get(t['signal'], actfrom[t['act_from']][0])
activity_to = actto[t['act_to']][1].get(t['signal'], actto[t['act_to']][0])
graph.add_edge(pydot.Edge( str(activity_from) ,str(activity_to), fontsize='10', **args))
nodes = cr.dictfetchall()
cr.execute('select * from wkf_activity where flow_start=True and wkf_id in ('+','.join(['%s']*len(wkf_ids))+')', wkf_ids)
start = cr.fetchone()[0]
cr.execute("select 'subflow.'||name,id from wkf_activity where flow_stop=True and wkf_id in ("+','.join(['%s']*len(wkf_ids))+')', wkf_ids)
stop = cr.fetchall()
if (stop):
if stop:
stop = (stop[0][1], dict(stop))
else:
stop = ("stop",{})
return ((start,{}),stop)
return (start, {}), stop
def graph_instance_get(cr, graph, inst_id, nested=False):
workitems = {}
cr.execute('select wkf_id from wkf_instance where id=%s', (inst_id,))
inst = cr.fetchall()
@ -169,7 +168,7 @@ showpage'''
inst_id = inst_id[0]
graph_instance_get(cr, graph, inst_id, data.get('nested', False))
ps_string = graph.create(prog='dot', format='ps')
except Exception, e:
except Exception:
_logger.exception('Exception in call:')
# string is in PS, like the success message would have been
ps_string = '''%PS-Adobe-3.0
@ -206,13 +205,13 @@ class report_graph(report.interface.report_int):
def result(self):
if self.obj.is_done():
return (True, self.obj.get(), 'pdf')
return True, self.obj.get(), 'pdf'
else:
return (False, False, False)
return False, False, False
def create(self, cr, uid, ids, data, context=None):
self.obj = report_graph_instance(cr, uid, ids, data)
return (self.obj.get(), 'pdf')
return self.obj.get(), 'pdf'
report_graph('report.workflow.instance.graph', 'ir.workflow')

View File

@ -18,9 +18,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import base64
from docutils import io, nodes
from docutils import nodes
from docutils.core import publish_string
from docutils.transforms import Transform, writer_aux
from docutils.writers.html4css1 import Writer
@ -30,6 +28,7 @@ import re
import urllib
import zipimport
import openerp
from openerp import modules, pooler, release, tools, addons
from openerp.modules.db import create_categories
from openerp.tools.parse_version import parse_version
@ -369,22 +368,28 @@ class module(osv.osv):
# Mark the given modules to be installed.
self.state_update(cr, uid, ids, 'to install', ['uninstalled'], context)
# Mark (recursively) the newly satisfied modules to also be installed:
# Mark (recursively) the newly satisfied modules to also be installed
# Select all auto-installable (but not yet installed) modules.
domain = [('state', '=', 'uninstalled'), ('auto_install', '=', True),]
domain = [('state', '=', 'uninstalled'), ('auto_install', '=', True)]
uninstalled_ids = self.search(cr, uid, domain, context=context)
uninstalled_modules = self.browse(cr, uid, uninstalled_ids, context=context)
# Keep those with all their dependencies satisfied.
# Keep those with:
# - all dependencies satisfied (installed or to be installed),
# - at least one dependency being 'to install'
satisfied_states = frozenset(('installed', 'to install', 'to upgrade'))
def all_depencies_satisfied(m):
return all(x.state in ('to install', 'installed', 'to upgrade') for x in m.dependencies_id)
states = set(d.state for d in m.dependencies_id)
return states.issubset(satisfied_states) and ('to install' in states)
to_install_modules = filter(all_depencies_satisfied, uninstalled_modules)
to_install_ids = map(lambda m: m.id, to_install_modules)
# Mark them to be installed.
if to_install_ids:
self.button_install(cr, uid, to_install_ids, context=context)
openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
return dict(ACTION_DICT, name=_('Install'))
def button_immediate_install(self, cr, uid, ids, context=None):

View File

@ -3,16 +3,16 @@
<template pageSize="(595.0,842.0)" title="Test" author="Martin Simon" allowSplitting="20">
<pageTemplate id="first">
<frame id="first" x1="42.0" y1="42.0" width="511" height="758"/>
<header>
<pageGraphics>
<setFont name="Helvetica-Bold" size="9"/>
<drawString x="1.0cm" y="28.1cm">[[ company.name ]]</drawString>
<drawRightString x="20cm" y="28.1cm"> Reference Guide </drawRightString>
<lineMode width="0.7"/>
<stroke color="black"/>
<lines>1cm 28cm 20cm 28cm</lines>
</pageGraphics>
</header>
<header>
<pageGraphics>
<setFont name="Helvetica-Bold" size="9"/>
<drawString x="1.0cm" y="28.1cm">[[ company.name ]]</drawString>
<drawRightString x="20cm" y="28.1cm"> Reference Guide </drawRightString>
<lineMode width="0.7"/>
<stroke color="black"/>
<lines>1cm 28cm 20cm 28cm</lines>
</pageGraphics>
</header>
</pageTemplate>
</template>
@ -236,7 +236,7 @@
<tr>
<td>
<para style="terp_default_9">[[ repeatIn(objdoc2(object.model) or [], 'sline') ]]</para>
<para style="terp_default_9"> [[ sline ]] </para>
<para style="terp_default_9"> [[ sline ]] </para>
</td>
</tr>
</blockTable>

View File

@ -20,7 +20,8 @@
##############################################################################
import time
from report import report_sxw
from openerp.report import report_sxw
class ir_module_reference_print(report_sxw.rml_parse):
def __init__(self, cr, uid, name, context):

View File

@ -19,12 +19,13 @@
#
##############################################################################
import tools
import base64
import cStringIO
from osv import fields,osv
from tools.translate import _
from tools.misc import get_iso_codes
from openerp import tools
from openerp.osv import fields,osv
from openerp.tools.translate import _
from openerp.tools.misc import get_iso_codes
NEW_LANG_KEY = '__new__'

View File

@ -19,10 +19,11 @@
#
##############################################################################
import tools
import base64
from tempfile import TemporaryFile
from osv import osv, fields
from openerp import tools
from openerp.osv import osv, fields
class base_language_import(osv.osv_memory):
""" Language Import """

View File

@ -19,9 +19,9 @@
#
##############################################################################
import tools
from osv import osv, fields
from tools.translate import _
from openerp import tools
from openerp.osv import osv, fields
from openerp.tools.translate import _
class base_language_install(osv.osv_memory):
""" Install Language"""

View File

@ -19,8 +19,8 @@
#
##############################################################################
from osv import osv
from tools.translate import _
from openerp.osv import osv
from openerp.tools.translate import _
class base_module_configuration(osv.osv_memory):

View File

@ -19,14 +19,14 @@
#
##############################################################################
import os
import tools
import zipfile
from StringIO import StringIO
import base64
from tools.translate import _
from osv import osv, fields
import os
from StringIO import StringIO
import zipfile
from openerp import tools
from openerp.osv import osv, fields
from openerp.tools.translate import _
ADDONS_PATH = tools.config['addons_path'].split(",")[-1]

View File

@ -21,11 +21,10 @@
import os
import glob
import imp
import tools
import zipfile
from osv import osv
from openerp import tools
from openerp.osv import osv
class base_module_scan(osv.osv_memory):
""" scan module """

View File

@ -1,31 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_base_module_scan" model="ir.ui.view">
<field name="name">Module Scan</field>
<field name="model">base.module.scan</field>
<field name="arch" type="xml">
<form string="Scan for new modules" version="7.0">
<label string="This function will check if you installed new modules in the 'addons' path of your server installation."/>
<footer>
<button name="watch_dir" string="Check new modules" type="object" class="oe_highlight"/>
or
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
<record id="action_view_base_module_scan" model="ir.actions.act_window">
<field name="name">Module Scan</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">base.module.scan</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</data>
</openerp>

View File

@ -18,7 +18,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from osv import osv, fields
from openerp.osv import osv, fields
class base_module_update(osv.osv_memory):
""" Update Module """
@ -54,4 +55,4 @@ class base_module_update(osv.osv_memory):
}
return res
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -19,10 +19,11 @@
#
##############################################################################
from osv import osv, fields
import tools
import cStringIO
from tools.translate import _
from openerp import tools
from openerp.osv import osv, fields
from openerp.tools.translate import _
class base_update_translations(osv.osv_memory):
def _get_languages(self, cr, uid, context):

View File

@ -19,7 +19,7 @@
#
##############################################################################
from report import report_sxw
from openerp.report import report_sxw
class rmlparser(report_sxw.rml_parse):
def set_context(self, objects, data, ids, report_type = None):

View File

@ -19,8 +19,6 @@
#
##############################################################################
import tools
import res_country
import res_lang
import res_partner

View File

@ -19,10 +19,11 @@
#
##############################################################################
from osv import osv,fields
from tools.misc import attrgetter
import time
from openerp.osv import osv,fields
from openerp.tools.misc import attrgetter
# -------------------------------------------------------------------------
# Properties
# -------------------------------------------------------------------------

View File

@ -19,8 +19,8 @@
#
##############################################################################
from osv import fields, osv
from tools.translate import _
from openerp.osv import fields, osv
from openerp.tools.translate import _
class Bank(osv.osv):
_description='Bank'

View File

@ -19,14 +19,14 @@
#
##############################################################################
from osv import osv
from osv import fields
import os
import tools
import openerp
from openerp import SUPERUSER_ID
from tools.translate import _
from tools.safe_eval import safe_eval as eval
from openerp import SUPERUSER_ID, tools
from openerp.osv import fields, osv
from openerp.tools.translate import _
from openerp.tools.safe_eval import safe_eval as eval
from openerp.tools import image_resize_image
class multi_company_default(osv.osv):
"""
@ -102,6 +102,16 @@ class res_company(osv.osv):
part_obj.create(cr, uid, {name: value or False, 'parent_id': company.partner_id.id}, context=context)
return True
def _get_logo_web(self, cr, uid, ids, _field_name, _args, context=None):
result = dict.fromkeys(ids, False)
for record in self.browse(cr, uid, ids, context=context):
size = (180, None)
result[record.id] = image_resize_image(record.partner_id.image, size)
return result
def _get_companies_from_partner(self, cr, uid, ids, context=None):
return self.pool['res.company'].search(cr, uid, [('partner_id', 'in', ids)], context=context)
_columns = {
'name': fields.related('partner_id', 'name', string='Company Name', size=128, required=True, store=True, type='char'),
'parent_id': fields.many2one('res.company', 'Parent Company', select=True),
@ -115,6 +125,10 @@ class res_company(osv.osv):
'rml_footer_readonly': fields.related('rml_footer', type='text', string='Report Footer', readonly=True),
'custom_footer': fields.boolean('Custom Footer', help="Check this to define the report footer manually. Otherwise it will be filled in automatically."),
'logo': fields.related('partner_id', 'image', string="Logo", type="binary"),
'logo_web': fields.function(_get_logo_web, string="Logo Web", type="binary", store={
'res.company': (lambda s, c, u, i, x: i, ['partner_id'], 10),
'res.partner': (_get_companies_from_partner, ['image'], 10),
}),
'currency_id': fields.many2one('res.currency', 'Currency', required=True),
'currency_ids': fields.one2many('res.currency', 'company_id', 'Currency'),
'user_ids': fields.many2many('res.users', 'res_company_users_rel', 'cid', 'user_id', 'Accepted Users'),
@ -352,7 +366,4 @@ class res_company(osv.osv):
]
res_company()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -21,10 +21,10 @@
<form string="Company" version="7.0">
<sheet>
<div>
<field name="logo" nolabel="1" widget="image" class="oe_avatar oe_left"/>
<field name="logo" widget="image" class="oe_avatar oe_left"/>
</div>
<div class="oe_right oe_button_box" name="button_box">
<button name="%(preview_report)d" string="Preview Header/Footer" type="action" icon="gtk-print" class="oe_inline oe_right"/>
<button name="%(preview_report)d" string="Preview Header/Footer" type="action"/>
</div>
<div class="oe_title">
<label for="name" class="oe_edit_only"/>

View File

@ -19,13 +19,12 @@
#
##############################################################################
import logging
from operator import attrgetter, itemgetter
from operator import attrgetter
from osv import osv, fields
from tools.translate import _
import netsvc
from tools import ustr
import pooler
from openerp import pooler
from openerp.osv import osv, fields
from openerp.tools import ustr
from openerp.tools.translate import _
_logger = logging.getLogger(__name__)
@ -309,7 +308,7 @@ class res_config_installer(osv.osv_memory):
hooks_results = set()
for module in base:
hook = getattr(self, '_if_%s'%(module), None)
hook = getattr(self, '_if_%s'% module, None)
if hook:
hooks_results.update(hook(cr, uid, ids, context=None) or set())

View File

@ -19,7 +19,7 @@
#
##############################################################################
from osv import fields, osv
from openerp.osv import fields, osv
def location_name_search(self, cr, user, name='', args=None, operator='ilike',
context=None, limit=100):

View File

@ -1186,7 +1186,7 @@
<record id="us" model="res.country">
<field name="name">United States</field>
<field name="code">us</field>
<field name="address_format" eval="'%(street)s\n%(street2)s\n%(city)s %(state_code)s %(zip)s\n%(country_name)s'" />
<field name="address_format" eval="'%(street)s\n%(street2)s\n%(city)s, %(state_code)s %(zip)s\n%(country_name)s'" />
<field name="currency_id" ref="USD"/>
</record>
<record id="uy" model="res.country">

View File

@ -18,14 +18,14 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import re
import time
import netsvc
from osv import fields, osv
import tools
from tools import float_round, float_is_zero, float_compare
from tools.translate import _
from openerp import tools
from openerp.osv import fields, osv
from openerp.tools import float_round, float_is_zero, float_compare
from openerp.tools.translate import _
CURRENCY_DISPLAY_PATTERN = re.compile(r'(\w+)\s*(?:\((.*)\))?')
@ -99,7 +99,7 @@ class res_currency(osv.osv):
res = super(res_currency, self).read(cr, user, ids, fields, context, load)
currency_rate_obj = self.pool.get('res.currency.rate')
values = res
if not isinstance(values, (list)):
if not isinstance(values, list):
values = [values]
for r in values:
if r.__contains__('rate_ids'):
@ -217,7 +217,7 @@ class res_currency(osv.osv):
if round:
return self.round(cr, uid, to_currency, from_amount * rate)
else:
return (from_amount * rate)
return from_amount * rate
res_currency()

View File

@ -20,14 +20,14 @@
##############################################################################
import locale
from locale import localeconv
import logging
import re
from osv import fields, osv
from locale import localeconv
import tools
from tools.safe_eval import safe_eval as eval
from tools.translate import _
from openerp import tools
from openerp.osv import fields, osv
from openerp.tools.safe_eval import safe_eval as eval
from openerp.tools.translate import _
_logger = logging.getLogger(__name__)
@ -168,7 +168,7 @@ class lang(osv.osv):
thousands_sep = lang_obj.thousands_sep or conv[monetary and 'mon_thousands_sep' or 'thousands_sep']
decimal_point = lang_obj.decimal_point
grouping = lang_obj.grouping
return (grouping, thousands_sep, decimal_point)
return grouping, thousands_sep, decimal_point
def write(self, cr, uid, ids, vals, context=None):
for lang_id in ids :
@ -231,7 +231,7 @@ lang()
def original_group(s, grouping, thousands_sep=''):
if not grouping:
return (s, 0)
return s, 0
result = ""
seps = 0

View File

@ -20,17 +20,16 @@
##############################################################################
import datetime
import math
import openerp
from osv import osv, fields
from openerp import SUPERUSER_ID
import re
import tools
from tools.translate import _
import logging
import pooler
import pytz
from lxml import etree
import math
import pytz
import re
import openerp
from openerp import SUPERUSER_ID
from openerp import pooler, tools
from openerp.osv import osv, fields
from openerp.tools.translate import _
class format_address(object):
def fields_view_get_address(self, cr, uid, arch, context={}):
@ -208,10 +207,6 @@ class res_partner(osv.osv, format_address):
'bank_ids': fields.one2many('res.partner.bank', 'partner_id', 'Banks'),
'website': fields.char('Website', size=64, help="Website of Partner or Company"),
'comment': fields.text('Notes'),
'address': fields.one2many('res.partner.address', 'partner_id', 'Addresses',
deprecated="The address information is now directly stored on each Partner record. "\
"Multiple contacts with their own address can be added via the child_ids relationship. "\
"This field will be removed as of OpenERP 7.1."),
'category_id': fields.many2many('res.partner.category', id1='partner_id', id2='category_id', string='Tags'),
'credit_limit': fields.float(string='Credit Limit'),
'ean13': fields.char('EAN13', size=13),
@ -309,7 +304,7 @@ class res_partner(osv.osv, format_address):
if default is None:
default = {}
name = self.read(cr, uid, [id], ['name'], context)[0]['name']
default.update({'name': _('%s (copy)') % (name)})
default.update({'name': _('%s (copy)') % name})
return super(res_partner, self).copy(cr, uid, id, default, context)
def onchange_type(self, cr, uid, ids, is_company, context=None):
@ -519,7 +514,7 @@ class res_partner(osv.osv, format_address):
def view_header_get(self, cr, uid, view_id, view_type, context):
res = super(res_partner, self).view_header_get(cr, uid, view_id, view_type, context)
if res: return res
if (not context.get('category_id', False)):
if not context.get('category_id', False):
return False
return _('Partners: ')+self.pool.get('res.partner.category').browse(cr, uid, context['category_id'], context).name
@ -538,7 +533,7 @@ class res_partner(osv.osv, format_address):
The purpose of this function is to build and return an address formatted accordingly to the
standards of the country where it belongs.
:param address: browse record of the res.partner.address to format
:param address: browse record of the res.partner to format
:returns: the address formatted in a display that fit its country habits (or the default ones
if not country is specified)
:rtype: string
@ -564,55 +559,4 @@ class res_partner(osv.osv, format_address):
address_format = '%(company_name)s\n' + address_format
return address_format % args
# res.partner.address is deprecated; it is still there for backward compability only and will be removed in next version
class res_partner_address(osv.osv):
_table = "res_partner"
_name = 'res.partner.address'
_order = 'type, name'
_columns = {
'parent_id': fields.many2one('res.partner', 'Company', ondelete='set null', select=True),
'partner_id': fields.related('parent_id', type='many2one', relation='res.partner', string='Partner'), # for backward compatibility
'type': fields.selection( [ ('default','Default'),('invoice','Invoice'), ('delivery','Delivery'), ('contact','Contact'), ('other','Other') ],'Address Type', help="Used to select automatically the right address according to the context in sales and purchases documents."),
'function': fields.char('Function', size=128),
'title': fields.many2one('res.partner.title','Title'),
'name': fields.char('Contact Name', size=64, select=1),
'street': fields.char('Street', size=128),
'street2': fields.char('Street2', size=128),
'zip': fields.char('Zip', change_default=True, size=24),
'city': fields.char('City', size=128),
'state_id': fields.many2one("res.country.state", 'Fed. State', domain="[('country_id','=',country_id)]"),
'country_id': fields.many2one('res.country', 'Country'),
'email': fields.char('Email', size=240),
'phone': fields.char('Phone', size=64),
'fax': fields.char('Fax', size=64),
'mobile': fields.char('Mobile', size=64),
'birthdate': fields.char('Birthdate', size=64),
'is_customer_add': fields.related('partner_id', 'customer', type='boolean', string='Customer'),
'is_supplier_add': fields.related('partner_id', 'supplier', type='boolean', string='Supplier'),
'active': fields.boolean('Active', help="Uncheck the active field to hide the contact."),
'company_id': fields.many2one('res.company', 'Company',select=1),
'color': fields.integer('Color Index'),
}
_defaults = {
'active': True,
'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'res.partner', context=c),
'color': 0,
'type': 'default',
}
def write(self, cr, uid, ids, vals, context=None):
logging.getLogger('res.partner').warning("Deprecated use of res.partner.address")
if 'partner_id' in vals:
vals['parent_id'] = vals.get('partner_id')
del(vals['partner_id'])
return self.pool.get('res.partner').write(cr, uid, ids, vals, context=context)
def create(self, cr, uid, vals, context=None):
logging.getLogger('res.partner').warning("Deprecated use of res.partner.address")
if 'partner_id' in vals:
vals['parent_id'] = vals.get('partner_id')
del(vals['partner_id'])
return self.pool.get('res.partner').create(cr, uid, vals, context=context)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -258,7 +258,7 @@
</group>
<group>
<field name="customer"/>
<field name="supplier" invisible="not context.get('default_supplier')"/>
<field name="supplier"/>
</group>
<group>
<field name="ref"/>

View File

@ -19,9 +19,10 @@
#
##############################################################################
from osv import osv, fields
import time
from openerp.osv import osv, fields
def _links_get(self, cr, uid, context=None):
obj = self.pool.get('res.request.link')
ids = obj.search(cr, uid, [], context=context)
@ -58,7 +59,7 @@ class res_request(osv.osv):
ids = map(lambda x:x[0], cr.fetchall())
cr.execute('select id from res_request where act_from=%s and (act_to<>%s) and (trigger_date<=%s or trigger_date is null) and active=True and state != %s', (uid,uid,time.strftime('%Y-%m-%d'), 'closed'))
ids2 = map(lambda x:x[0], cr.fetchall())
return (ids, ids2)
return ids, ids2
_columns = {
'create_date': fields.datetime('Created Date', readonly=True),

View File

@ -19,23 +19,18 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from functools import partial
import logging
from lxml import etree
from lxml.builder import E
import netsvc
from openerp import SUPERUSER_ID
import openerp
from openerp import SUPERUSER_ID
from openerp import pooler, tools
import openerp.exceptions
from osv import fields,osv
from osv.orm import browse_record
import pooler
import random
from service import security
import tools
from tools.translate import _
from openerp.osv import fields,osv
from openerp.osv.orm import browse_record
from openerp.tools.translate import _
_logger = logging.getLogger(__name__)
@ -72,6 +67,7 @@ class groups(osv.osv):
'rule_groups': fields.many2many('ir.rule', 'rule_group_rel',
'group_id', 'rule_group_id', 'Rules', domain=[('global', '=', False)]),
'menu_access': fields.many2many('ir.ui.menu', 'ir_ui_menu_group_rel', 'gid', 'menu_id', 'Access Menu'),
'view_access': fields.many2many('ir.ui.view', 'ir_ui_view_group_rel', 'group_id', 'view_id', 'Views'),
'comment' : fields.text('Comment', size=250, translate=True),
'category_id': fields.many2one('ir.module.category', 'Application', select=True),
'full_name': fields.function(_get_full_name, type='char', string='Group Name', fnct_search=_search_group),
@ -142,7 +138,7 @@ class res_users(osv.osv):
'id': fields.integer('ID'),
'login_date': fields.date('Latest connection', select=1),
'partner_id': fields.many2one('res.partner', required=True,
string='Related Partner', ondelete='cascade',
string='Related Partner', ondelete='restrict',
help='Partner-related data of the user'),
'login': fields.char('Login', size=64, required=True,
help="Used to log into the system"),
@ -253,7 +249,7 @@ class res_users(osv.osv):
# User can write on a few of his own fields (but not his groups for example)
SELF_WRITEABLE_FIELDS = ['password', 'signature', 'action_id', 'company_id', 'email', 'name', 'image', 'image_medium', 'image_small', 'lang', 'tz']
# User can read a few of his own fields
SELF_READABLE_FIELDS = ['signature', 'company_id', 'login', 'email', 'name', 'image', 'image_medium', 'image_small', 'lang', 'tz', 'groups_id', 'partner_id', '__last_update']
SELF_READABLE_FIELDS = ['signature', 'company_id', 'login', 'email', 'name', 'image', 'image_medium', 'image_small', 'lang', 'tz', 'tz_offset', 'groups_id', 'partner_id', '__last_update']
def read(self, cr, uid, ids, fields=None, context=None, load='_classic_read'):
def override_password(o):
@ -303,7 +299,7 @@ class res_users(osv.osv):
for id in ids:
if id in self._uid_cache[db]:
del self._uid_cache[db][id]
self.context_get.clear_cache(self)
return res
def unlink(self, cr, uid, ids, context=None):
@ -337,6 +333,7 @@ class res_users(osv.osv):
default['login'] = _("%s (copy)") % user2copy['login']
return super(res_users, self).copy(cr, uid, id, default, context)
@tools.ormcache(skiparg=2)
def context_get(self, cr, uid, context=None):
user = self.browse(cr, SUPERUSER_ID, uid, context)
result = {}

View File

@ -32,6 +32,9 @@
<page string="Menus">
<field name="menu_access"/>
</page>
<page string="Views">
<field name="view_access"/>
</page>
<page string="Access Rights">
<field name="model_access">
<tree string="Access Rules" editable="top">
@ -93,7 +96,7 @@
<field name="image" widget='image' class="oe_left oe_avatar" options='{"preview_image": "image_medium", "size": [90, 90]}'/>
<div class="oe_title">
<div class="oe_edit_only">
<label for="name"/> (
<label for="name"/>
</div>
<h1>
<field name="name" default_focus="1" placeholder="Name" />

View File

@ -1,4 +0,0 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"access_res_partner_address_group_partner_manager","res_partner_address group_partner_manager","model_res_partner_address","group_partner_manager",1,1,1,1
"access_res_partner_address_group_user","res_partner_address group_user","model_res_partner_address","group_user",1,0,0,0
"access_res_partner_address","res.partner.address","model_res_partner_address","group_system",1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_res_partner_address_group_partner_manager res_partner_address group_partner_manager model_res_partner_address group_partner_manager 1 1 1 1
3 access_res_partner_address_group_user res_partner_address group_user model_res_partner_address group_user 1 0 0 0
4 access_res_partner_address res.partner.address model_res_partner_address group_system 1 1 1 1

View File

@ -1,8 +1,9 @@
import test_base, test_expression, test_search, test_ir_values
import test_base, test_expression, test_search, test_ir_values, test_menu
checks = [
test_base,
test_expression,
test_search,
test_ir_values,
test_menu,
]

View File

@ -1,11 +1,33 @@
import unittest2
from openerp.osv.orm import BaseModel
import openerp.tests.common as common
class test_expression(common.TransactionCase):
def test_in_not_in_m2m(self):
def _reinit_mock(self):
self.query_list = list()
def _mock_base_model_where_calc(self, model, *args, **kwargs):
""" Mock build_email to be able to test its values. Store them into
some internal variable for latter processing. """
self.query_list.append(self._base_model_where_calc(model, *args, **kwargs))
# return the lastly stored query, the one the ORM wants to perform
return self.query_list[-1]
def setUp(self):
super(test_expression, self).setUp()
# Mock BaseModel._where_calc(), to be able to proceed to some tests about generated expression
self._reinit_mock()
self._base_model_where_calc = BaseModel._where_calc
BaseModel._where_calc = lambda model, cr, uid, args, context: self._mock_base_model_where_calc(model, cr, uid, args, context)
def tearDown(self):
# Remove mocks
BaseModel._where_calc = self._base_model_where_calc
super(test_expression, self).tearDown()
def test_00_in_not_in_m2m(self):
registry, cr, uid = self.registry, self.cr, self.uid
# Create 4 partners with no category, or one or two categories (out of two categories).
@ -70,3 +92,346 @@ class test_expression(common.TransactionCase):
# self.assertTrue(a not in with_any_other_than_a, "Search for category_id with any other than cat_a failed (1).")
# self.assertTrue(ab in with_any_other_than_a, "Search for category_id with any other than cat_a failed (2).")
def test_10_expression_parse(self):
# TDE note: those tests have been added when refactoring the expression.parse() method.
# They come in addition to the already existing test_osv_expression.yml; maybe some tests
# will be a bit redundant
registry, cr, uid = self.registry, self.cr, self.uid
users_obj = registry('res.users')
# Create users
a = users_obj.create(cr, uid, {'name': 'test_A', 'login': 'test_A'})
b1 = users_obj.create(cr, uid, {'name': 'test_B', 'login': 'test_B'})
b1_user = users_obj.browse(cr, uid, [b1])[0]
b2 = users_obj.create(cr, uid, {'name': 'test_B2', 'login': 'test_B2', 'parent_id': b1_user.partner_id.id})
# Test1: simple inheritance
user_ids = users_obj.search(cr, uid, [('name', 'like', 'test')])
self.assertEqual(set(user_ids), set([a, b1, b2]), 'searching through inheritance failed')
user_ids = users_obj.search(cr, uid, [('name', '=', 'test_B')])
self.assertEqual(set(user_ids), set([b1]), 'searching through inheritance failed')
# Test2: inheritance + relational fields
user_ids = users_obj.search(cr, uid, [('child_ids.name', 'like', 'test_B')])
self.assertEqual(set(user_ids), set([b1]), 'searching through inheritance failed')
def test_20_auto_join(self):
registry, cr, uid = self.registry, self.cr, self.uid
# Get models
partner_obj = registry('res.partner')
state_obj = registry('res.country.state')
bank_obj = registry('res.partner.bank')
# Get test columns
partner_state_id_col = partner_obj._columns.get('state_id') # many2one on res.partner to res.country.state
partner_parent_id_col = partner_obj._columns.get('parent_id') # many2one on res.partner to res.partner
state_country_id_col = state_obj._columns.get('country_id') # many2one on res.country.state on res.country
partner_child_ids_col = partner_obj._columns.get('child_ids') # one2many on res.partner to res.partner
partner_bank_ids_col = partner_obj._columns.get('bank_ids') # one2many on res.partner to res.partner.bank
category_id_col = partner_obj._columns.get('category_id') # many2many on res.partner to res.partner.category
# Get the first bank account type to be able to create a res.partner.bank
bank_type = bank_obj._bank_type_get(cr, uid)[0]
# Get country/state data
country_us_id = registry('res.country').search(cr, uid, [('code', 'like', 'US')])[0]
state_ids = registry('res.country.state').search(cr, uid, [('country_id', '=', country_us_id)], limit=2)
# Create demo data: partners and bank object
p_a = partner_obj.create(cr, uid, {'name': 'test__A', 'state_id': state_ids[0]})
p_b = partner_obj.create(cr, uid, {'name': 'test__B', 'state_id': state_ids[1]})
p_aa = partner_obj.create(cr, uid, {'name': 'test__AA', 'parent_id': p_a, 'state_id': state_ids[0]})
p_ab = partner_obj.create(cr, uid, {'name': 'test__AB', 'parent_id': p_a, 'state_id': state_ids[1]})
p_ba = partner_obj.create(cr, uid, {'name': 'test__BA', 'parent_id': p_b, 'state_id': state_ids[0]})
b_aa = bank_obj.create(cr, uid, {'name': '__bank_test_a', 'state': bank_type[0], 'partner_id': p_aa, 'acc_number': '1234'})
b_ab = bank_obj.create(cr, uid, {'name': '__bank_test_b', 'state': bank_type[0], 'partner_id': p_ab, 'acc_number': '5678'})
b_ba = bank_obj.create(cr, uid, {'name': '__bank_test_b', 'state': bank_type[0], 'partner_id': p_ba, 'acc_number': '9876'})
# --------------------------------------------------
# Test1: basics about the attribute
# --------------------------------------------------
category_id_col._auto_join = True
self.assertRaises(NotImplementedError, partner_obj.search, cr, uid, [('category_id.name', '=', 'foo')])
category_id_col._auto_join = False
# --------------------------------------------------
# Test2: one2many
# --------------------------------------------------
name_test = 'test_a'
# Do: one2many without _auto_join
self._reinit_mock()
partner_ids = partner_obj.search(cr, uid, [('bank_ids.name', 'like', name_test)])
# Test result
self.assertEqual(set(partner_ids), set([p_aa]),
"_auto_join off: ('bank_ids.name', 'like', '..'): incorrect result")
# Test produced queries
self.assertEqual(len(self.query_list), 3,
"_auto_join off: ('bank_ids.name', 'like', '..') should produce 3 queries (1 in res_partner_bank, 2 on res_partner)")
sql_query = self.query_list[0].get_sql()
self.assertIn('res_partner_bank', sql_query[0],
"_auto_join off: ('bank_ids.name', 'like', '..') first query incorrect main table")
self.assertIn('"res_partner_bank"."name" like %s', sql_query[1],
"_auto_join off: ('bank_ids.name', 'like', '..') first query incorrect where condition")
self.assertEqual(set(['%' + name_test + '%']), set(sql_query[2]),
"_auto_join off: ('bank_ids.name', 'like', '..') first query incorrect parameter")
sql_query = self.query_list[2].get_sql()
self.assertIn('res_partner', sql_query[0],
"_auto_join off: ('bank_ids.name', 'like', '..') third query incorrect main table")
self.assertIn('"res_partner"."id" in (%s)', sql_query[1],
"_auto_join off: ('bank_ids.name', 'like', '..') third query incorrect where condition")
self.assertEqual(set([p_aa]), set(sql_query[2]),
"_auto_join off: ('bank_ids.name', 'like', '..') third query incorrect parameter")
# Do: cascaded one2many without _auto_join
self._reinit_mock()
partner_ids = partner_obj.search(cr, uid, [('child_ids.bank_ids.id', 'in', [b_aa, b_ba])])
# Test result
self.assertEqual(set(partner_ids), set([p_a, p_b]),
"_auto_join off: ('child_ids.bank_ids.id', 'in', [..]): incorrect result")
# Test produced queries
self.assertEqual(len(self.query_list), 5,
"_auto_join off: ('child_ids.bank_ids.id', 'in', [..]) should produce 5 queries (1 in res_partner_bank, 4 on res_partner)")
# Do: one2many with _auto_join
partner_bank_ids_col._auto_join = True
self._reinit_mock()
partner_ids = partner_obj.search(cr, uid, [('bank_ids.name', 'like', 'test_a')])
# Test result
self.assertEqual(set(partner_ids), set([p_aa]),
"_auto_join on: ('bank_ids.name', 'like', '..') incorrect result")
# Test produced queries
self.assertEqual(len(self.query_list), 1,
"_auto_join on: ('bank_ids.name', 'like', '..') should produce 1 query")
sql_query = self.query_list[0].get_sql()
self.assertIn('"res_partner"', sql_query[0],
"_auto_join on: ('bank_ids.name', 'like', '..') query incorrect main table")
self.assertIn('"res_partner_bank" as "res_partner__bank_ids"', sql_query[0],
"_auto_join on: ('bank_ids.name', 'like', '..') query incorrect join")
self.assertIn('"res_partner__bank_ids"."name" like %s', sql_query[1],
"_auto_join on: ('bank_ids.name', 'like', '..') query incorrect where condition")
self.assertIn('"res_partner"."id"="res_partner__bank_ids"."partner_id"', sql_query[1],
"_auto_join on: ('bank_ids.name', 'like', '..') query incorrect join condition")
self.assertEqual(set(['%' + name_test + '%']), set(sql_query[2]),
"_auto_join on: ('bank_ids.name', 'like', '..') query incorrect parameter")
# Do: one2many with _auto_join, test final leaf is an id
self._reinit_mock()
partner_ids = partner_obj.search(cr, uid, [('bank_ids.id', 'in', [b_aa, b_ab])])
# Test result
self.assertEqual(set(partner_ids), set([p_aa, p_ab]),
"_auto_join on: ('bank_ids.id', 'in', [..]) incorrect result")
# Test produced queries
self.assertEqual(len(self.query_list), 1,
"_auto_join on: ('bank_ids.id', 'in', [..]) should produce 1 query")
sql_query = self.query_list[0].get_sql()
self.assertIn('"res_partner"', sql_query[0],
"_auto_join on: ('bank_ids.id', 'in', [..]) query incorrect main table")
self.assertIn('"res_partner__bank_ids"."id" in (%s,%s)', sql_query[1],
"_auto_join on: ('bank_ids.id', 'in', [..]) query incorrect where condition")
self.assertEqual(set([b_aa, b_ab]), set(sql_query[2]),
"_auto_join on: ('bank_ids.id', 'in', [..]) query incorrect parameter")
# Do: 2 cascaded one2many with _auto_join, test final leaf is an id
partner_child_ids_col._auto_join = True
self._reinit_mock()
partner_ids = partner_obj.search(cr, uid, [('child_ids.bank_ids.id', 'in', [b_aa, b_ba])])
# Test result
self.assertEqual(set(partner_ids), set([p_a, p_b]),
"_auto_join on: ('child_ids.bank_ids.id', 'not in', [..]): incorrect result")
# # Test produced queries
self.assertEqual(len(self.query_list), 1,
"_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) should produce 1 query")
sql_query = self.query_list[0].get_sql()
self.assertIn('"res_partner"', sql_query[0],
"_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) incorrect main table")
self.assertIn('"res_partner" as "res_partner__child_ids"', sql_query[0],
"_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect join")
self.assertIn('"res_partner_bank" as "res_partner__child_ids__bank_ids"', sql_query[0],
"_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect join")
self.assertIn('"res_partner__child_ids__bank_ids"."id" in (%s,%s)', sql_query[1],
"_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect where condition")
self.assertIn('"res_partner"."id"="res_partner__child_ids"."parent_id"', sql_query[1],
"_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect join condition")
self.assertIn('"res_partner__child_ids"."id"="res_partner__child_ids__bank_ids"."partner_id"', sql_query[1],
"_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect join condition")
self.assertEqual(set([b_aa, b_ba]), set(sql_query[2]),
"_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect parameter")
# --------------------------------------------------
# Test3: many2one
# --------------------------------------------------
name_test = 'US'
# Do: many2one without _auto_join
self._reinit_mock()
partner_ids = partner_obj.search(cr, uid, [('state_id.country_id.code', 'like', name_test)])
# Test result: at least our added data + demo data
self.assertTrue(set([p_a, p_b, p_aa, p_ab, p_ba]).issubset(set(partner_ids)),
"_auto_join off: ('state_id.country_id.code', 'like', '..') incorrect result")
# Test produced queries
self.assertEqual(len(self.query_list), 3,
"_auto_join off: ('state_id.country_id.code', 'like', '..') should produce 3 queries (1 on res_country, 1 on res_country_state, 1 on res_partner)")
# Do: many2one with 1 _auto_join on the first many2one
partner_state_id_col._auto_join = True
self._reinit_mock()
partner_ids = partner_obj.search(cr, uid, [('state_id.country_id.code', 'like', name_test)])
# Test result: at least our added data + demo data
self.assertTrue(set([p_a, p_b, p_aa, p_ab, p_ba]).issubset(set(partner_ids)),
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') incorrect result")
# Test produced queries
self.assertEqual(len(self.query_list), 2,
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') should produce 2 query")
sql_query = self.query_list[0].get_sql()
self.assertIn('"res_country"', sql_query[0],
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect main table")
self.assertIn('"res_country"."code" like %s', sql_query[1],
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect where condition")
self.assertEqual(['%' + name_test + '%'], sql_query[2],
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect parameter")
sql_query = self.query_list[1].get_sql()
self.assertIn('"res_partner"', sql_query[0],
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect main table")
self.assertIn('"res_country_state" as "res_partner__state_id"', sql_query[0],
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect join")
self.assertIn('"res_partner__state_id"."country_id" in (%s)', sql_query[1],
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect where condition")
self.assertIn('"res_partner"."state_id"="res_partner__state_id"."id"', sql_query[1],
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect join condition")
# Do: many2one with 1 _auto_join on the second many2one
partner_state_id_col._auto_join = False
state_country_id_col._auto_join = True
self._reinit_mock()
partner_ids = partner_obj.search(cr, uid, [('state_id.country_id.code', 'like', name_test)])
# Test result: at least our added data + demo data
self.assertTrue(set([p_a, p_b, p_aa, p_ab, p_ba]).issubset(set(partner_ids)),
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') incorrect result")
# Test produced queries
self.assertEqual(len(self.query_list), 2,
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') should produce 2 query")
# -- first query
sql_query = self.query_list[0].get_sql()
self.assertIn('"res_country_state"', sql_query[0],
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect main table")
self.assertIn('"res_country" as "res_country_state__country_id"', sql_query[0],
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect join")
self.assertIn('"res_country_state__country_id"."code" like %s', sql_query[1],
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect where condition")
self.assertIn('"res_country_state"."country_id"="res_country_state__country_id"."id"', sql_query[1],
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect join condition")
self.assertEqual(['%' + name_test + '%'], sql_query[2],
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect parameter")
# -- second query
sql_query = self.query_list[1].get_sql()
self.assertIn('"res_partner"', sql_query[0],
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect main table")
self.assertIn('"res_partner"."state_id" in', sql_query[1],
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect where condition")
# Do: many2one with 2 _auto_join
partner_state_id_col._auto_join = True
state_country_id_col._auto_join = True
self._reinit_mock()
partner_ids = partner_obj.search(cr, uid, [('state_id.country_id.code', 'like', name_test)])
# Test result: at least our added data + demo data
self.assertTrue(set([p_a, p_b, p_aa, p_ab, p_ba]).issubset(set(partner_ids)),
"_auto_join on: ('state_id.country_id.code', 'like', '..') incorrect result")
# Test produced queries
self.assertEqual(len(self.query_list), 1,
"_auto_join on: ('state_id.country_id.code', 'like', '..') should produce 1 query")
sql_query = self.query_list[0].get_sql()
self.assertIn('"res_partner"', sql_query[0],
"_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect main table")
self.assertIn('"res_country_state" as "res_partner__state_id"', sql_query[0],
"_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join")
self.assertIn('"res_country" as "res_partner__state_id__country_id"', sql_query[0],
"_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join")
self.assertIn('"res_partner__state_id__country_id"."code" like %s', sql_query[1],
"_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect where condition")
self.assertIn('"res_partner"."state_id"="res_partner__state_id"."id"', sql_query[1],
"_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join condition")
self.assertIn('"res_partner__state_id"."country_id"="res_partner__state_id__country_id"."id"', sql_query[1],
"_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join condition")
self.assertEqual(['%' + name_test + '%'], sql_query[2],
"_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect parameter")
# --------------------------------------------------
# Test4: domain attribute on one2many fields
# --------------------------------------------------
partner_child_ids_col._auto_join = True
partner_bank_ids_col._auto_join = True
partner_child_ids_col._domain = lambda self: ['!', ('name', '=', self._name)]
partner_bank_ids_col._domain = [('acc_number', 'like', '1')]
# Do: 2 cascaded one2many with _auto_join, test final leaf is an id
self._reinit_mock()
partner_ids = partner_obj.search(cr, uid, ['&', (1, '=', 1), ('child_ids.bank_ids.id', 'in', [b_aa, b_ba])])
# Test result: at least one of our added data
self.assertTrue(set([p_a]).issubset(set(partner_ids)),
"_auto_join on one2many with domains incorrect result")
self.assertTrue(set([p_ab, p_ba]) not in set(partner_ids),
"_auto_join on one2many with domains incorrect result")
# Test produced queries that domains effectively present
sql_query = self.query_list[0].get_sql()
self.assertIn('"res_partner__child_ids__bank_ids"."acc_number" like %s', sql_query[1],
"_auto_join on one2many with domains incorrect result")
# TDE TODO: check first domain has a correct table name
self.assertIn('"res_partner__child_ids"."name" = %s', sql_query[1],
"_auto_join on one2many with domains incorrect result")
partner_child_ids_col._domain = lambda self: [('name', '=', '__%s' % self._name)]
self._reinit_mock()
partner_ids = partner_obj.search(cr, uid, ['&', (1, '=', 1), ('child_ids.bank_ids.id', 'in', [b_aa, b_ba])])
# Test result: no one
self.assertFalse(partner_ids,
"_auto_join on one2many with domains incorrect result")
# ----------------------------------------
# Test5: result-based tests
# ----------------------------------------
partner_bank_ids_col._auto_join = False
partner_child_ids_col._auto_join = False
partner_state_id_col._auto_join = False
partner_parent_id_col._auto_join = False
state_country_id_col._auto_join = False
partner_child_ids_col._domain = []
partner_bank_ids_col._domain = []
# Do: ('child_ids.state_id.country_id.code', 'like', '..') without _auto_join
self._reinit_mock()
partner_ids = partner_obj.search(cr, uid, [('child_ids.state_id.country_id.code', 'like', name_test)])
# Test result: at least our added data + demo data
self.assertTrue(set([p_a, p_b]).issubset(set(partner_ids)),
"_auto_join off: ('child_ids.state_id.country_id.code', 'like', '..') incorrect result")
# Test produced queries
self.assertEqual(len(self.query_list), 5,
"_auto_join off: ('child_ids.state_id.country_id.code', 'like', '..') number of queries incorrect")
# Do: ('child_ids.state_id.country_id.code', 'like', '..') with _auto_join
partner_child_ids_col._auto_join = True
partner_state_id_col._auto_join = True
state_country_id_col._auto_join = True
self._reinit_mock()
partner_ids = partner_obj.search(cr, uid, [('child_ids.state_id.country_id.code', 'like', name_test)])
# Test result: at least our added data + demo data
self.assertTrue(set([p_a, p_b]).issubset(set(partner_ids)),
"_auto_join on: ('child_ids.state_id.country_id.code', 'like', '..') incorrect result")
# Test produced queries
self.assertEqual(len(self.query_list), 1,
"_auto_join on: ('child_ids.state_id.country_id.code', 'like', '..') number of queries incorrect")
# Remove mocks and modifications
partner_bank_ids_col._auto_join = False
partner_child_ids_col._auto_join = False
partner_state_id_col._auto_join = False
partner_parent_id_col._auto_join = False
state_country_id_col._auto_join = False
if __name__ == '__main__':
unittest2.main()

View File

@ -50,20 +50,25 @@ class test_ir_values(common.TransactionCase):
# Create some action bindings for a non-existing model.
act_id_1 = self.ref('base.act_values_form_action')
act_id_2 = self.ref('base.act_values_form_defaults')
act_id_3 = self.ref('base.action_res_company_form')
act_id_4 = self.ref('base.action_res_company_tree')
ir_values = self.registry('ir.values')
ir_values.set(self.cr, self.uid, 'action', 'tree_but_open', 'OnDblClick Action', ['unexisting_model'], 'ir.actions.act_window,10', isobject=True)
ir_values.set(self.cr, self.uid, 'action', 'tree_but_open', 'OnDblClick Action 2', ['unexisting_model'], 'ir.actions.act_window,11', isobject=True)
ir_values.set(self.cr, self.uid, 'action', 'client_action_multi', 'Side Wizard', ['unexisting_model'], 'ir.actions.act_window,12', isobject=True)
ir_values.set(self.cr, self.uid, 'action', 'tree_but_open', 'OnDblClick Action', ['unexisting_model'], 'ir.actions.act_window,%d' % act_id_1, isobject=True)
ir_values.set(self.cr, self.uid, 'action', 'tree_but_open', 'OnDblClick Action 2', ['unexisting_model'], 'ir.actions.act_window,%d' % act_id_2, isobject=True)
ir_values.set(self.cr, self.uid, 'action', 'client_action_multi', 'Side Wizard', ['unexisting_model'], 'ir.actions.act_window,%d' % act_id_3, isobject=True)
report_ids = self.registry('ir.actions.report.xml').search(self.cr, self.uid, [], {})
reports = self.registry('ir.actions.report.xml').browse(self.cr, self.uid, report_ids, {})
report_id = [report.id for report in reports if not report.groups_id][0] # assume at least one
ir_values.set(self.cr, self.uid, 'action', 'client_print_multi', 'Nice Report', ['unexisting_model'], 'ir.actions.report.xml,%s'%report_id, isobject=True)
ir_values.set(self.cr, self.uid, 'action', 'client_action_relate', 'Related Stuff', ['unexisting_model'], 'ir.actions.act_window,14', isobject=True)
report_id = [report.id for report in reports if not report.groups_id][0] # assume at least one
ir_values.set(self.cr, self.uid, 'action', 'client_print_multi', 'Nice Report', ['unexisting_model'], 'ir.actions.report.xml,%d' % report_id, isobject=True)
ir_values.set(self.cr, self.uid, 'action', 'client_action_relate', 'Related Stuff', ['unexisting_model'], 'ir.actions.act_window,%d' % act_id_4, isobject=True)
# Replace one action binding to set a new name.
ir_values = self.registry('ir.values')
ir_values.set(self.cr, self.uid, 'action', 'tree_but_open', 'OnDblClick Action New', ['unexisting_model'], 'ir.actions.act_window,10', isobject=True)
ir_values.set(self.cr, self.uid, 'action', 'tree_but_open', 'OnDblClick Action New', ['unexisting_model'], 'ir.actions.act_window,%d' % act_id_1, isobject=True)
# Retrieve the action bindings and check they're correct
@ -73,17 +78,17 @@ class test_ir_values(common.TransactionCase):
#first action
assert len(actions[0]) == 3, "Malformed action definition"
assert actions[0][1] == 'OnDblClick Action 2', 'Bound action does not match definition'
assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == 11, 'Bound action does not match definition'
assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == act_id_2, 'Bound action does not match definition'
#second action - this ones comes last because it was re-created with a different name
assert len(actions[1]) == 3, "Malformed action definition"
assert actions[1][1] == 'OnDblClick Action New', 'Re-Registering an action should replace it'
assert isinstance(actions[1][2], dict) and actions[1][2]['id'] == 10, 'Bound action does not match definition'
assert isinstance(actions[1][2], dict) and actions[1][2]['id'] == act_id_1, 'Bound action does not match definition'
actions = ir_values.get(self.cr, self.uid, 'action', 'client_action_multi', ['unexisting_model'])
assert len(actions) == 1, "Mismatching number of bound actions"
assert len(actions[0]) == 3, "Malformed action definition"
assert actions[0][1] == 'Side Wizard', 'Bound action does not match definition'
assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == 12, 'Bound action does not match definition'
assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == act_id_3, 'Bound action does not match definition'
actions = ir_values.get(self.cr, self.uid, 'action', 'client_print_multi', ['unexisting_model'])
assert len(actions) == 1, "Mismatching number of bound actions"
@ -95,7 +100,7 @@ class test_ir_values(common.TransactionCase):
assert len(actions) == 1, "Mismatching number of bound actions"
assert len(actions[0]) == 3, "Malformed action definition"
assert actions[0][1] == 'Related Stuff', 'Bound action does not match definition'
assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == 14, 'Bound action does not match definition'
assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == act_id_4, 'Bound action does not match definition'
if __name__ == '__main__':

View File

@ -0,0 +1,32 @@
import openerp.tests.common as common
class test_menu(common.TransactionCase):
def setUp(self):
super(test_menu,self).setUp()
self.Menus = self.registry('ir.ui.menu')
def test_00_menu_deletion(self):
"""Verify that menu deletion works properly when there are child menus, and those
are indeed made orphans"""
cr, uid, Menus = self.cr, self.uid, self.Menus
# Generic trick necessary for search() calls to avoid hidden menus
ctx = {'ir.ui.menu.full_list': True}
root_id = Menus.create(cr, uid, {'name': 'Test root'})
child1_id = Menus.create(cr, uid, {'name': 'Test child 1', 'parent_id': root_id})
child2_id = Menus.create(cr, uid, {'name': 'Test child 2', 'parent_id': root_id})
child21_id = Menus.create(cr, uid, {'name': 'Test child 2-1', 'parent_id': child2_id})
all_ids = [root_id, child1_id, child2_id, child21_id]
# delete and check that direct children are promoted to top-level
# cfr. explanation in menu.unlink()
Menus.unlink(cr, uid, [root_id])
remaining_ids = Menus.search(cr, uid, [('id', 'in', all_ids)], order="id", context=ctx)
self.assertEqual([child1_id, child2_id, child21_id], remaining_ids)
orphan_ids = Menus.search(cr, uid, [('id', 'in', all_ids), ('parent_id', '=', False)], order="id", context=ctx)
self.assertEqual([child1_id, child2_id], orphan_ids)

View File

@ -2,16 +2,17 @@ import unittest2
import openerp.tests.common as common
class test_expression(common.TransactionCase):
def test_search_order(self):
class test_search(common.TransactionCase):
def test_00_search_order(self):
registry, cr, uid = self.registry, self.cr, self.uid
# Create 6 partners with a given name, and a given creation order to
# ensure the order of their ID. Some are set as unactive to verify they
# are by default excluded from the searches and to provide a second
# `order` argument.
# Create 6 partners with a given name, and a given creation order to
# ensure the order of their ID. Some are set as unactive to verify they
# are by default excluded from the searches and to provide a second
# `order` argument.
partners = registry('res.partner')
c = partners.create(cr, uid, {'name': 'test_search_order_C'})
@ -23,9 +24,9 @@ class test_expression(common.TransactionCase):
# The tests.
# The basic searches should exclude records that have active = False.
# The order of the returned ids should be given by the `order`
# parameter of search().
# The basic searches should exclude records that have active = False.
# The order of the returned ids should be given by the `order`
# parameter of search().
name_asc = partners.search(cr, uid, [('name', 'like', 'test_search_order%')], order="name asc")
self.assertEqual([a, ab, b, c], name_asc, "Search with 'NAME ASC' order failed.")
@ -36,9 +37,9 @@ class test_expression(common.TransactionCase):
id_desc = partners.search(cr, uid, [('name', 'like', 'test_search_order%')], order="id desc")
self.assertEqual([ab, b, a, c], id_desc, "Search with 'ID DESC' order failed.")
# The inactive records shouldn't be excluded as soon as a condition on
# that field is present in the domain. The `order` parameter of
# search() should support any legal coma-separated values.
# The inactive records shouldn't be excluded as soon as a condition on
# that field is present in the domain. The `order` parameter of
# search() should support any legal coma-separated values.
active_asc_id_asc = partners.search(cr, uid, [('name', 'like', 'test_search_order%'), '|', ('active', '=', True), ('active', '=', False)], order="active asc, id asc")
self.assertEqual([d, e, c, a, b, ab], active_asc_id_asc, "Search with 'ACTIVE ASC, ID ASC' order failed.")
@ -57,4 +58,52 @@ class test_expression(common.TransactionCase):
id_desc_active_desc = partners.search(cr, uid, [('name', 'like', 'test_search_order%'), '|', ('active', '=', True), ('active', '=', False)], order="id desc, active desc")
self.assertEqual([e, ab, b, a, d, c], id_desc_active_desc, "Search with 'ID DESC, ACTIVE DESC' order failed.")
def test_10_inherits_m2order(self):
registry, cr, uid = self.registry, self.cr, self.uid
users_obj = registry('res.users')
# Find Employee group
group_employee_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'base', 'group_user')
group_employee_id = group_employee_ref and group_employee_ref[1] or False
# Get country/state data
country_us_id = registry('res.country').search(cr, uid, [('code', 'like', 'US')])[0]
state_ids = registry('res.country.state').search(cr, uid, [('country_id', '=', country_us_id)], limit=2)
country_be_id = registry('res.country').search(cr, uid, [('code', 'like', 'BE')])[0]
# Create test users
search_user = users_obj.create(cr, uid, {'name': '__search', 'login': '__search', 'groups_id': [(6, 0, [group_employee_id])]})
a = users_obj.create(cr, uid, {'name': '__test_A', 'login': '__test_A', 'country_id': country_be_id, 'state_id': country_be_id})
b = users_obj.create(cr, uid, {'name': '__test_B', 'login': '__a_test_B', 'country_id': country_us_id, 'state_id': state_ids[1]})
c = users_obj.create(cr, uid, {'name': '__test_B', 'login': '__z_test_B', 'country_id': country_us_id, 'state_id': state_ids[0]})
# Do: search on res.users, order on a field on res.partner to try inherits'd fields, then res.users
user_ids = users_obj.search(cr, search_user, [], order='name asc, login desc')
expected_ids = [search_user, a, c, b]
test_user_ids = filter(lambda x: x in expected_ids, user_ids)
self.assertEqual(test_user_ids, expected_ids, 'search on res_users did not provide expected ids or expected order')
# Do: order on many2one and inherits'd fields
user_ids = users_obj.search(cr, search_user, [], order='state_id asc, country_id desc, name asc, login desc')
expected_ids = [c, b, a, search_user]
test_user_ids = filter(lambda x: x in expected_ids, user_ids)
self.assertEqual(test_user_ids, expected_ids, 'search on res_users did not provide expected ids or expected order')
# Do: order on many2one and inherits'd fields
user_ids = users_obj.search(cr, search_user, [], order='country_id desc, state_id desc, name asc, login desc')
expected_ids = [search_user, b, c, a]
test_user_ids = filter(lambda x: x in expected_ids, user_ids)
self.assertEqual(test_user_ids, expected_ids, 'search on res_users did not provide expected ids or expected order')
# Do: order on many2one, but not by specifying in order parameter of search, but by overriding _order of res_users
old_order = users_obj._order
users_obj._order = 'country_id desc, name asc, login desc'
user_ids = users_obj.search(cr, search_user, [])
expected_ids = [search_user, c, b, a]
test_user_ids = filter(lambda x: x in expected_ids, user_ids)
self.assertEqual(test_user_ids, expected_ids, 'search on res_users did not provide expected ids or expected order')
users_obj._order = old_order
if __name__ == '__main__':
unittest2.main()

View File

@ -20,16 +20,14 @@
##############################################################################
import xmlrpclib
import ConfigParser
import optparse
import sys
import thread
import threading
import os
import time
import pickle
import base64
import socket
import string
admin_passwd = 'admin'
waittime = 10
@ -67,10 +65,10 @@ def execute(connector, method, *args):
except socket.error,e:
if e.args[0] == 111:
if wait_count > wait_limit:
print "Server is taking too long to start, it has exceeded the maximum limit of %d seconds."%(wait_limit)
print "Server is taking too long to start, it has exceeded the maximum limit of %d seconds." % wait_limit
clean()
sys.exit(1)
print 'Please wait %d sec to start server....'%(waittime)
print 'Please wait %d sec to start server....' % waittime
wait_count += 1
time.sleep(waittime)
res = execute(connector, method, *args)
@ -136,7 +134,7 @@ def check_quality(uri, user, pwd, dbname, modules, quality_logs):
detail_html +='''<div id=\"%s\"><h3>%s (Score : %s)</h3><font color=red><h5>%s</h5></font>%s</div>'''%(test.replace(' ', '-'), test, score, msg, detail.get('detail', ''))
test_detail[test] = (score,msg,detail.get('detail',''))
html += "</ul>"
html += "%s"%(detail_html)
html += "%s"% detail_html
html += "</div></body></html>"
if not os.path.isdir(quality_logs):
os.mkdir(quality_logs)
@ -305,13 +303,11 @@ options = {
'port' : opt.port or 8069,
'netport':opt.netport or 8070,
'database': opt.db_name or 'terp',
'modules' : opt.modules or [],
'modules' : map(string.strip, opt.modules.split(',')) if opt.modules else [],
'login' : opt.login or 'admin',
'pwd' : opt.pwd or '',
'extra-addons':opt.extra_addons or []
}
options['modules'] = opt.modules and map(lambda m: m.strip(), opt.modules.split(',')) or []
# Hint:i18n-import=purchase:ar_AR.po+sale:fr_FR.po,nl_BE.po
if opt.translate_in:
translate = opt.translate_in

View File

@ -2,6 +2,8 @@ import logging
import sys
import openerp
from openerp import tools
from openerp.modules import module
_logger = logging.getLogger(__name__)
@ -32,8 +34,25 @@ import server
def main():
args = sys.argv[1:]
# The only shared option is '--addons-path=' needed to discover additional
# commands from modules
if len(args) > 1 and args[0].startswith('--addons-path=') and not args[1].startswith("-"):
tools.config.parse_config([args[0]])
args = args[1:]
# Default legacy command
command = "server"
# Subcommand discovery
if len(args) and not args[0].startswith("-"):
for m in module.get_modules():
m = 'openerp.addons.' + m
__import__(m)
#try:
#except Exception, e:
# raise
# print e
command = args[0]
args = args[1:]

View File

@ -94,10 +94,8 @@ def setup_pid_file():
def preload_registry(dbname):
""" Preload a registry, and start the cron."""
try:
db, registry = openerp.pooler.get_db_and_pool(dbname, update_module=openerp.tools.config['init'] or openerp.tools.config['update'], pooljobs=False)
# jobs will start to be processed later, when openerp.cron.start_master_thread() is called by openerp.service.start_services()
registry.schedule_cron_jobs()
update_module = True if openerp.tools.config['init'] or openerp.tools.config['update'] else False
db, registry = openerp.pooler.get_db_and_pool(dbname, update_module=update_module, pooljobs=False)
except Exception:
_logger.exception('Failed to initialize database `%s`.', dbname)

View File

@ -35,10 +35,6 @@ must be used.
import deprecation
# Maximum number of threads processing concurrently cron jobs.
max_cron_threads = 4 # Actually the default value here is meaningless,
# look at tools.config for the default value.
# Paths to search for OpenERP addons.
addons_paths = []

View File

@ -1,212 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2011 OpenERP SA (<http://www.openerp.com>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
""" Cron jobs scheduling
Cron jobs are defined in the ir_cron table/model. This module deals with all
cron jobs, for all databases of a single OpenERP server instance.
It defines a single master thread that will spawn (a bounded number of)
threads to process individual cron jobs.
The thread runs forever, checking every 60 seconds for new
'database wake-ups'. It maintains a heapq of database wake-ups. At each
wake-up, it will call ir_cron._run_jobs_multithread() for the given database. _run_jobs_multithread
will check the jobs defined in the ir_cron table and spawn accordingly threads
to process them.
This module's behavior depends on the following configuration variable:
openerp.conf.max_cron_threads.
"""
import heapq
import logging
import threading
import time
import openerp
import tools
_logger = logging.getLogger(__name__)
# Heapq of database wake-ups. Note that 'database wake-up' meaning is in
# the context of the cron management. This is not originally about loading
# a database, although having the database name in the queue will
# cause it to be loaded when the schedule time is reached, even if it was
# unloaded in the mean time. Normally a database's wake-up is cancelled by
# the RegistryManager when the database is unloaded - so this should not
# cause it to be reloaded.
#
# TODO: perhaps in the future we could consider a flag on ir.cron jobs
# that would cause database wake-up even if the database has not been
# loaded yet or was already unloaded (e.g. 'force_db_wakeup' or something)
#
# Each element is a triple (timestamp, database-name, boolean). The boolean
# specifies if the wake-up is canceled (so a wake-up can be canceled without
# relying on the heapq implementation detail; no need to remove the job from
# the heapq).
_wakeups = []
# Mapping of database names to the wake-up defined in the heapq,
# so that we can cancel the wake-up without messing with the heapq
# invariant: lookup the wake-up by database-name, then set
# its third element to True.
_wakeup_by_db = {}
# Re-entrant lock to protect the above _wakeups and _wakeup_by_db variables.
# We could use a simple (non-reentrant) lock if the runner function below
# was more fine-grained, but we are fine with the loop owning the lock
# while spawning a few threads.
_wakeups_lock = threading.RLock()
# Maximum number of threads allowed to process cron jobs concurrently. This
# variable is set by start_master_thread using openerp.conf.max_cron_threads.
_thread_slots = None
# A (non re-entrant) lock to protect the above _thread_slots variable.
_thread_slots_lock = threading.Lock()
# Sleep duration limits - must not loop too quickly, but can't sleep too long
# either, because a new job might be inserted in ir_cron with a much sooner
# execution date than current known ones. We won't see it until we wake!
MAX_SLEEP = 60 # 1 min
MIN_SLEEP = 1 # 1 sec
# Dummy wake-up timestamp that can be used to force a database wake-up asap
WAKE_UP_NOW = 1
def get_thread_slots():
""" Return the number of available thread slots """
return _thread_slots
def release_thread_slot():
""" Increment the number of available thread slots """
global _thread_slots
with _thread_slots_lock:
_thread_slots += 1
def take_thread_slot():
""" Decrement the number of available thread slots """
global _thread_slots
with _thread_slots_lock:
_thread_slots -= 1
def cancel(db_name):
""" Cancel the next wake-up of a given database, if any.
:param db_name: database name for which the wake-up is canceled.
"""
_logger.debug("Cancel next wake-up for database '%s'.", db_name)
with _wakeups_lock:
if db_name in _wakeup_by_db:
_wakeup_by_db[db_name][2] = True
def cancel_all():
""" Cancel all database wake-ups. """
_logger.debug("Cancel all database wake-ups")
global _wakeups
global _wakeup_by_db
with _wakeups_lock:
_wakeups = []
_wakeup_by_db = {}
def schedule_wakeup(timestamp, db_name):
""" Schedule a new wake-up for a database.
If an earlier wake-up is already defined, the new wake-up is discarded.
If another wake-up is defined, that wake-up is discarded and the new one
is scheduled.
:param db_name: database name for which a new wake-up is scheduled.
:param timestamp: when the wake-up is scheduled.
"""
if not timestamp:
return
with _wakeups_lock:
if db_name in _wakeup_by_db:
task = _wakeup_by_db[db_name]
if not task[2] and timestamp > task[0]:
# existing wakeup is valid and occurs earlier than new one
return
task[2] = True # cancel existing task
task = [timestamp, db_name, False]
heapq.heappush(_wakeups, task)
_wakeup_by_db[db_name] = task
_logger.debug("Wake-up scheduled for database '%s' @ %s", db_name,
'NOW' if timestamp == WAKE_UP_NOW else timestamp)
def runner():
"""Neverending function (intended to be run in a dedicated thread) that
checks every 60 seconds the next database wake-up. TODO: make configurable
"""
while True:
runner_body()
def runner_body():
with _wakeups_lock:
while _wakeups and _wakeups[0][0] < time.time() and get_thread_slots():
task = heapq.heappop(_wakeups)
timestamp, db_name, canceled = task
if canceled:
continue
del _wakeup_by_db[db_name]
registry = openerp.pooler.get_pool(db_name)
if not registry._init:
_logger.debug("Database '%s' wake-up! Firing multi-threaded cron job processing", db_name)
registry['ir.cron']._run_jobs_multithread()
amount = MAX_SLEEP
with _wakeups_lock:
# Sleep less than MAX_SLEEP if the next known wake-up will happen before that.
if _wakeups and get_thread_slots():
amount = min(MAX_SLEEP, max(MIN_SLEEP, _wakeups[0][0] - time.time()))
_logger.debug("Going to sleep for %ss", amount)
time.sleep(amount)
def start_master_thread():
""" Start the above runner function in a daemon thread.
The thread is a typical daemon thread: it will never quit and must be
terminated when the main process exits - with no consequence (the processing
threads it spawns are not marked daemon).
"""
global _thread_slots
_thread_slots = openerp.conf.max_cron_threads
db_maxconn = tools.config['db_maxconn']
if _thread_slots >= tools.config.get('db_maxconn', 64):
_logger.warning("Connection pool size (%s) is set lower than max number of cron threads (%s), "
"this may cause trouble if you reach that number of parallel cron tasks.",
db_maxconn, _thread_slots)
t = threading.Thread(target=runner, name="openerp.cron.master_thread")
t.setDaemon(True)
t.start()
_logger.debug("Master cron daemon started!")
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -110,7 +110,7 @@ def get_encodings(hint_encoding='utf-8'):
# some defaults (also taking care of pure ASCII)
for charset in ['utf8','latin1']:
if not (hint_encoding) or (charset.lower() != hint_encoding.lower()):
if not hint_encoding or (charset.lower() != hint_encoding.lower()):
yield charset
from locale import getpreferredencoding
@ -129,7 +129,7 @@ def ustr(value, hint_encoding='utf-8', errors='strict'):
:param: value: the value to convert
:param: hint_encoding: an optional encoding that was detecte
upstream and should be tried first to decode ``value``.
:param str error: optional `errors` flag to pass to the unicode
:param str errors: optional `errors` flag to pass to the unicode
built-in to indicate how illegal character values should be
treated when converting a string: 'strict', 'ignore' or 'replace'
(see ``unicode()`` constructor).

View File

@ -142,9 +142,13 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=
migrations = openerp.modules.migration.MigrationManager(cr, graph)
_logger.debug('loading %d packages...', len(graph))
# get db timestamp
cr.execute("select (now() at time zone 'UTC')::timestamp")
dt_before_load = cr.fetchone()[0]
# Query manual fields for all models at once and save them on the registry
# so the initialization code for each model does not have to do it
# one model at a time.
pool.fields_by_model = {}
cr.execute('SELECT * FROM ir_model_fields WHERE state=%s', ('manual',))
for field in cr.dictfetchall():
pool.fields_by_model.setdefault(field['model'], []).append(field)
# register, instantiate and initialize models for each modules
for index, package in enumerate(graph):
@ -159,6 +163,7 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=
load_openerp_module(package.name)
models = pool.load(cr, package)
loaded_modules.append(package.name)
if hasattr(package, 'init') or hasattr(package, 'update') or package.state in ('to install', 'to upgrade'):
init_module_models(cr, package.name, models)
@ -220,6 +225,10 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=
delattr(package, kind)
cr.commit()
# The query won't be valid for models created later (i.e. custom model
# created after the registry has been loaded), so empty its result.
pool.fields_by_model = None
cr.commit()
@ -239,7 +248,7 @@ def _check_module_names(cr, module_names):
incorrect_names = mod_names.difference([x['name'] for x in cr.dictfetchall()])
_logger.warning('invalid module names, ignored: %s', ", ".join(incorrect_names))
def load_marked_modules(cr, graph, states, force, progressdict, report, loaded_modules):
def load_marked_modules(cr, graph, states, force, progressdict, report, loaded_modules, perform_checks):
"""Loads modules marked with ``states``, adding them to ``graph`` and
``loaded_modules`` and returns a list of installed/upgraded modules."""
processed_modules = []
@ -248,7 +257,7 @@ def load_marked_modules(cr, graph, states, force, progressdict, report, loaded_m
module_list = [name for (name,) in cr.fetchall() if name not in graph]
graph.add_modules(cr, module_list, force)
_logger.debug('Updating graph with %d more modules', len(module_list))
loaded, processed = load_module_graph(cr, graph, progressdict, report=report, skip_modules=loaded_modules)
loaded, processed = load_module_graph(cr, graph, progressdict, report=report, skip_modules=loaded_modules, perform_checks=perform_checks)
processed_modules.extend(processed)
loaded_modules.extend(loaded)
if not processed: break
@ -293,7 +302,7 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
# processed_modules: for cleanup step after install
# loaded_modules: to avoid double loading
report = pool._assertion_report
loaded_modules, processed_modules = load_module_graph(cr, graph, status, perform_checks=(not update_module), report=report)
loaded_modules, processed_modules = load_module_graph(cr, graph, status, perform_checks=update_module, report=report)
if tools.config['load_language']:
for lang in tools.config['load_language'].split(','):
@ -333,11 +342,11 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
# be dropped in STEP 6 later, before restarting the loading
# process.
states_to_load = ['installed', 'to upgrade', 'to remove']
processed = load_marked_modules(cr, graph, states_to_load, force, status, report, loaded_modules)
processed = load_marked_modules(cr, graph, states_to_load, force, status, report, loaded_modules, update_module)
processed_modules.extend(processed)
if update_module:
states_to_load = ['to install']
processed = load_marked_modules(cr, graph, states_to_load, force, status, report, loaded_modules)
processed = load_marked_modules(cr, graph, states_to_load, force, status, report, loaded_modules, update_module)
processed_modules.extend(processed)
# load custom models

View File

@ -433,8 +433,9 @@ def get_modules():
return name
def is_really_module(name):
name = opj(dir, name)
return os.path.isdir(name) or zipfile.is_zipfile(name)
manifest_name = opj(dir, name, '__openerp__.py')
zipfile_name = opj(dir, name)
return os.path.isfile(manifest_name) or zipfile.is_zipfile(zipfile_name)
return map(clean, filter(is_really_module, os.listdir(dir)))
plist = []

View File

@ -28,7 +28,6 @@ import threading
import openerp.sql_db
import openerp.osv.orm
import openerp.cron
import openerp.tools
import openerp.modules.db
import openerp.tools.config
@ -51,6 +50,7 @@ class Registry(object):
self._init = True
self._init_parent = {}
self._assertion_report = assertion_report.assertion_report()
self.fields_by_model = None
# modules fully loaded (maintained during init phase by `loading` module)
self._init_modules = set()
@ -58,6 +58,21 @@ class Registry(object):
self.db_name = db_name
self.db = openerp.sql_db.db_connect(db_name)
# In monoprocess cron jobs flag (pooljobs)
self.cron = False
# Inter-process signaling (used only when openerp.multi_process is True):
# The `base_registry_signaling` sequence indicates the whole registry
# must be reloaded.
# The `base_cache_signaling sequence` indicates all caches must be
# invalidated (i.e. cleared).
self.base_registry_signaling_sequence = 1
self.base_cache_signaling_sequence = 1
# Flag indicating if at least one model cache has been cleared.
# Useful only in a multi-process context.
self._any_cache_cleared = False
cr = self.db.cursor()
has_unaccent = openerp.modules.db.has_unaccent(cr)
if openerp.tools.config['unaccent'] and not has_unaccent:
@ -95,15 +110,16 @@ class Registry(object):
and registers them in the registry.
"""
res = []
models_to_load = [] # need to preserve loading order
# Instantiate registered classes (via the MetaModel automatic discovery
# or via explicit constructor call), and add them to the pool.
for cls in openerp.osv.orm.MetaModel.module_to_models.get(module.name, []):
res.append(cls.create_instance(self, cr))
return res
# models register themselves in self.models
model = cls.create_instance(self, cr)
if model._name not in models_to_load:
# avoid double-loading models whose declaration is split
models_to_load.append(model._name)
return [self.models[m] for m in models_to_load]
def schedule_cron_jobs(self):
""" Make the cron thread care about this registry/database jobs.
@ -112,7 +128,7 @@ class Registry(object):
monitor the ir.cron model for future jobs. See openerp.cron for
details.
"""
openerp.cron.schedule_wakeup(openerp.cron.WAKE_UP_NOW, self.db.dbname)
self.cron = True
def clear_caches(self):
""" Clear the caches
@ -121,6 +137,36 @@ class Registry(object):
"""
for model in self.models.itervalues():
model.clear_caches()
# Special case for ir_ui_menu which does not use openerp.tools.ormcache.
ir_ui_menu = self.models.get('ir.ui.menu')
if ir_ui_menu:
ir_ui_menu.clear_cache()
# Useful only in a multi-process context.
def reset_any_cache_cleared(self):
self._any_cache_cleared = False
# Useful only in a multi-process context.
def any_cache_cleared(self):
return self._any_cache_cleared
@classmethod
def setup_multi_process_signaling(cls, cr):
if not openerp.multi_process:
return
# Inter-process signaling:
# The `base_registry_signaling` sequence indicates the whole registry
# must be reloaded.
# The `base_cache_signaling sequence` indicates all caches must be
# invalidated (i.e. cleared).
cr.execute("""SELECT sequence_name FROM information_schema.sequences WHERE sequence_name='base_registry_signaling'""")
if not cr.fetchall():
cr.execute("""CREATE SEQUENCE base_registry_signaling INCREMENT BY 1 START WITH 1""")
cr.execute("""SELECT nextval('base_registry_signaling')""")
cr.execute("""CREATE SEQUENCE base_cache_signaling INCREMENT BY 1 START WITH 1""")
cr.execute("""SELECT nextval('base_cache_signaling')""")
@contextmanager
def cursor(self, auto_commit=True):
@ -182,6 +228,7 @@ class RegistryManager(object):
cr = registry.db.cursor()
try:
Registry.setup_multi_process_signaling(cr)
registry.do_parent_store(cr)
registry.get('ir.actions.report.xml').register_all(cr)
cr.commit()
@ -195,20 +242,11 @@ class RegistryManager(object):
@classmethod
def delete(cls, db_name):
"""Delete the registry linked to a given database.
This also cleans the associated caches. For good measure this also
cancels the associated cron job. But please note that the cron job can
be running and take some time before ending, and that you should not
remove a registry if it can still be used by some thread. So it might
be necessary to call yourself openerp.cron.Agent.cancel(db_name) and
and join (i.e. wait for) the thread.
"""
"""Delete the registry linked to a given database. """
with cls.registries_lock:
if db_name in cls.registries:
cls.registries[db_name].clear_caches()
del cls.registries[db_name]
openerp.cron.cancel(db_name)
@classmethod
def delete_all(cls):
@ -232,5 +270,71 @@ class RegistryManager(object):
if db_name in cls.registries:
cls.registries[db_name].clear_caches()
@classmethod
def check_registry_signaling(cls, db_name):
if openerp.multi_process and db_name in cls.registries:
registry = cls.get(db_name, pooljobs=False)
cr = registry.db.cursor()
try:
cr.execute("""
SELECT base_registry_signaling.last_value,
base_cache_signaling.last_value
FROM base_registry_signaling, base_cache_signaling""")
r, c = cr.fetchone()
# Check if the model registry must be reloaded (e.g. after the
# database has been updated by another process).
if registry.base_registry_signaling_sequence != r:
_logger.info("Reloading the model registry after database signaling.")
# Don't run the cron in the Gunicorn worker.
registry = cls.new(db_name, pooljobs=False)
registry.base_registry_signaling_sequence = r
# Check if the model caches must be invalidated (e.g. after a write
# occured on another process). Don't clear right after a registry
# has been reload.
elif registry.base_cache_signaling_sequence != c:
_logger.info("Invalidating all model caches after database signaling.")
registry.base_cache_signaling_sequence = c
registry.clear_caches()
registry.reset_any_cache_cleared()
# One possible reason caches have been invalidated is the
# use of decimal_precision.write(), in which case we need
# to refresh fields.float columns.
for model in registry.models.values():
for column in model._columns.values():
if hasattr(column, 'digits_change'):
column.digits_change(cr)
finally:
cr.close()
@classmethod
def signal_caches_change(cls, db_name):
if openerp.multi_process and db_name in cls.registries:
# Check the registries if any cache has been cleared and signal it
# through the database to other processes.
registry = cls.get(db_name, pooljobs=False)
if registry.any_cache_cleared():
_logger.info("At least one model cache has been cleared, signaling through the database.")
cr = registry.db.cursor()
r = 1
try:
cr.execute("select nextval('base_cache_signaling')")
r = cr.fetchone()[0]
finally:
cr.close()
registry.base_cache_signaling_sequence = r
registry.reset_any_cache_cleared()
@classmethod
def signal_registry_change(cls, db_name):
if openerp.multi_process and db_name in cls.registries:
registry = cls.get(db_name, pooljobs=False)
cr = registry.db.cursor()
r = 1
try:
cr.execute("select nextval('base_registry_signaling')")
r = cr.fetchone()[0]
finally:
cr.close()
registry.base_registry_signaling_sequence = r
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -20,6 +20,9 @@
#
##############################################################################
#.apidoc title: Common Services: netsvc
#.apidoc module-mods: member-order: bysource
import errno
import logging
import logging.handlers
@ -40,6 +43,7 @@ import openerp
_logger = logging.getLogger(__name__)
def close_socket(sock):
""" Closes a socket instance cleanly
@ -54,22 +58,18 @@ def close_socket(sock):
# of the other side (or something), see
# http://bugs.python.org/issue4397
# note: stdlib fixed test, not behavior
if e.errno != errno.ENOTCONN or platform.system() != 'Darwin':
if e.errno != errno.ENOTCONN or platform.system() not in ['Darwin', 'Windows']:
raise
sock.close()
#.apidoc title: Common Services: netsvc
#.apidoc module-mods: member-order: bysource
def abort_response(dummy_1, description, dummy_2, details):
# TODO Replace except_{osv,orm} with these directly.
raise openerp.osv.osv.except_osv(description, details)
class Service(object):
""" Base class for *Local* services
Functionality here is trusted, no authentication.
""" Base class for Local services
Functionality here is trusted, no authentication.
Workflow engine and reports subclass this.
"""
_services = {}
def __init__(self, name):
@ -145,7 +145,6 @@ class ColoredFormatter(DBFormatter):
record.levelname = COLOR_PATTERN % (30 + fg_color, 40 + bg_color, record.levelname)
return DBFormatter.format(self, record)
def init_logger():
from tools.translate import resetlocale
resetlocale()
@ -246,85 +245,6 @@ def init_alternative_logger():
logger.addHandler(handler)
logger.setLevel(logging.ERROR)
class Server:
""" Generic interface for all servers with an event loop etc.
Override this to impement http, net-rpc etc. servers.
Servers here must have threaded behaviour. start() must not block,
there is no run().
"""
__is_started = False
__servers = []
__starter_threads = []
# we don't want blocking server calls (think select()) to
# wait forever and possibly prevent exiting the process,
# but instead we want a form of polling/busy_wait pattern, where
# _server_timeout should be used as the default timeout for
# all I/O blocking operations
_busywait_timeout = 0.5
def __init__(self):
Server.__servers.append(self)
if Server.__is_started:
# raise Exception('All instances of servers must be inited before the startAll()')
# Since the startAll() won't be called again, allow this server to
# init and then start it after 1sec (hopefully). Register that
# timer thread in a list, so that we can abort the start if quitAll
# is called in the meantime
t = threading.Timer(1.0, self._late_start)
t.name = 'Late start timer for %s' % str(self.__class__)
Server.__starter_threads.append(t)
t.start()
def start(self):
_logger.debug("called stub Server.start")
def _late_start(self):
self.start()
for thr in Server.__starter_threads:
if thr.finished.is_set():
Server.__starter_threads.remove(thr)
def stop(self):
_logger.debug("called stub Server.stop")
def stats(self):
""" This function should return statistics about the server """
return "%s: No statistics" % str(self.__class__)
@classmethod
def startAll(cls):
if cls.__is_started:
return
_logger.info("Starting %d services" % len(cls.__servers))
for srv in cls.__servers:
srv.start()
cls.__is_started = True
@classmethod
def quitAll(cls):
if not cls.__is_started:
return
_logger.info("Stopping %d services" % len(cls.__servers))
for thr in cls.__starter_threads:
if not thr.finished.is_set():
thr.cancel()
cls.__starter_threads.remove(thr)
for srv in cls.__servers:
srv.stop()
cls.__is_started = False
@classmethod
def allStats(cls):
res = ["Servers %s" % ('stopped', 'started')[cls.__is_started]]
res.extend(srv.stats() for srv in cls.__servers)
return '\n'.join(res)
def _close_socket(self):
close_socket(self.socket)
def replace_request_password(args):
# password is always 3rd argument in a request, we replace it in RPC logs
# so it's easier to forward logs for diagnostics/debugging purposes...

File diff suppressed because it is too large Load Diff

View File

@ -27,6 +27,9 @@
Fields Attributes:
* _classic_read: is a classic sql fields
* _type : field type
* _auto_join: for one2many and many2one fields, tells whether select
queries will join the relational table instead of replacing the
field condition by an equivalent-one based on a search.
* readonly
* required
* size
@ -67,6 +70,7 @@ class _column(object):
"""
_classic_read = True
_classic_write = True
_auto_join = False
_prefetch = True
_properties = False
_type = 'unknown'
@ -163,7 +167,7 @@ class boolean(_column):
_logger.debug(
"required=True is deprecated: making a boolean field"
" `required` has no effect, as NULL values are "
"automatically turned into False.")
"automatically turned into False. args: %r",args)
class integer(_column):
_type = 'integer'
@ -427,9 +431,10 @@ class many2one(_column):
_symbol_f = lambda x: x or None
_symbol_set = (_symbol_c, _symbol_f)
def __init__(self, obj, string='unknown', **args):
def __init__(self, obj, string='unknown', auto_join=False, **args):
_column.__init__(self, string=string, **args)
self._obj = obj
self._auto_join = auto_join
def get(self, cr, obj, ids, name, user=None, context=None, values=None):
if context is None:
@ -496,11 +501,12 @@ class one2many(_column):
_prefetch = False
_type = 'one2many'
def __init__(self, obj, fields_id, string='unknown', limit=None, **args):
def __init__(self, obj, fields_id, string='unknown', limit=None, auto_join=False, **args):
_column.__init__(self, string=string, **args)
self._obj = obj
self._fields_id = fields_id
self._limit = limit
self._auto_join = auto_join
#one2many can't be used as condition for defaults
assert(self.change_default != True)
@ -662,7 +668,7 @@ class many2many(_column):
col1 = '%s_id' % source_model._table
if not col2:
col2 = '%s_id' % dest_model._table
return (tbl, col1, col2)
return tbl, col1, col2
def _get_query_and_where_params(self, cr, model, ids, values, where_params):
""" Extracted from ``get`` to facilitate fine-tuning of the generated
@ -1298,7 +1304,7 @@ class sparse(function):
def __init__(self, serialization_field, **kwargs):
self.serialization_field = serialization_field
return super(sparse, self).__init__(self._fnct_read, fnct_inv=self._fnct_write, multi='__sparse_multi', **kwargs)
super(sparse, self).__init__(self._fnct_read, fnct_inv=self._fnct_write, multi='__sparse_multi', **kwargs)
@ -1554,7 +1560,7 @@ class column_info(object):
def __str__(self):
return '%s(%s, %s, %s, %s, %s)' % (
self.__name__, self.name, self.column,
self.__class__.__name__, self.name, self.column,
self.parent_model, self.parent_column, self.original_parent)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

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