[MERGE] from trunk
bzr revid: rco@openerp.com-20121219130750-3ljqo3dyz05zww08
This commit is contained in:
@@ -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:
|
||||
Vendored
+3
@@ -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,
|
||||
|
||||
@@ -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
|
||||
----------
|
||||
|
||||
|
||||
@@ -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
|
||||
+++++++++++++++++++++++++
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
+11
-11
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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);
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
+287
-224
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||
"OpenERP’s 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
|
||||
|
||||
+203
-138
File diff suppressed because it is too large
Load Diff
+2685
-1370
File diff suppressed because it is too large
Load Diff
+169
-165
File diff suppressed because it is too large
Load Diff
+183
-51
@@ -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 l’application 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 l’enregistrement"
|
||||
#. 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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+234
-213
File diff suppressed because it is too large
Load Diff
+2848
-413
File diff suppressed because it is too large
Load Diff
+708
-326
File diff suppressed because it is too large
Load Diff
+692
-337
File diff suppressed because it is too large
Load Diff
+497
-58
File diff suppressed because it is too large
Load Diff
+303
-257
File diff suppressed because it is too large
Load Diff
+406
-366
File diff suppressed because it is too large
Load Diff
+288
-126
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+3418
-318
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from osv import fields,osv
|
||||
from openerp.osv import fields, osv
|
||||
|
||||
class ir_default(osv.osv):
|
||||
_name = 'ir.default'
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from osv import fields,osv
|
||||
from openerp.osv import fields,osv
|
||||
|
||||
|
||||
class ir_exports(osv.osv):
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import collections
|
||||
import datetime
|
||||
import functools
|
||||
import operator
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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)
|
||||
"""
|
||||
|
||||
@@ -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)])
|
||||
|
||||
@@ -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...">
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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...">
|
||||
|
||||
@@ -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())
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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')
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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__'
|
||||
|
||||
|
||||
@@ -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 """
|
||||
|
||||
@@ -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"""
|
||||
|
||||
@@ -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):
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -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 """
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import tools
|
||||
|
||||
import res_country
|
||||
import res_lang
|
||||
import res_partner
|
||||
|
||||
@@ -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
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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())
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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 = {}
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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,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,
|
||||
]
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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__':
|
||||
|
||||
@@ -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)
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:]
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
|
||||
-212
@@ -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:
|
||||
@@ -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).
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
+122
-18
@@ -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:
|
||||
|
||||
+8
-88
@@ -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...
|
||||
|
||||
+676
-283
File diff suppressed because it is too large
Load Diff
+12
-6
@@ -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
Reference in New Issue
Block a user