[MERGE] Merge with latest.
bzr revid: vba@tinyerp.com-20121212045613-bhhp0we60u643ckv
This commit is contained in:
@@ -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
|
||||
+266
-204
File diff suppressed because it is too large
Load Diff
+144
-92
@@ -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-09 23:26+0000\n"
|
||||
"Last-Translator: lambdasoftware <development@lambdasoftware.net>\n"
|
||||
"PO-Revision-Date: 2012-12-11 00:59+0000\n"
|
||||
"Last-Translator: Santi (Pexego) <santiago@pexego.es>\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-10 04:36+0000\n"
|
||||
"X-Generator: Launchpad (build 16341)\n"
|
||||
"X-Launchpad-Export-Date: 2012-12-12 04:38+0000\n"
|
||||
"X-Generator: Launchpad (build 16361)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
@@ -97,7 +97,7 @@ msgstr "Interfaz de pantalla táctil para tiendas"
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_l10n_in_hr_payroll
|
||||
msgid "Indian Payroll"
|
||||
msgstr ""
|
||||
msgstr "Nomina de la India"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.cron,model:0
|
||||
@@ -440,6 +440,8 @@ msgid ""
|
||||
"There is already a shared filter set as default for %(model)s, delete or "
|
||||
"change it before setting a new default"
|
||||
msgstr ""
|
||||
"Ya existe un filtro compartido por defecto para %(model)s, elimínelo o "
|
||||
"cámbielo antes de configurar uno nuevo"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/orm.py:2648
|
||||
@@ -591,7 +593,7 @@ msgstr "Guayana francesa"
|
||||
#. module: base
|
||||
#: model:ir.module.module,summary:base.module_hr
|
||||
msgid "Jobs, Departments, Employees Details"
|
||||
msgstr ""
|
||||
msgstr "Trabajos, departamentos y detalles de empleados"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_analytic
|
||||
@@ -607,6 +609,16 @@ msgid ""
|
||||
"that have no counterpart in the general financial accounts.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Módulo para definir el objeto contabilidad analítica\n"
|
||||
"===============================================\n"
|
||||
"\n"
|
||||
"En OpenERP, las cuentas analíticas están enlazadas con las cuentas "
|
||||
"generales\n"
|
||||
"pero se tratan de forma totalmente independiente. Por tanto, se pueden "
|
||||
"introducir\n"
|
||||
"operaciones analíticas sin contrapartida en la contabilidad financiera\n"
|
||||
" "
|
||||
|
||||
#. module: base
|
||||
#: field:ir.ui.view.custom,ref_id:0
|
||||
@@ -630,6 +642,26 @@ msgid ""
|
||||
"* Use emails to automatically confirm and send acknowledgements for any "
|
||||
"event registration\n"
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Organización y gestión de eventos.\n"
|
||||
"======================================\n"
|
||||
"\n"
|
||||
"EL módulo 'event' le permite organizar eventos y todas sus tareas "
|
||||
"relacionadas de forma eficiente: planificación,\n"
|
||||
"seguimiento del registro, asistencia, etc...\n"
|
||||
"\n"
|
||||
"Funcionalidades clave\n"
|
||||
"------------------------\n"
|
||||
"* Gestione sus eventos y registros\n"
|
||||
"* Use emails para confirmar y enviar automáticamente acuses de recibo para "
|
||||
"cada evento del registro\n"
|
||||
"\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
|
||||
@@ -916,7 +948,7 @@ msgstr "Seguridad y autenticación"
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_web_calendar
|
||||
msgid "Web Calendar"
|
||||
msgstr ""
|
||||
msgstr "Calendario web"
|
||||
|
||||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
@@ -1129,7 +1161,7 @@ msgstr "Otra licencia aprobada por OSI"
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_web_gantt
|
||||
msgid "Web Gantt"
|
||||
msgstr ""
|
||||
msgstr "Diagrama Gantt web"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.actions.act_window,name:base.act_menu_create
|
||||
@@ -1262,12 +1294,12 @@ msgstr "¡El nombre del país debe ser único!"
|
||||
#. module: base
|
||||
#: field:ir.module.module,installed_version:0
|
||||
msgid "Latest Version"
|
||||
msgstr ""
|
||||
msgstr "Última versión"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.rule:0
|
||||
msgid "Delete Access Right"
|
||||
msgstr ""
|
||||
msgstr "Eliminar derecho de acceso"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/ir/ir_mail_server.py:213
|
||||
@@ -1294,7 +1326,7 @@ msgstr "Islas Caimán"
|
||||
#. module: base
|
||||
#: view:ir.rule:0
|
||||
msgid "Record Rule"
|
||||
msgstr ""
|
||||
msgstr "Regla de registro"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.kr
|
||||
@@ -1423,7 +1455,7 @@ msgstr "Tests"
|
||||
#. module: base
|
||||
#: field:ir.actions.report.xml,attachment:0
|
||||
msgid "Save as Attachment Prefix"
|
||||
msgstr ""
|
||||
msgstr "Prefijo del adjunto al guardar como"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.ui.view_sc,res_id:0
|
||||
@@ -1460,7 +1492,7 @@ msgstr "Haití"
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_l10n_fr_hr_payroll
|
||||
msgid "French Payroll"
|
||||
msgstr ""
|
||||
msgstr "Nomina de Francia"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.ui.view:0
|
||||
@@ -1631,7 +1663,7 @@ msgstr "No existe un idioma con código \"%s\""
|
||||
#: model:ir.module.category,name:base.module_category_social_network
|
||||
#: model:ir.module.module,shortdesc:base.module_mail
|
||||
msgid "Social Network"
|
||||
msgstr ""
|
||||
msgstr "Red social"
|
||||
|
||||
#. module: base
|
||||
#: view:res.lang:0
|
||||
@@ -1641,12 +1673,12 @@ msgstr "%Y - Año con siglo."
|
||||
#. module: base
|
||||
#: view:res.company:0
|
||||
msgid "Report Footer Configuration"
|
||||
msgstr ""
|
||||
msgstr "Configuración de pie de informe"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.translation,comments:0
|
||||
msgid "Translation comments"
|
||||
msgstr ""
|
||||
msgstr "Comentarios de traducción"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_lunch
|
||||
@@ -1711,7 +1743,7 @@ msgstr ""
|
||||
#. module: base
|
||||
#: help:res.partner,website:0
|
||||
msgid "Website of Partner or Company"
|
||||
msgstr ""
|
||||
msgstr "Sitio web de la empresa o compañía"
|
||||
|
||||
#. module: base
|
||||
#: help:base.language.install,overwrite:0
|
||||
@@ -1758,7 +1790,7 @@ msgstr ""
|
||||
#. module: base
|
||||
#: model:ir.module.module,summary:base.module_sale
|
||||
msgid "Quotations, Sale Orders, Invoicing"
|
||||
msgstr ""
|
||||
msgstr "Presupuestos, órdenes de venta, facturación"
|
||||
|
||||
#. module: base
|
||||
#: field:res.users,login:0
|
||||
@@ -1909,6 +1941,7 @@ msgstr ""
|
||||
#, python-format
|
||||
msgid "Unknown value '%s' for boolean field '%%(field)s', assuming '%s'"
|
||||
msgstr ""
|
||||
"Valor desconocido '%s' para el campo booleano '%%(field)s', se asume '%s'"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.nl
|
||||
@@ -1923,7 +1956,7 @@ msgstr ""
|
||||
#. module: base
|
||||
#: selection:ir.translation,state:0
|
||||
msgid "Translation in Progress"
|
||||
msgstr ""
|
||||
msgstr "Traducción en curso"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_ir_rule
|
||||
@@ -1938,7 +1971,7 @@ msgstr "Días"
|
||||
#. module: base
|
||||
#: model:ir.module.module,summary:base.module_fleet
|
||||
msgid "Vehicle, leasing, insurances, costs"
|
||||
msgstr ""
|
||||
msgstr "Vehículo, leasing, seguros, costes"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.model.access:0
|
||||
@@ -1978,6 +2011,8 @@ msgid ""
|
||||
"Check this box if this contact is a supplier. If it's not checked, purchase "
|
||||
"people will not see it when encoding a purchase order."
|
||||
msgstr ""
|
||||
"Marque esta casilla si el contacto es un proveedor. Si no está marcada, el "
|
||||
"equipo de compras no lo verá cuando esté haciendo una orden de compra."
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_hr_evaluation
|
||||
@@ -2025,7 +2060,7 @@ msgstr "Crear tareas en OV"
|
||||
#: code:addons/base/ir/ir_model.py:316
|
||||
#, python-format
|
||||
msgid "This column contains module data and cannot be removed!"
|
||||
msgstr ""
|
||||
msgstr "¡Esta columna contiene datos del módulo y no puede ser eliminada!"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.attachment,res_model:0
|
||||
@@ -2170,7 +2205,7 @@ msgstr "Ruta completa"
|
||||
#. module: base
|
||||
#: view:base.language.export:0
|
||||
msgid "The next step depends on the file format:"
|
||||
msgstr ""
|
||||
msgstr "El siguiente paso depende del formato del archivo:"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_idea
|
||||
@@ -2215,7 +2250,7 @@ msgstr "Crear / Escribir / Copiar"
|
||||
#. module: base
|
||||
#: view:ir.sequence:0
|
||||
msgid "Second: %(sec)s"
|
||||
msgstr ""
|
||||
msgstr "Segundo: %(sec)s"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.actions.act_window,view_mode:0
|
||||
@@ -2602,7 +2637,7 @@ msgstr ""
|
||||
#. module: base
|
||||
#: field:res.company,rml_header1:0
|
||||
msgid "Company Slogan"
|
||||
msgstr ""
|
||||
msgstr "Lema de la empresa"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.bb
|
||||
@@ -2626,7 +2661,7 @@ msgstr ""
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_auth_oauth_signup
|
||||
msgid "Signup with OAuth2 Authentication"
|
||||
msgstr ""
|
||||
msgstr "Registro con autenticación OAuth2"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.model,state:0
|
||||
@@ -2653,7 +2688,7 @@ msgstr "Griego / Ελληνικά"
|
||||
#. module: base
|
||||
#: field:res.company,custom_footer:0
|
||||
msgid "Custom Footer"
|
||||
msgstr ""
|
||||
msgstr "Pie de página personalizado."
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_sale_crm
|
||||
@@ -3105,6 +3140,8 @@ msgid ""
|
||||
"the user will have an access to the sales configuration as well as statistic "
|
||||
"reports."
|
||||
msgstr ""
|
||||
"el usuario tendrá acceso a la configuración de ventas así como a los "
|
||||
"informes estadísticos."
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.nz
|
||||
@@ -3191,6 +3228,8 @@ msgid ""
|
||||
"the user will have an access to the human resources configuration as well as "
|
||||
"statistic reports."
|
||||
msgstr ""
|
||||
"el usuario tendrá acceso a la configuración de recursos humanos así como a "
|
||||
"los informes estadísticos."
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_web_shortcuts
|
||||
@@ -3300,7 +3339,7 @@ msgstr "Sweden"
|
||||
#. module: base
|
||||
#: field:ir.actions.report.xml,report_file:0
|
||||
msgid "Report File"
|
||||
msgstr ""
|
||||
msgstr "Archivo de informe"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.actions.act_window.view,view_mode:0
|
||||
@@ -3333,7 +3372,7 @@ msgstr ""
|
||||
#: code:addons/orm.py:3839
|
||||
#, python-format
|
||||
msgid "Missing document(s)"
|
||||
msgstr ""
|
||||
msgstr "Falta el documento(s)"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_res_partner_bank_type
|
||||
@@ -3348,6 +3387,8 @@ msgid ""
|
||||
"For more details about translating OpenERP in your language, please refer to "
|
||||
"the"
|
||||
msgstr ""
|
||||
"Para más información acerca de la traducción de OpenERP a su idioma, por "
|
||||
"favor contacta con"
|
||||
|
||||
#. module: base
|
||||
#: field:res.partner,image:0
|
||||
@@ -3634,6 +3675,7 @@ msgstr "Malta"
|
||||
msgid ""
|
||||
"Only users with the following access level are currently allowed to do that"
|
||||
msgstr ""
|
||||
"Sólo usuarios con los siguientes permisos están autorizados a hacer esto"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.actions.server,fields_lines:0
|
||||
@@ -3784,7 +3826,7 @@ msgstr "Interacción entre reglas"
|
||||
#: field:res.company,rml_footer:0
|
||||
#: field:res.company,rml_footer_readonly:0
|
||||
msgid "Report Footer"
|
||||
msgstr ""
|
||||
msgstr "Pie de página del informe"
|
||||
|
||||
#. module: base
|
||||
#: selection:res.lang,direction:0
|
||||
@@ -4305,7 +4347,7 @@ msgstr "Planes de cuentas"
|
||||
#. module: base
|
||||
#: model:ir.ui.menu,name:base.menu_event_main
|
||||
msgid "Events Organization"
|
||||
msgstr ""
|
||||
msgstr "Organización de eventos"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.actions.act_window,name:base.action_partner_customer_form
|
||||
@@ -4333,7 +4375,7 @@ msgstr "Campo base"
|
||||
#. module: base
|
||||
#: model:ir.module.category,name:base.module_category_managing_vehicles_and_contracts
|
||||
msgid "Managing vehicles and contracts"
|
||||
msgstr ""
|
||||
msgstr "Gestión de vehiculos y contratos"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_base_setup
|
||||
@@ -4459,7 +4501,7 @@ msgstr ""
|
||||
#. module: base
|
||||
#: field:res.partner,parent_id:0
|
||||
msgid "Related Company"
|
||||
msgstr ""
|
||||
msgstr "Empresa relacionada"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.actions.act_url,help:0
|
||||
@@ -4594,7 +4636,7 @@ msgstr "Hindi / हिंदी"
|
||||
#: model:ir.actions.act_window,name:base.action_view_base_language_install
|
||||
#: model:ir.ui.menu,name:base.menu_view_base_language_install
|
||||
msgid "Load a Translation"
|
||||
msgstr ""
|
||||
msgstr "Cargar una traducción"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.module.module,latest_version:0
|
||||
@@ -4925,7 +4967,7 @@ msgstr "Aplicaciones específicas de la industria"
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_google_docs
|
||||
msgid "Google Docs integration"
|
||||
msgstr ""
|
||||
msgstr "Integración con Google Docs"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/ir/ir_fields.py:328
|
||||
@@ -4987,7 +5029,7 @@ msgstr "Lesotho"
|
||||
#. module: base
|
||||
#: view:base.language.export:0
|
||||
msgid ", or your preferred text editor"
|
||||
msgstr ""
|
||||
msgstr ", o su editor de texto preferido"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_crm_partner_assign
|
||||
@@ -5595,7 +5637,7 @@ msgstr ""
|
||||
#. module: base
|
||||
#: model:res.groups,name:base.group_sale_salesman_all_leads
|
||||
msgid "See all Leads"
|
||||
msgstr ""
|
||||
msgstr "Ver todas las iniciativas"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.ci
|
||||
@@ -5615,7 +5657,7 @@ msgstr "%w - Día de la semana [0(domingo),6]."
|
||||
#. module: base
|
||||
#: model:ir.ui.menu,name:base.menu_ir_filters
|
||||
msgid "User-defined Filters"
|
||||
msgstr ""
|
||||
msgstr "Filtros definidos por el usuario"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.actions.act_window_close,name:0
|
||||
@@ -5835,7 +5877,7 @@ msgstr "Etiopía"
|
||||
#. module: base
|
||||
#: model:ir.module.category,name:base.module_category_authentication
|
||||
msgid "Authentication"
|
||||
msgstr ""
|
||||
msgstr "Autentificación"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sj
|
||||
@@ -5869,7 +5911,7 @@ msgstr "título"
|
||||
#: code:addons/base/ir/ir_fields.py:147
|
||||
#, python-format
|
||||
msgid "true"
|
||||
msgstr ""
|
||||
msgstr "verdadero"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_base_language_install
|
||||
@@ -5879,7 +5921,7 @@ msgstr "Instalar idioma"
|
||||
#. module: base
|
||||
#: model:res.partner.category,name:base.res_partner_category_11
|
||||
msgid "Services"
|
||||
msgstr ""
|
||||
msgstr "Servicios"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.translation:0
|
||||
@@ -6160,6 +6202,8 @@ msgstr ""
|
||||
msgid ""
|
||||
"Administrative divisions of a country. E.g. Fed. State, Departement, Canton"
|
||||
msgstr ""
|
||||
"División administrativa de un país. Por ejemplo Comunidad Autónoma, "
|
||||
"Provincia, Ayuntamiento"
|
||||
|
||||
#. module: base
|
||||
#: view:res.partner.bank:0
|
||||
@@ -6321,7 +6365,7 @@ msgstr ""
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_auth_reset_password
|
||||
msgid "Reset Password"
|
||||
msgstr ""
|
||||
msgstr "Restablecer contraseña"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.attachment:0
|
||||
@@ -6438,6 +6482,7 @@ msgstr "Cabo Verde"
|
||||
#: model:res.groups,comment:base.group_sale_salesman
|
||||
msgid "the user will have access to his own data in the sales application."
|
||||
msgstr ""
|
||||
"El usuario tendrá acceso a sus propios datos en la aplicación ventas."
|
||||
|
||||
#. module: base
|
||||
#: model:res.groups,comment:base.group_user
|
||||
@@ -6695,7 +6740,7 @@ msgstr "Israel"
|
||||
#: code:addons/base/res/res_config.py:444
|
||||
#, python-format
|
||||
msgid "Cannot duplicate configuration!"
|
||||
msgstr ""
|
||||
msgstr "¡No se puede duplicar la configuración!"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_l10n_syscohada
|
||||
@@ -6804,7 +6849,7 @@ msgstr ""
|
||||
#. module: base
|
||||
#: view:ir.sequence:0
|
||||
msgid "Week of the Year: %(woy)s"
|
||||
msgstr ""
|
||||
msgstr "Semana del año: %(woy)s"
|
||||
|
||||
#. module: base
|
||||
#: field:res.users,id:0
|
||||
@@ -6874,7 +6919,7 @@ msgstr "Títulos de empresa"
|
||||
#: code:addons/base/ir/ir_fields.py:228
|
||||
#, python-format
|
||||
msgid "Use the format '%s'"
|
||||
msgstr ""
|
||||
msgstr "Use el formato '%s'"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.actions.act_window,auto_refresh:0
|
||||
@@ -7219,7 +7264,7 @@ msgstr "Traducible"
|
||||
#. module: base
|
||||
#: help:base.language.import,code:0
|
||||
msgid "ISO Language and Country code, e.g. en_US"
|
||||
msgstr ""
|
||||
msgstr "Código ISO de idioma y país, ej. es_ES"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.vn
|
||||
@@ -7328,7 +7373,7 @@ msgstr ""
|
||||
#. module: base
|
||||
#: field:res.partner,function:0
|
||||
msgid "Job Position"
|
||||
msgstr ""
|
||||
msgstr "Puesto de trabajo"
|
||||
|
||||
#. module: base
|
||||
#: view:res.partner:0
|
||||
@@ -7900,7 +7945,7 @@ msgstr "Inicio"
|
||||
#: view:res.partner:0
|
||||
#: field:res.partner,user_id:0
|
||||
msgid "Salesperson"
|
||||
msgstr ""
|
||||
msgstr "Comercial"
|
||||
|
||||
#. module: base
|
||||
#: view:res.lang:0
|
||||
@@ -7965,7 +8010,7 @@ msgstr "Contraseña opcional para la autenticación SMTP"
|
||||
#: code:addons/base/ir/ir_model.py:719
|
||||
#, python-format
|
||||
msgid "Sorry, you are not allowed to modify this document."
|
||||
msgstr ""
|
||||
msgstr "Lo siento, no está autorizado para modificar este documento."
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/res/res_config.py:350
|
||||
@@ -8310,7 +8355,7 @@ msgstr "Bélgica - Contabilidad"
|
||||
#. module: base
|
||||
#: view:ir.model.access:0
|
||||
msgid "Access Control"
|
||||
msgstr ""
|
||||
msgstr "Control de acceso"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.kw
|
||||
@@ -8778,7 +8823,7 @@ msgstr ""
|
||||
#. module: base
|
||||
#: view:base.language.export:0
|
||||
msgid "documentation"
|
||||
msgstr ""
|
||||
msgstr "documentación"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.model,osv_memory:0
|
||||
@@ -8823,7 +8868,7 @@ msgstr "%b - Nombre abreviado del mes."
|
||||
#: code:addons/base/ir/ir_model.py:721
|
||||
#, python-format
|
||||
msgid "Sorry, you are not allowed to delete this document."
|
||||
msgstr ""
|
||||
msgstr "Lo siento, no está autorizado a eliminar este documento."
|
||||
|
||||
#. module: base
|
||||
#: constraint:ir.rule:0
|
||||
@@ -8974,12 +9019,12 @@ msgstr ""
|
||||
#. module: base
|
||||
#: model:res.partner.category,name:base.res_partner_category_5
|
||||
msgid "Silver"
|
||||
msgstr ""
|
||||
msgstr "Plata"
|
||||
|
||||
#. module: base
|
||||
#: field:res.partner.title,shortcut:0
|
||||
msgid "Abbreviation"
|
||||
msgstr ""
|
||||
msgstr "Abreviatura"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.ui.menu,name:base.menu_crm_case_job_req_main
|
||||
@@ -9182,7 +9227,7 @@ msgstr ""
|
||||
#. module: base
|
||||
#: model:ir.module.category,name:base.module_category_warehouse_management
|
||||
msgid "Warehouse"
|
||||
msgstr ""
|
||||
msgstr "Almacén"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.exports,resource:0
|
||||
@@ -9215,7 +9260,7 @@ msgstr "8. %I:%M:%S %p ==> 06:25:20 PM"
|
||||
#. module: base
|
||||
#: view:ir.filters:0
|
||||
msgid "Filters shared with all users"
|
||||
msgstr ""
|
||||
msgstr "Filtros compartidos con todos los usuarios"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.translation:0
|
||||
@@ -9445,7 +9490,7 @@ msgstr "Modelos"
|
||||
#: code:addons/base/module/module.py:472
|
||||
#, python-format
|
||||
msgid "The `base` module cannot be uninstalled"
|
||||
msgstr ""
|
||||
msgstr "El modulo `base`no puede ser desinstalado"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/ir/ir_cron.py:390
|
||||
@@ -9472,7 +9517,7 @@ msgstr "osv_memory.autovacuum"
|
||||
#: code:addons/base/ir/ir_model.py:720
|
||||
#, python-format
|
||||
msgid "Sorry, you are not allowed to create this kind of document."
|
||||
msgstr ""
|
||||
msgstr "Lo siento, no estás autorizado a crear este tipo de documento."
|
||||
|
||||
#. module: base
|
||||
#: field:base.language.export,lang:0
|
||||
@@ -9888,7 +9933,7 @@ msgstr ""
|
||||
#. module: base
|
||||
#: field:res.partner,use_parent_address:0
|
||||
msgid "Use Company Address"
|
||||
msgstr ""
|
||||
msgstr "Utilizar la dirección de la empresa"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,summary:base.module_hr_holidays
|
||||
@@ -10252,7 +10297,7 @@ msgstr "Isla Pitcairn"
|
||||
#. module: base
|
||||
#: field:res.partner,category_id:0
|
||||
msgid "Tags"
|
||||
msgstr ""
|
||||
msgstr "Etiquetas"
|
||||
|
||||
#. module: base
|
||||
#: view:base.module.upgrade:0
|
||||
@@ -10272,7 +10317,7 @@ msgstr "Reglas de registros"
|
||||
#. module: base
|
||||
#: view:multi_company.default:0
|
||||
msgid "Multi Company"
|
||||
msgstr ""
|
||||
msgstr "Multi compañía"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.category,name:base.module_category_portal
|
||||
@@ -10289,7 +10334,7 @@ msgstr ""
|
||||
#: code:addons/base/ir/ir_fields.py:295
|
||||
#, python-format
|
||||
msgid "See all possible values"
|
||||
msgstr ""
|
||||
msgstr "Ver todos los posibles valores"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_claim_from_delivery
|
||||
@@ -10344,7 +10389,7 @@ msgstr "Guinea Bissau"
|
||||
#. module: base
|
||||
#: field:ir.actions.report.xml,header:0
|
||||
msgid "Add RML Header"
|
||||
msgstr ""
|
||||
msgstr "Añadir cabecera RML"
|
||||
|
||||
#. module: base
|
||||
#: help:res.company,rml_footer:0
|
||||
@@ -10556,6 +10601,8 @@ msgstr "ir.traduccion"
|
||||
msgid ""
|
||||
"Please contact your system administrator if you think this is an error."
|
||||
msgstr ""
|
||||
"Por favor, contacte con el administrador del sistema si piensa que esto es "
|
||||
"un error."
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/module/module.py:519
|
||||
@@ -10752,7 +10799,7 @@ msgstr ""
|
||||
#. module: base
|
||||
#: help:res.partner,ean13:0
|
||||
msgid "BarCode"
|
||||
msgstr ""
|
||||
msgstr "Código de Barras"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.model.fields,model_id:0
|
||||
@@ -10851,7 +10898,7 @@ msgstr "No puede eliminar el idioma que está actualmente activo!"
|
||||
#: code:addons/base/ir/ir_model.py:1023
|
||||
#, python-format
|
||||
msgid "Permission Denied"
|
||||
msgstr ""
|
||||
msgstr "Permiso denegado"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.ui.menu,child_id:0
|
||||
@@ -11089,7 +11136,7 @@ msgstr "Árabe / الْعَرَبيّة"
|
||||
#. module: base
|
||||
#: selection:ir.translation,state:0
|
||||
msgid "Translated"
|
||||
msgstr ""
|
||||
msgstr "Traducido"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.actions.act_window,name:base.action_inventory_form
|
||||
@@ -11210,7 +11257,7 @@ msgstr ""
|
||||
#. module: base
|
||||
#: view:ir.filters:0
|
||||
msgid "Shared"
|
||||
msgstr ""
|
||||
msgstr "Compartido"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/module/module.py:336
|
||||
@@ -11288,6 +11335,9 @@ msgid ""
|
||||
"Allow users to sign up through OAuth2 Provider.\n"
|
||||
"===============================================\n"
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Permite registrar a los usuarios a través de un proveedor OAuth2.\n"
|
||||
"===============================================\n"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.cron:0
|
||||
@@ -11344,7 +11394,7 @@ msgstr "Granada"
|
||||
#. module: base
|
||||
#: help:res.partner,customer:0
|
||||
msgid "Check this box if this contact is a customer."
|
||||
msgstr ""
|
||||
msgstr "Marque esta casilla si el contacto es un cliente."
|
||||
|
||||
#. module: base
|
||||
#: view:ir.actions.server:0
|
||||
@@ -11380,7 +11430,7 @@ msgstr ""
|
||||
#. module: base
|
||||
#: field:res.users,partner_id:0
|
||||
msgid "Related Partner"
|
||||
msgstr ""
|
||||
msgstr "Empresa relacionada"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/osv.py:151
|
||||
@@ -11962,7 +12012,7 @@ msgstr "Ref. usuario"
|
||||
#: code:addons/base/ir/ir_fields.py:227
|
||||
#, python-format
|
||||
msgid "'%s' does not seem to be a valid datetime for field '%%(field)s'"
|
||||
msgstr ""
|
||||
msgstr "'%s' no parece una fecha valida para el campo '%%(field)s'"
|
||||
|
||||
#. module: base
|
||||
#: model:res.partner.bank.type.field,name:base.bank_normal_field_bic
|
||||
@@ -12116,7 +12166,7 @@ msgstr "Información de la conexión"
|
||||
#. module: base
|
||||
#: model:res.partner.title,name:base.res_partner_title_prof
|
||||
msgid "Professor"
|
||||
msgstr ""
|
||||
msgstr "Profesor"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.hm
|
||||
@@ -12146,7 +12196,7 @@ msgstr ""
|
||||
#. module: base
|
||||
#: field:res.users,login_date:0
|
||||
msgid "Latest connection"
|
||||
msgstr ""
|
||||
msgstr "Última conexión"
|
||||
|
||||
#. module: base
|
||||
#: field:res.groups,implied_ids:0
|
||||
@@ -12229,7 +12279,7 @@ msgstr "Binario"
|
||||
#. module: base
|
||||
#: model:res.partner.title,name:base.res_partner_title_doctor
|
||||
msgid "Doctor"
|
||||
msgstr ""
|
||||
msgstr "Doctor"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_mrp_repair
|
||||
@@ -12286,7 +12336,7 @@ msgstr "Otras empresas"
|
||||
#: view:workflow.workitem:0
|
||||
#: field:workflow.workitem,state:0
|
||||
msgid "Status"
|
||||
msgstr ""
|
||||
msgstr "Estado"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.actions.act_window,name:base.action_currency_form
|
||||
@@ -12298,7 +12348,7 @@ msgstr "Monedas"
|
||||
#. module: base
|
||||
#: model:res.partner.category,name:base.res_partner_category_8
|
||||
msgid "Consultancy Services"
|
||||
msgstr ""
|
||||
msgstr "Servicios de consultoría"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.values,value:0
|
||||
@@ -12413,7 +12463,7 @@ msgstr "Abastecimientos"
|
||||
#. module: base
|
||||
#: model:res.partner.category,name:base.res_partner_category_6
|
||||
msgid "Bronze"
|
||||
msgstr ""
|
||||
msgstr "Bronce"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_hr_payroll_account
|
||||
@@ -12443,12 +12493,12 @@ msgstr "Mes de creación"
|
||||
#. module: base
|
||||
#: field:ir.module.module,demo:0
|
||||
msgid "Demo Data"
|
||||
msgstr ""
|
||||
msgstr "Datos de ejemplo"
|
||||
|
||||
#. module: base
|
||||
#: model:res.partner.title,shortcut:base.res_partner_title_mister
|
||||
msgid "Mr."
|
||||
msgstr ""
|
||||
msgstr "Sr."
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.mv
|
||||
@@ -12636,7 +12686,7 @@ msgstr "BANCO"
|
||||
#: model:ir.module.category,name:base.module_category_point_of_sale
|
||||
#: model:ir.module.module,shortdesc:base.module_point_of_sale
|
||||
msgid "Point of Sale"
|
||||
msgstr ""
|
||||
msgstr "Terminal punto de venta"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_mail
|
||||
@@ -12944,7 +12994,7 @@ msgstr "Chipre"
|
||||
#. module: base
|
||||
#: field:res.users,new_password:0
|
||||
msgid "Set Password"
|
||||
msgstr ""
|
||||
msgstr "Establecer contraseña"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.actions.server,subject:0
|
||||
@@ -13413,7 +13463,7 @@ msgstr "Zaire"
|
||||
#. module: base
|
||||
#: model:ir.module.module,summary:base.module_project
|
||||
msgid "Projects, Tasks"
|
||||
msgstr ""
|
||||
msgstr "Proyectos, tareas"
|
||||
|
||||
#. module: base
|
||||
#: field:workflow.instance,res_id:0
|
||||
@@ -13431,7 +13481,7 @@ msgstr "Información"
|
||||
#: code:addons/base/ir/ir_fields.py:147
|
||||
#, python-format
|
||||
msgid "false"
|
||||
msgstr ""
|
||||
msgstr "falso"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_analytic_analysis
|
||||
@@ -13739,7 +13789,7 @@ msgstr "Actividad destino"
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_project_issue
|
||||
msgid "Issue Tracker"
|
||||
msgstr ""
|
||||
msgstr "Seguimiento de errores"
|
||||
|
||||
#. module: base
|
||||
#: view:base.module.update:0
|
||||
@@ -13819,6 +13869,8 @@ msgid ""
|
||||
"Check this to define the report footer manually. Otherwise it will be "
|
||||
"filled in automatically."
|
||||
msgstr ""
|
||||
"Marque si quiere definir el pie de informe manualmente. En otro caso se "
|
||||
"rellenará automáticamente."
|
||||
|
||||
#. module: base
|
||||
#: view:res.partner:0
|
||||
@@ -14008,7 +14060,7 @@ msgstr "Configuración del sistema realizada."
|
||||
#: view:ir.config_parameter:0
|
||||
#: model:ir.ui.menu,name:base.ir_config_menu
|
||||
msgid "System Parameters"
|
||||
msgstr ""
|
||||
msgstr "Parámetros del sistema"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_l10n_ch
|
||||
@@ -14077,7 +14129,7 @@ msgstr "Acción en múltiples doc."
|
||||
#: model:ir.actions.act_window,name:base.action_partner_title_partner
|
||||
#: model:ir.ui.menu,name:base.menu_partner_title_partner
|
||||
msgid "Titles"
|
||||
msgstr ""
|
||||
msgstr "Títulos"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_anonymization
|
||||
@@ -14218,7 +14270,7 @@ msgstr ""
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_base_gengo
|
||||
msgid "Automated Translations through Gengo API"
|
||||
msgstr ""
|
||||
msgstr "Traducción automática a través de Gengo"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_account_payment
|
||||
@@ -14378,12 +14430,12 @@ msgstr "Tasa monetaria"
|
||||
#: view:base.module.upgrade:0
|
||||
#: field:base.module.upgrade,module_info:0
|
||||
msgid "Modules to Update"
|
||||
msgstr ""
|
||||
msgstr "Módulos a actualizar"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.ui.menu,name:base.menu_custom_multicompany
|
||||
msgid "Multi-Companies"
|
||||
msgstr ""
|
||||
msgstr "Multi-Compañías"
|
||||
|
||||
#. module: base
|
||||
#: field:workflow,osv:0
|
||||
@@ -14499,7 +14551,7 @@ msgstr "Uso de la acción"
|
||||
#. module: base
|
||||
#: field:ir.module.module,name:0
|
||||
msgid "Technical Name"
|
||||
msgstr ""
|
||||
msgstr "Nombre técnico"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_workflow_workitem
|
||||
@@ -14550,7 +14602,7 @@ msgstr "Alemania - Contabilidad"
|
||||
#. module: base
|
||||
#: view:ir.sequence:0
|
||||
msgid "Day of the Year: %(doy)s"
|
||||
msgstr ""
|
||||
msgstr "Día del año: %(doy)s"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.ui.menu,web_icon:0
|
||||
@@ -14820,7 +14872,7 @@ msgstr "Servicio de Post-venta"
|
||||
#. module: base
|
||||
#: field:base.language.import,code:0
|
||||
msgid "ISO Code"
|
||||
msgstr ""
|
||||
msgstr "Código ISO"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_l10n_fr
|
||||
@@ -14835,7 +14887,7 @@ msgstr "Lanzar"
|
||||
#. module: base
|
||||
#: selection:res.partner,type:0
|
||||
msgid "Shipping"
|
||||
msgstr ""
|
||||
msgstr "Envío"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_project_mrp
|
||||
@@ -15382,7 +15434,7 @@ msgstr "Seychelles"
|
||||
#. module: base
|
||||
#: model:res.partner.category,name:base.res_partner_category_4
|
||||
msgid "Gold"
|
||||
msgstr ""
|
||||
msgstr "Oro"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/res/res_company.py:159
|
||||
|
||||
+478
-271
File diff suppressed because it is too large
Load Diff
+762
-95
File diff suppressed because it is too large
Load Diff
+235
-165
File diff suppressed because it is too large
Load Diff
+296
-250
File diff suppressed because it is too large
Load Diff
@@ -7,14 +7,14 @@ msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 5.0.0\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2012-12-03 16:01+0000\n"
|
||||
"PO-Revision-Date: 2012-12-07 10:59+0000\n"
|
||||
"Last-Translator: Rui Franco (multibase.pt) <Unknown>\n"
|
||||
"PO-Revision-Date: 2012-12-11 10:27+0000\n"
|
||||
"Last-Translator: Virgílio Oliveira <virgilio.oliveira@multibase.pt>\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-08 04:58+0000\n"
|
||||
"X-Generator: Launchpad (build 16341)\n"
|
||||
"X-Launchpad-Export-Date: 2012-12-12 04:38+0000\n"
|
||||
"X-Generator: Launchpad (build 16361)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
@@ -525,7 +525,7 @@ msgstr ""
|
||||
#. module: base
|
||||
#: field:ir.model.relation,name:0
|
||||
msgid "Relation Name"
|
||||
msgstr ""
|
||||
msgstr "Nome da relação"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.rule:0
|
||||
@@ -693,7 +693,7 @@ msgstr "Colômbia"
|
||||
#. module: base
|
||||
#: model:res.partner.title,name:base.res_partner_title_mister
|
||||
msgid "Mister"
|
||||
msgstr ""
|
||||
msgstr "Sr."
|
||||
|
||||
#. module: base
|
||||
#: help:res.country,code:0
|
||||
@@ -1008,7 +1008,7 @@ msgstr "Preferências de email"
|
||||
#: code:addons/base/ir/ir_fields.py:196
|
||||
#, python-format
|
||||
msgid "'%s' does not seem to be a valid date for field '%%(field)s'"
|
||||
msgstr ""
|
||||
msgstr "%s' não parece ser uma data válida para o campo '%%(field)s'"
|
||||
|
||||
#. module: base
|
||||
#: view:res.partner:0
|
||||
@@ -1025,6 +1025,8 @@ msgstr "Zimbabué"
|
||||
msgid ""
|
||||
"Type of the constraint: `f` for a foreign key, `u` for other constraints."
|
||||
msgstr ""
|
||||
"Tipo de restrição: `f` para uma chave estrangeira, `u` para outras "
|
||||
"restrições."
|
||||
|
||||
#. module: base
|
||||
#: view:ir.actions.report.xml:0
|
||||
@@ -1621,7 +1623,7 @@ msgstr "Configuração do rodapé de relatório"
|
||||
#. module: base
|
||||
#: field:ir.translation,comments:0
|
||||
msgid "Translation comments"
|
||||
msgstr ""
|
||||
msgstr "Comentários à tradução"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_lunch
|
||||
@@ -1686,7 +1688,7 @@ msgstr ""
|
||||
#. module: base
|
||||
#: help:res.partner,website:0
|
||||
msgid "Website of Partner or Company"
|
||||
msgstr ""
|
||||
msgstr "Sítio web do parceiro ou da empresa"
|
||||
|
||||
#. module: base
|
||||
#: help:base.language.install,overwrite:0
|
||||
@@ -1892,7 +1894,7 @@ msgstr "Países Baixos"
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_portal_event
|
||||
msgid "Portal Event"
|
||||
msgstr ""
|
||||
msgstr "Evento do portal"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.translation,state:0
|
||||
@@ -2189,7 +2191,7 @@ msgstr "Criar / Escrever / Copiar"
|
||||
#. module: base
|
||||
#: view:ir.sequence:0
|
||||
msgid "Second: %(sec)s"
|
||||
msgstr ""
|
||||
msgstr "Segundo: %(sec)s"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.actions.act_window,view_mode:0
|
||||
@@ -2946,7 +2948,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' não aparenta ser um número inteiro para o campo '%%(field)s'"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.category,description:base.module_category_report_designer
|
||||
@@ -3826,7 +3828,7 @@ msgstr "Togo"
|
||||
#: field:ir.actions.act_window,res_model:0
|
||||
#: field:ir.actions.client,res_model:0
|
||||
msgid "Destination Model"
|
||||
msgstr ""
|
||||
msgstr "Modelo de destino"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.sequence,implementation:0
|
||||
@@ -4400,7 +4402,7 @@ msgstr ""
|
||||
#. module: base
|
||||
#: field:res.partner,parent_id:0
|
||||
msgid "Related Company"
|
||||
msgstr ""
|
||||
msgstr "Empresa relacionada"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.actions.act_url,help:0
|
||||
@@ -4835,7 +4837,7 @@ msgstr "Workflows"
|
||||
#. module: base
|
||||
#: model:ir.ui.menu,name:base.next_id_73
|
||||
msgid "Purchase"
|
||||
msgstr ""
|
||||
msgstr "Compra"
|
||||
|
||||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
@@ -5310,7 +5312,7 @@ msgstr "Separador Decimal"
|
||||
#: code:addons/orm.py:5245
|
||||
#, python-format
|
||||
msgid "Missing required value for the field '%s'."
|
||||
msgstr ""
|
||||
msgstr "Falta o valor do campo '%s'."
|
||||
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_res_partner_address
|
||||
@@ -5374,7 +5376,7 @@ msgstr "Ilha Bouvet"
|
||||
#. module: base
|
||||
#: field:ir.model.constraint,type:0
|
||||
msgid "Constraint Type"
|
||||
msgstr ""
|
||||
msgstr "Tipo de restrição"
|
||||
|
||||
#. module: base
|
||||
#: field:res.company,child_ids:0
|
||||
@@ -5537,7 +5539,7 @@ msgstr "%w - Número de dias da semana [0(Sunday),6]."
|
||||
#. module: base
|
||||
#: model:ir.ui.menu,name:base.menu_ir_filters
|
||||
msgid "User-defined Filters"
|
||||
msgstr ""
|
||||
msgstr "Filtros definidos pelo utilizador"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.actions.act_window_close,name:0
|
||||
@@ -6951,7 +6953,7 @@ msgstr "Ficheiro do módulo importado com sucesso!"
|
||||
#: view:ir.model.constraint:0
|
||||
#: model:ir.ui.menu,name:base.ir_model_constraint_menu
|
||||
msgid "Model Constraints"
|
||||
msgstr ""
|
||||
msgstr "Restrições de modelo"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_hr_timesheet
|
||||
@@ -7720,7 +7722,7 @@ msgstr "Canadá"
|
||||
#. module: base
|
||||
#: view:base.language.export:0
|
||||
msgid "Launchpad"
|
||||
msgstr ""
|
||||
msgstr "Launchpad"
|
||||
|
||||
#. module: base
|
||||
#: help:res.currency.rate,currency_rate_type_id:0
|
||||
@@ -7890,7 +7892,7 @@ msgstr "Assistente"
|
||||
#: code:addons/base/ir/ir_fields.py:304
|
||||
#, python-format
|
||||
msgid "database id"
|
||||
msgstr ""
|
||||
msgstr "id da base de dados"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_base_import
|
||||
@@ -8460,7 +8462,7 @@ msgstr "Nepal"
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_document_page
|
||||
msgid "Document Page"
|
||||
msgstr ""
|
||||
msgstr "Página do documento"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_l10n_ar
|
||||
@@ -8650,6 +8652,10 @@ msgid ""
|
||||
"\n"
|
||||
"(Document type: %s)"
|
||||
msgstr ""
|
||||
"Para este tipo de documento, só pode aceder a registos que tenham sido "
|
||||
"criados por si.\n"
|
||||
"\n"
|
||||
"(Tipo de documento: %s)"
|
||||
|
||||
#. module: base
|
||||
#: view:base.language.export:0
|
||||
@@ -8722,7 +8728,7 @@ msgstr "Multi Ações"
|
||||
#. module: base
|
||||
#: model:ir.module.module,summary:base.module_mail
|
||||
msgid "Discussions, Mailing Lists, News"
|
||||
msgstr ""
|
||||
msgstr "Discussões, listas de correio, notícias"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_fleet
|
||||
@@ -9085,7 +9091,7 @@ msgstr "8. %I:%M:%S %p ==> 06:25:20 PM"
|
||||
#. module: base
|
||||
#: view:ir.filters:0
|
||||
msgid "Filters shared with all users"
|
||||
msgstr ""
|
||||
msgstr "Filtros partilhados com todos os utilizadores"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.translation:0
|
||||
@@ -9495,7 +9501,7 @@ msgstr ""
|
||||
#: code:addons/base/ir/ir_fields.py:317
|
||||
#, python-format
|
||||
msgid "external id"
|
||||
msgstr ""
|
||||
msgstr "id externo"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.model:0
|
||||
@@ -10027,7 +10033,7 @@ msgstr "res.request"
|
||||
#. module: base
|
||||
#: field:res.partner,image_medium:0
|
||||
msgid "Medium-sized image"
|
||||
msgstr ""
|
||||
msgstr "Imagem de média dimensão"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.model:0
|
||||
@@ -10185,7 +10191,7 @@ msgstr ""
|
||||
#. module: base
|
||||
#: help:res.company,rml_footer:0
|
||||
msgid "Footer text displayed at the bottom of all reports."
|
||||
msgstr ""
|
||||
msgstr "Texto de rodapé mostrados no final de todos os relatórios."
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_note_pad
|
||||
@@ -10608,7 +10614,7 @@ msgstr "Tipos de Sequências"
|
||||
#: code:addons/base/res/res_bank.py:195
|
||||
#, python-format
|
||||
msgid "Formating Error"
|
||||
msgstr ""
|
||||
msgstr "Erro de formatação"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.ye
|
||||
@@ -10930,7 +10936,7 @@ msgstr "Domínio"
|
||||
#: code:addons/base/ir/ir_fields.py:167
|
||||
#, python-format
|
||||
msgid "Use '1' for yes and '0' for no"
|
||||
msgstr ""
|
||||
msgstr "Use '1' para sim e '0' para não"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_marketing_campaign
|
||||
@@ -11172,7 +11178,7 @@ msgstr ""
|
||||
#. module: base
|
||||
#: field:res.users,partner_id:0
|
||||
msgid "Related Partner"
|
||||
msgstr ""
|
||||
msgstr "Parceiro relacionado"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/osv.py:151
|
||||
@@ -11897,7 +11903,7 @@ msgstr "Informação da Ligação"
|
||||
#. module: base
|
||||
#: model:res.partner.title,name:base.res_partner_title_prof
|
||||
msgid "Professor"
|
||||
msgstr ""
|
||||
msgstr "Professor"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.hm
|
||||
@@ -12193,7 +12199,7 @@ msgstr "Aquisições"
|
||||
#. module: base
|
||||
#: model:res.partner.category,name:base.res_partner_category_6
|
||||
msgid "Bronze"
|
||||
msgstr ""
|
||||
msgstr "Bronze"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_hr_payroll_account
|
||||
@@ -12223,7 +12229,7 @@ msgstr "Mês de Criação"
|
||||
#. module: base
|
||||
#: field:ir.module.module,demo:0
|
||||
msgid "Demo Data"
|
||||
msgstr ""
|
||||
msgstr "Dados de demonstração"
|
||||
|
||||
#. module: base
|
||||
#: model:res.partner.title,shortcut:base.res_partner_title_mister
|
||||
@@ -12383,7 +12389,7 @@ msgstr ""
|
||||
#. module: base
|
||||
#: field:ir.actions.report.xml,report_sxw:0
|
||||
msgid "SXW Path"
|
||||
msgstr ""
|
||||
msgstr "Caminho de SXW"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_asset
|
||||
@@ -12416,7 +12422,7 @@ msgstr "BANCO"
|
||||
#: model:ir.module.category,name:base.module_category_point_of_sale
|
||||
#: model:ir.module.module,shortdesc:base.module_point_of_sale
|
||||
msgid "Point of Sale"
|
||||
msgstr ""
|
||||
msgstr "Ponto de venda"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_mail
|
||||
@@ -13180,7 +13186,7 @@ msgstr "Zaire"
|
||||
#. module: base
|
||||
#: model:ir.module.module,summary:base.module_project
|
||||
msgid "Projects, Tasks"
|
||||
msgstr ""
|
||||
msgstr "Projetos, tarefas"
|
||||
|
||||
#. module: base
|
||||
#: field:workflow.instance,res_id:0
|
||||
@@ -13198,7 +13204,7 @@ msgstr "Informação"
|
||||
#: code:addons/base/ir/ir_fields.py:147
|
||||
#, python-format
|
||||
msgid "false"
|
||||
msgstr ""
|
||||
msgstr "falso"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_analytic_analysis
|
||||
@@ -13594,7 +13600,7 @@ msgstr "Instalar Módulos"
|
||||
#. module: base
|
||||
#: model:ir.ui.menu,name:base.menu_import_crm
|
||||
msgid "Import & Synchronize"
|
||||
msgstr ""
|
||||
msgstr "Importar e sincronizar"
|
||||
|
||||
#. module: base
|
||||
#: view:res.partner:0
|
||||
@@ -13839,7 +13845,7 @@ msgstr "Ação em multiplos documentos."
|
||||
#: model:ir.actions.act_window,name:base.action_partner_title_partner
|
||||
#: model:ir.ui.menu,name:base.menu_partner_title_partner
|
||||
msgid "Titles"
|
||||
msgstr ""
|
||||
msgstr "Títulos"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_anonymization
|
||||
@@ -13900,7 +13906,7 @@ msgstr "Luxemburgo"
|
||||
#. module: base
|
||||
#: model:ir.module.module,summary:base.module_base_calendar
|
||||
msgid "Personal & Shared Calendar"
|
||||
msgstr ""
|
||||
msgstr "Calendários personalizado e partilhado"
|
||||
|
||||
#. module: base
|
||||
#: selection:res.request,priority:0
|
||||
@@ -14022,7 +14028,7 @@ msgstr "Dicas & Oportunidades"
|
||||
#. module: base
|
||||
#: model:res.country,name:base.gg
|
||||
msgid "Guernsey"
|
||||
msgstr ""
|
||||
msgstr "Guernsey"
|
||||
|
||||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
@@ -14364,17 +14370,17 @@ msgstr ""
|
||||
#. module: base
|
||||
#: view:base.language.export:0
|
||||
msgid "Export Settings"
|
||||
msgstr ""
|
||||
msgstr "Configurações de exportação"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.actions.act_window,src_model:0
|
||||
msgid "Source Model"
|
||||
msgstr ""
|
||||
msgstr "Modelo fonte"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.sequence:0
|
||||
msgid "Day of the Week (0:Monday): %(weekday)s"
|
||||
msgstr ""
|
||||
msgstr "Dia da semana (0:segunda feira): %(weekday)s"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/module/wizard/base_module_upgrade.py:84
|
||||
@@ -14589,7 +14595,7 @@ msgstr "Lançar"
|
||||
#. module: base
|
||||
#: selection:res.partner,type:0
|
||||
msgid "Shipping"
|
||||
msgstr ""
|
||||
msgstr "Envio"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_project_mrp
|
||||
@@ -14741,7 +14747,7 @@ msgstr "Módulos Genéricos"
|
||||
#. module: base
|
||||
#: model:res.country,name:base.mk
|
||||
msgid "Macedonia, the former Yugoslav Republic of"
|
||||
msgstr ""
|
||||
msgstr "Macedónia (Antiga República Jugoslava)"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.rw
|
||||
@@ -14878,7 +14884,7 @@ msgstr "Holanda - Contabilidade"
|
||||
#. module: base
|
||||
#: model:res.country,name:base.gs
|
||||
msgid "South Georgia and the South Sandwich Islands"
|
||||
msgstr ""
|
||||
msgstr "Geórgia do Sul e Ilhas Sandwich do Sul"
|
||||
|
||||
#. module: base
|
||||
#: view:res.lang:0
|
||||
@@ -15305,7 +15311,7 @@ msgstr "Parceiros: "
|
||||
#. module: base
|
||||
#: view:res.partner:0
|
||||
msgid "Is a Company?"
|
||||
msgstr ""
|
||||
msgstr "É uma empresa?"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/res/res_company.py:159
|
||||
@@ -15413,7 +15419,7 @@ msgstr "Russo / русский язык"
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_auth_signup
|
||||
msgid "Signup"
|
||||
msgstr ""
|
||||
msgstr "Registo"
|
||||
|
||||
#~ msgid "SMS - Gateway: clickatell"
|
||||
#~ msgstr "SMS - Gateway: clickatell"
|
||||
|
||||
@@ -7,15 +7,15 @@ msgstr ""
|
||||
"Project-Id-Version: pt_BR\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2012-12-03 16:01+0000\n"
|
||||
"PO-Revision-Date: 2012-12-07 22:22+0000\n"
|
||||
"PO-Revision-Date: 2012-12-11 11:52+0000\n"
|
||||
"Last-Translator: Fábio Martinelli - http://zupy.com.br "
|
||||
"<webmaster@guaru.net>\n"
|
||||
"Language-Team: <pt@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-08 04:58+0000\n"
|
||||
"X-Generator: Launchpad (build 16341)\n"
|
||||
"X-Launchpad-Export-Date: 2012-12-12 04:39+0000\n"
|
||||
"X-Generator: Launchpad (build 16361)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
@@ -255,6 +255,34 @@ msgid ""
|
||||
"* Planned Revenue by Stage and User (graph)\n"
|
||||
"* Opportunities by Stage (graph)\n"
|
||||
msgstr ""
|
||||
"\n"
|
||||
"CRM - Gestão de Relacionamento com o Cliente\n"
|
||||
"================================================== ===\n"
|
||||
"\n"
|
||||
"Este aplicativo permite que um grupo de pessoas gerencie de forma "
|
||||
"inteligente e eficiente prospectos, oportunidades, reuniões e telefonemas.\n"
|
||||
"\n"
|
||||
"Ele gerencia tarefas essenciais, como a comunicação, a identificação, "
|
||||
"priorização, atribuição, resolução e notificação.\n"
|
||||
"\n"
|
||||
"O OpenERP garante que todos os casos são rastreado com sucesso por usuários, "
|
||||
"clientes e fornecedores. É possível enviar automaticamente lembretes, "
|
||||
"escalar o pedido, acionar métodos específicos e muitas outras ações com base "
|
||||
"em suas próprias regras empresariais.\n"
|
||||
"\n"
|
||||
"A melhor coisa sobre este sistema é que os usuários não precisam fazer nada "
|
||||
"de especial. O módulo de CRM tem um gateway de e-mail para a interface de "
|
||||
"sincronização entre e-mails e OpenERP. Dessa forma, os usuários podem enviar "
|
||||
"e-mails apenas para o rastreador do pedido.\n"
|
||||
"\n"
|
||||
"O OpenERP vai cuidar de agradecê-los por sua mensagem, encaminhá-la para o "
|
||||
"pessoal apropriado automaticamente e certificar-se de que toda a futura "
|
||||
"correspondência chegue ao lugar certo.\n"
|
||||
"\n"
|
||||
"O Painel para CRM inclue:\n"
|
||||
"-----------------------------------\n"
|
||||
"* Receita planejada por estágio e usuário (gráfico)\n"
|
||||
"* Oportunidades por estágio (gráfico)\n"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/ir/ir_model.py:397
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
from osv import osv
|
||||
|
||||
|
||||
class ir_needaction_mixin(osv.AbstractModel):
|
||||
'''Mixin class for objects using the need action feature.
|
||||
|
||||
@@ -60,4 +61,5 @@ class ir_needaction_mixin(osv.AbstractModel):
|
||||
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)
|
||||
|
||||
@@ -52,7 +52,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 +130,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)
|
||||
|
||||
@@ -269,17 +269,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')
|
||||
@@ -290,7 +306,7 @@ 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'),
|
||||
'child_id': fields.one2many('ir.ui.menu', 'parent_id', 'Child IDs'),
|
||||
'parent_id': fields.many2one('ir.ui.menu', 'Parent Menu', 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. "\
|
||||
@@ -300,11 +316,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=[
|
||||
@@ -321,12 +340,12 @@ 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"
|
||||
|
||||
|
||||
@@ -370,16 +370,20 @@ 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)
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
+19
-2
@@ -20,6 +20,9 @@
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
#.apidoc title: Common Services: netsvc
|
||||
#.apidoc module-mods: member-order: bysource
|
||||
|
||||
import errno
|
||||
import logging
|
||||
import logging.handlers
|
||||
@@ -41,9 +44,23 @@ import openerp
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def close_socket(sock):
|
||||
""" Closes a socket instance cleanly
|
||||
|
||||
#.apidoc title: Common Services: netsvc
|
||||
#.apidoc module-mods: member-order: bysource
|
||||
:param sock: the network socket to close
|
||||
:type sock: socket.socket
|
||||
"""
|
||||
try:
|
||||
sock.shutdown(socket.SHUT_RDWR)
|
||||
except socket.error, e:
|
||||
# On OSX, socket shutdowns both sides if any side closes it
|
||||
# causing an error 57 'Socket is not connected' on shutdown
|
||||
# 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() not in ['Darwin', 'Windows']:
|
||||
raise
|
||||
sock.close()
|
||||
|
||||
def abort_response(dummy_1, description, dummy_2, details):
|
||||
# TODO Replace except_{osv,orm} with these directly.
|
||||
|
||||
+665
-278
File diff suppressed because it is too large
Load Diff
@@ -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'
|
||||
@@ -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)
|
||||
|
||||
|
||||
+38
-25
@@ -58,7 +58,6 @@ import types
|
||||
|
||||
import psycopg2
|
||||
from lxml import etree
|
||||
import warnings
|
||||
|
||||
import fields
|
||||
import openerp
|
||||
@@ -2721,22 +2720,17 @@ class BaseModel(object):
|
||||
|
||||
return result
|
||||
|
||||
def _inherits_join_add(self, current_table, parent_model_name, query):
|
||||
def _inherits_join_add(self, current_model, parent_model_name, query):
|
||||
"""
|
||||
Add missing table SELECT and JOIN clause to ``query`` for reaching the parent table (no duplicates)
|
||||
:param current_table: current model object
|
||||
:param current_model: current model object
|
||||
:param parent_model_name: name of the parent model for which the clauses should be added
|
||||
:param query: query object on which the JOIN should be added
|
||||
"""
|
||||
inherits_field = current_table._inherits[parent_model_name]
|
||||
inherits_field = current_model._inherits[parent_model_name]
|
||||
parent_model = self.pool.get(parent_model_name)
|
||||
parent_table_name = parent_model._table
|
||||
quoted_parent_table_name = '"%s"' % parent_table_name
|
||||
if quoted_parent_table_name not in query.tables:
|
||||
query.tables.append(quoted_parent_table_name)
|
||||
query.where_clause.append('(%s.%s = %s.id)' % (current_table._table, inherits_field, parent_table_name))
|
||||
|
||||
|
||||
parent_alias, parent_alias_statement = query.add_join((current_model._table, parent_model._table, inherits_field, 'id', inherits_field), implicit=True)
|
||||
return parent_alias
|
||||
|
||||
def _inherits_join_calc(self, field, query):
|
||||
"""
|
||||
@@ -2748,12 +2742,13 @@ class BaseModel(object):
|
||||
:return: qualified name of field, to be used in SELECT clause
|
||||
"""
|
||||
current_table = self
|
||||
parent_alias = '"%s"' % current_table._table
|
||||
while field in current_table._inherit_fields and not field in current_table._columns:
|
||||
parent_model_name = current_table._inherit_fields[field][0]
|
||||
parent_table = self.pool.get(parent_model_name)
|
||||
self._inherits_join_add(current_table, parent_model_name, query)
|
||||
parent_alias = self._inherits_join_add(current_table, parent_model_name, query)
|
||||
current_table = parent_table
|
||||
return '"%s".%s' % (current_table._table, field)
|
||||
return '%s."%s"' % (parent_alias, field)
|
||||
|
||||
def _parent_store_compute(self, cr):
|
||||
if not self._parent_store:
|
||||
@@ -4662,11 +4657,27 @@ class BaseModel(object):
|
||||
:param query: the current query object
|
||||
"""
|
||||
def apply_rule(added_clause, added_params, added_tables, parent_model=None, child_object=None):
|
||||
""" :param string parent_model: string of the parent model
|
||||
:param model child_object: model object, base of the rule application
|
||||
"""
|
||||
if added_clause:
|
||||
if parent_model and child_object:
|
||||
# as inherited rules are being applied, we need to add the missing JOIN
|
||||
# to reach the parent table (if it was not JOINed yet in the query)
|
||||
child_object._inherits_join_add(child_object, parent_model, query)
|
||||
parent_alias = child_object._inherits_join_add(child_object, parent_model, query)
|
||||
# inherited rules are applied on the external table -> need to get the alias and replace
|
||||
parent_table = self.pool.get(parent_model)._table
|
||||
added_clause = [clause.replace('"%s"' % parent_table, '"%s"' % parent_alias) for clause in added_clause]
|
||||
# change references to parent_table to parent_alias, because we now use the alias to refer to the table
|
||||
new_tables = []
|
||||
for table in added_tables:
|
||||
# table is just a table name -> switch to the full alias
|
||||
if table == '"%s"' % (parent_table):
|
||||
new_tables.append('"%s" as "%s"' % (parent_table, parent_alias))
|
||||
# table is already a full statement -> replace reference to the table to its alias, is correct with the way aliases are generated
|
||||
else:
|
||||
new_tables.append(table.replace('"%s"' % parent_table, '"%s"' % parent_alias))
|
||||
added_tables = new_tables
|
||||
query.where_clause += added_clause
|
||||
query.where_clause_params += added_params
|
||||
for table in added_tables:
|
||||
@@ -4677,12 +4688,14 @@ class BaseModel(object):
|
||||
|
||||
# apply main rules on the object
|
||||
rule_obj = self.pool.get('ir.rule')
|
||||
apply_rule(*rule_obj.domain_get(cr, uid, self._name, mode, context=context))
|
||||
rule_where_clause, rule_where_clause_params, rule_tables = rule_obj.domain_get(cr, uid, self._name, mode, context=context)
|
||||
apply_rule(rule_where_clause, rule_where_clause_params, rule_tables)
|
||||
|
||||
# apply ir.rules from the parents (through _inherits)
|
||||
for inherited_model in self._inherits:
|
||||
kwargs = dict(parent_model=inherited_model, child_object=self) #workaround for python2.5
|
||||
apply_rule(*rule_obj.domain_get(cr, uid, inherited_model, mode, context=context), **kwargs)
|
||||
rule_where_clause, rule_where_clause_params, rule_tables = rule_obj.domain_get(cr, uid, inherited_model, mode, context=context)
|
||||
apply_rule(rule_where_clause, rule_where_clause_params, rule_tables,
|
||||
parent_model=inherited_model, child_object=self)
|
||||
|
||||
def _generate_m2o_order_by(self, order_field, query):
|
||||
"""
|
||||
@@ -4717,17 +4730,16 @@ class BaseModel(object):
|
||||
# extract the field names, to be able to qualify them and add desc/asc
|
||||
m2o_order_list = []
|
||||
for order_part in m2o_order.split(","):
|
||||
m2o_order_list.append(order_part.strip().split(" ",1)[0].strip())
|
||||
m2o_order_list.append(order_part.strip().split(" ", 1)[0].strip())
|
||||
m2o_order = m2o_order_list
|
||||
|
||||
# Join the dest m2o table if it's not joined yet. We use [LEFT] OUTER join here
|
||||
# as we don't want to exclude results that have NULL values for the m2o
|
||||
src_table, src_field = qualified_field.replace('"','').split('.', 1)
|
||||
query.join((src_table, dest_model._table, src_field, 'id'), outer=True)
|
||||
qualify = lambda field: '"%s"."%s"' % (dest_model._table, field)
|
||||
src_table, src_field = qualified_field.replace('"', '').split('.', 1)
|
||||
dst_alias, dst_alias_statement = query.add_join((src_table, dest_model._table, src_field, 'id', src_field), implicit=False, outer=True)
|
||||
qualify = lambda field: '"%s"."%s"' % (dst_alias, field)
|
||||
return map(qualify, m2o_order) if isinstance(m2o_order, list) else qualify(m2o_order)
|
||||
|
||||
|
||||
def _generate_order_by(self, order_spec, query):
|
||||
"""
|
||||
Attempt to consruct an appropriate ORDER BY clause based on order_spec, which must be
|
||||
@@ -4735,7 +4747,8 @@ class BaseModel(object):
|
||||
|
||||
:raise" except_orm in case order_spec is malformed
|
||||
"""
|
||||
order_by_clause = self._order
|
||||
order_by_clause = ''
|
||||
order_spec = order_spec or self._order
|
||||
if order_spec:
|
||||
order_by_elements = []
|
||||
self._check_qorder(order_spec)
|
||||
@@ -4753,7 +4766,7 @@ class BaseModel(object):
|
||||
elif order_column._type == 'many2one':
|
||||
inner_clause = self._generate_m2o_order_by(order_field, query)
|
||||
else:
|
||||
continue # ignore non-readable or "non-joinable" fields
|
||||
continue # ignore non-readable or "non-joinable" fields
|
||||
elif order_field in self._inherit_fields:
|
||||
parent_obj = self.pool.get(self._inherit_fields[order_field][3])
|
||||
order_column = parent_obj._columns[order_field]
|
||||
@@ -4762,7 +4775,7 @@ class BaseModel(object):
|
||||
elif order_column._type == 'many2one':
|
||||
inner_clause = self._generate_m2o_order_by(order_field, query)
|
||||
else:
|
||||
continue # ignore non-readable or "non-joinable" fields
|
||||
continue # ignore non-readable or "non-joinable" fields
|
||||
if inner_clause:
|
||||
if isinstance(inner_clause, list):
|
||||
for clause in inner_clause:
|
||||
|
||||
+61
-25
@@ -21,6 +21,7 @@
|
||||
|
||||
#.apidoc title: Query object
|
||||
|
||||
|
||||
def _quote(to_quote):
|
||||
if '"' not in to_quote:
|
||||
return '"%s"' % to_quote
|
||||
@@ -67,15 +68,35 @@ class Query(object):
|
||||
# LEFT JOIN "table_c" ON ("table_a"."table_a_col2" = "table_c"."table_c_col")
|
||||
self.joins = joins or {}
|
||||
|
||||
def join(self, connection, outer=False):
|
||||
"""Adds the JOIN specified in ``connection``.
|
||||
def _get_table_aliases(self):
|
||||
from openerp.osv.expression import get_alias_from_query
|
||||
return [get_alias_from_query(from_statement)[1] for from_statement in self.tables]
|
||||
|
||||
:param connection: a tuple ``(lhs, table, lhs_col, col)``.
|
||||
The join corresponds to the SQL equivalent of::
|
||||
def _get_alias_mapping(self):
|
||||
from openerp.osv.expression import get_alias_from_query
|
||||
mapping = {}
|
||||
for table in self.tables:
|
||||
alias, statement = get_alias_from_query(table)
|
||||
mapping[statement] = table
|
||||
return mapping
|
||||
|
||||
(lhs.lhs_col = table.col)
|
||||
def add_join(self, connection, implicit=True, outer=False):
|
||||
""" Join a destination table to the current table.
|
||||
|
||||
:param outer: True if a LEFT OUTER JOIN should be used, if possible
|
||||
:param implicit: False if the join is an explicit join. This allows
|
||||
to fall back on the previous implementation of ``join`` before
|
||||
OpenERP 7.0. It therefore adds the JOIN specified in ``connection``
|
||||
If True, the join is done implicitely, by adding the table alias
|
||||
in the from clause and the join condition in the where clause
|
||||
of the query. Implicit joins do not handle outer parameter.
|
||||
:param connection: a tuple ``(lhs, table, lhs_col, col, link)``.
|
||||
The join corresponds to the SQL equivalent of::
|
||||
|
||||
(lhs.lhs_col = table.col)
|
||||
|
||||
Note that all connection elements are strings. Please refer to expression.py for more details about joins.
|
||||
|
||||
:param outer: True if a LEFT OUTER JOIN should be used, if possible
|
||||
(no promotion to OUTER JOIN is supported in case the JOIN
|
||||
was already present in the query, as for the moment
|
||||
implicit INNER JOINs are only connected from NON-NULL
|
||||
@@ -83,38 +104,53 @@ class Query(object):
|
||||
``_inherits`` or when a domain criterion explicitly
|
||||
adds filtering)
|
||||
"""
|
||||
(lhs, table, lhs_col, col) = connection
|
||||
lhs = _quote(lhs)
|
||||
table = _quote(table)
|
||||
assert lhs in self.tables, "Left-hand-side table must already be part of the query!"
|
||||
if table in self.tables:
|
||||
# already joined, must ignore (promotion to outer and multiple joins not supported yet)
|
||||
pass
|
||||
from openerp.osv.expression import generate_table_alias
|
||||
(lhs, table, lhs_col, col, link) = connection
|
||||
alias, alias_statement = generate_table_alias(lhs, [(table, link)])
|
||||
|
||||
if implicit:
|
||||
if alias_statement not in self.tables:
|
||||
self.tables.append(alias_statement)
|
||||
condition = '("%s"."%s" = "%s"."%s")' % (lhs, lhs_col, alias, col)
|
||||
self.where_clause.append(condition)
|
||||
else:
|
||||
# already joined
|
||||
pass
|
||||
return alias, alias_statement
|
||||
else:
|
||||
# add JOIN
|
||||
self.tables.append(table)
|
||||
self.joins.setdefault(lhs, []).append((table, lhs_col, col, outer and 'LEFT JOIN' or 'JOIN'))
|
||||
return self
|
||||
aliases = self._get_table_aliases()
|
||||
assert lhs in aliases, "Left-hand-side table %s must already be part of the query tables %s!" % (lhs, str(self.tables))
|
||||
if alias_statement in self.tables:
|
||||
# already joined, must ignore (promotion to outer and multiple joins not supported yet)
|
||||
pass
|
||||
else:
|
||||
# add JOIN
|
||||
self.tables.append(alias_statement)
|
||||
self.joins.setdefault(lhs, []).append((alias, lhs_col, col, outer and 'LEFT JOIN' or 'JOIN'))
|
||||
return alias, alias_statement
|
||||
|
||||
def get_sql(self):
|
||||
"""Returns (query_from, query_where, query_params)"""
|
||||
""" Returns (query_from, query_where, query_params). """
|
||||
from openerp.osv.expression import get_alias_from_query
|
||||
query_from = ''
|
||||
tables_to_process = list(self.tables)
|
||||
alias_mapping = self._get_alias_mapping()
|
||||
|
||||
def add_joins_for_table(table, query_from):
|
||||
for (dest_table, lhs_col, col, join) in self.joins.get(table,[]):
|
||||
tables_to_process.remove(dest_table)
|
||||
query_from += ' %s %s ON (%s."%s" = %s."%s")' % \
|
||||
(join, dest_table, table, lhs_col, dest_table, col)
|
||||
for (dest_table, lhs_col, col, join) in self.joins.get(table, []):
|
||||
tables_to_process.remove(alias_mapping[dest_table])
|
||||
query_from += ' %s %s ON ("%s"."%s" = "%s"."%s")' % \
|
||||
(join, alias_mapping[dest_table], table, lhs_col, dest_table, col)
|
||||
query_from = add_joins_for_table(dest_table, query_from)
|
||||
return query_from
|
||||
|
||||
for table in tables_to_process:
|
||||
query_from += table
|
||||
if table in self.joins:
|
||||
query_from = add_joins_for_table(table, query_from)
|
||||
table_alias = get_alias_from_query(table)[1]
|
||||
if table_alias in self.joins:
|
||||
query_from = add_joins_for_table(table_alias, query_from)
|
||||
query_from += ','
|
||||
query_from = query_from[:-1] # drop last comma
|
||||
query_from = query_from[:-1] # drop last comma
|
||||
return (query_from, " AND ".join(self.where_clause), self.where_clause_params)
|
||||
|
||||
def __str__(self):
|
||||
|
||||
@@ -22,8 +22,6 @@
|
||||
#.apidoc title: NET-RPC Server
|
||||
|
||||
""" This file contains instance of the net-rpc server
|
||||
|
||||
|
||||
"""
|
||||
import logging
|
||||
import select
|
||||
@@ -38,24 +36,6 @@ import openerp.tools as tools
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
def close_socket(sock):
|
||||
""" Closes a socket instance cleanly
|
||||
|
||||
:param sock: the network socket to close
|
||||
:type sock: socket.socket
|
||||
"""
|
||||
try:
|
||||
sock.shutdown(socket.SHUT_RDWR)
|
||||
except socket.error, e:
|
||||
# On OSX, socket shutdowns both sides if any side closes it
|
||||
# causing an error 57 'Socket is not connected' on shutdown
|
||||
# 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() not in ['Darwin', 'Windows']:
|
||||
raise
|
||||
sock.close()
|
||||
|
||||
class Server:
|
||||
""" Generic interface for all servers with an event loop etc.
|
||||
Override this to impement http, net-rpc etc. servers.
|
||||
@@ -133,7 +113,7 @@ class Server:
|
||||
return '\n'.join(res)
|
||||
|
||||
def _close_socket(self):
|
||||
close_socket(self.socket)
|
||||
netsvc.close_socket(self.socket)
|
||||
|
||||
class TinySocketClientThread(threading.Thread):
|
||||
def __init__(self, sock, threads):
|
||||
|
||||
@@ -302,10 +302,10 @@ class Worker(object):
|
||||
self.multi.pipe_ping(self.watchdog_pipe)
|
||||
self.sleep()
|
||||
self.process_work()
|
||||
_logger.info("Worker (%s) exiting...",self.pid)
|
||||
_logger.info("Worker (%s) exiting. request_count: %s.", self.pid, self.request_count)
|
||||
self.stop()
|
||||
except Exception,e:
|
||||
_logger.exception("Worker (%s) Exception occured, exiting..."%self.pid)
|
||||
_logger.exception("Worker (%s) Exception occured, exiting..." % self.pid)
|
||||
# should we use 3 to abort everything ?
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2010 OpenERP S.A. http://www.openerp.com
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from test_osv import *
|
||||
from test_translate import *
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
@@ -8,9 +8,10 @@ Tests can be explicitely added to the `fast_suite` or `checks` lists or not.
|
||||
See the :ref:`test-framework` section in the :ref:`features` list.
|
||||
"""
|
||||
|
||||
from . import test_expression, test_mail, test_ir_sequence, test_orm,\
|
||||
from . import test_expression, test_mail, test_ir_sequence, test_orm, \
|
||||
test_fields, test_basecase, \
|
||||
test_view_validation, test_uninstall, test_misc, test_db_cursor
|
||||
test_view_validation, test_uninstall, test_misc, test_db_cursor, \
|
||||
test_osv, test_translate
|
||||
from . import test_ir_filters
|
||||
|
||||
fast_suite = [
|
||||
@@ -27,6 +28,8 @@ checks = [
|
||||
test_basecase,
|
||||
test_view_validation,
|
||||
test_misc,
|
||||
test_osv,
|
||||
test_translate,
|
||||
]
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -2,11 +2,12 @@ import unittest2
|
||||
|
||||
import openerp
|
||||
|
||||
|
||||
class test_domain_normalization(unittest2.TestCase):
|
||||
def test_normalize_domain(self):
|
||||
expression = openerp.osv.expression
|
||||
norm_domain = domain = ['&',(1,'=',1),('a','=','b')]
|
||||
assert norm_domain == expression.normalize(domain), "Normalized domains should be left untouched"
|
||||
domain = [('x','in',['y','z']),('a.v','=','e'),'|','|',('a','=','b'),'!',('c','>','d'),('e','!=','f'),('g','=','h')]
|
||||
norm_domain = ['&','&','&'] + domain
|
||||
assert norm_domain == expression.normalize(domain), "Non-normalized domains should be properly normalized"
|
||||
norm_domain = domain = ['&', (1, '=', 1), ('a', '=', 'b')]
|
||||
assert norm_domain == expression.normalize_domain(domain), "Normalized domains should be left untouched"
|
||||
domain = [('x', 'in', ['y', 'z']), ('a.v', '=', 'e'), '|', '|', ('a', '=', 'b'), '!', ('c', '>', 'd'), ('e', '!=', 'f'), ('g', '=', 'h')]
|
||||
norm_domain = ['&', '&', '&'] + domain
|
||||
assert norm_domain == expression.normalize_domain(domain), "Non-normalized domains should be properly normalized"
|
||||
|
||||
@@ -22,45 +22,45 @@
|
||||
import unittest
|
||||
from openerp.osv.query import Query
|
||||
|
||||
|
||||
class QueryTestCase(unittest.TestCase):
|
||||
|
||||
def test_basic_query(self):
|
||||
query = Query()
|
||||
query.tables.extend(['"product_product"','"product_template"'])
|
||||
query.tables.extend(['"product_product"', '"product_template"'])
|
||||
query.where_clause.append("product_product.template_id = product_template.id")
|
||||
query.join(("product_template", "product_category", "categ_id", "id"), outer=False) # add normal join
|
||||
query.join(("product_product", "res_user", "user_id", "id"), outer=True) # outer join
|
||||
query.add_join(("product_template", "product_category", "categ_id", "id", "categ_id"), implicit=False, outer=False) # add normal join
|
||||
query.add_join(("product_product", "res_user", "user_id", "id", "user_id"), implicit=False, outer=True) # outer join
|
||||
self.assertEquals(query.get_sql()[0].strip(),
|
||||
""""product_product" LEFT JOIN "res_user" ON ("product_product"."user_id" = "res_user"."id"),"product_template" JOIN "product_category" ON ("product_template"."categ_id" = "product_category"."id") """.strip())
|
||||
""""product_product" LEFT JOIN "res_user" as "product_product__user_id" ON ("product_product"."user_id" = "product_product__user_id"."id"),"product_template" JOIN "product_category" as "product_template__categ_id" ON ("product_template"."categ_id" = "product_template__categ_id"."id") """.strip())
|
||||
self.assertEquals(query.get_sql()[1].strip(), """product_product.template_id = product_template.id""".strip())
|
||||
|
||||
def test_query_chained_explicit_joins(self):
|
||||
query = Query()
|
||||
query.tables.extend(['"product_product"','"product_template"'])
|
||||
query.tables.extend(['"product_product"', '"product_template"'])
|
||||
query.where_clause.append("product_product.template_id = product_template.id")
|
||||
query.join(("product_template", "product_category", "categ_id", "id"), outer=False) # add normal join
|
||||
query.join(("product_category", "res_user", "user_id", "id"), outer=True) # CHAINED outer join
|
||||
query.add_join(("product_template", "product_category", "categ_id", "id", "categ_id"), implicit=False, outer=False) # add normal join
|
||||
query.add_join(("product_template__categ_id", "res_user", "user_id", "id", "user_id"), implicit=False, outer=True) # CHAINED outer join
|
||||
self.assertEquals(query.get_sql()[0].strip(),
|
||||
""""product_product","product_template" JOIN "product_category" ON ("product_template"."categ_id" = "product_category"."id") LEFT JOIN "res_user" ON ("product_category"."user_id" = "res_user"."id")""".strip())
|
||||
""""product_product","product_template" JOIN "product_category" as "product_template__categ_id" ON ("product_template"."categ_id" = "product_template__categ_id"."id") LEFT JOIN "res_user" as "product_template__categ_id__user_id" ON ("product_template__categ_id"."user_id" = "product_template__categ_id__user_id"."id")""".strip())
|
||||
self.assertEquals(query.get_sql()[1].strip(), """product_product.template_id = product_template.id""".strip())
|
||||
|
||||
def test_mixed_query_chained_explicit_implicit_joins(self):
|
||||
query = Query()
|
||||
query.tables.extend(['"product_product"','"product_template"'])
|
||||
query.tables.extend(['"product_product"', '"product_template"'])
|
||||
query.where_clause.append("product_product.template_id = product_template.id")
|
||||
query.join(("product_template", "product_category", "categ_id", "id"), outer=False) # add normal join
|
||||
query.join(("product_category", "res_user", "user_id", "id"), outer=True) # CHAINED outer join
|
||||
query.add_join(("product_template", "product_category", "categ_id", "id", "categ_id"), implicit=False, outer=False) # add normal join
|
||||
query.add_join(("product_template__categ_id", "res_user", "user_id", "id", "user_id"), implicit=False, outer=True) # CHAINED outer join
|
||||
query.tables.append('"account.account"')
|
||||
query.where_clause.append("product_category.expense_account_id = account_account.id") # additional implicit join
|
||||
query.where_clause.append("product_category.expense_account_id = account_account.id") # additional implicit join
|
||||
self.assertEquals(query.get_sql()[0].strip(),
|
||||
""""product_product","product_template" JOIN "product_category" ON ("product_template"."categ_id" = "product_category"."id") LEFT JOIN "res_user" ON ("product_category"."user_id" = "res_user"."id"),"account.account" """.strip())
|
||||
""""product_product","product_template" JOIN "product_category" as "product_template__categ_id" ON ("product_template"."categ_id" = "product_template__categ_id"."id") LEFT JOIN "res_user" as "product_template__categ_id__user_id" ON ("product_template__categ_id"."user_id" = "product_template__categ_id__user_id"."id"),"account.account" """.strip())
|
||||
self.assertEquals(query.get_sql()[1].strip(), """product_product.template_id = product_template.id AND product_category.expense_account_id = account_account.id""".strip())
|
||||
|
||||
|
||||
def test_raise_missing_lhs(self):
|
||||
query = Query()
|
||||
query.tables.append('"product_product"')
|
||||
self.assertRaises(AssertionError, query.join, ("product_template", "product_category", "categ_id", "id"), outer=False)
|
||||
self.assertRaises(AssertionError, query.add_join, ("product_template", "product_category", "categ_id", "id", "categ_id"), implicit=False, outer=False)
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
+17
-5
@@ -36,6 +36,7 @@ def image_resize_image(base64_source, size=(1024, 1024), encoding='base64', file
|
||||
filled with transparent background. The image will not be stretched if
|
||||
smaller than the expected size.
|
||||
Steps of the resizing:
|
||||
- Compute width and height if not specified.
|
||||
- if avoid_if_small: if both image sizes are smaller than the requested
|
||||
sizes, the original image is returned. This is used to avoid adding
|
||||
transparent content around images that we do not want to alter but
|
||||
@@ -46,14 +47,14 @@ def image_resize_image(base64_source, size=(1024, 1024), encoding='base64', file
|
||||
function. Aspect ratios are preserved when using it. Note that if the
|
||||
source image is smaller than the expected size, it will not be
|
||||
extended, but filled to match the size.
|
||||
- create a transparent background that will hold the final
|
||||
image.
|
||||
- past the thumbnail on the transparent background and center
|
||||
it.
|
||||
- create a transparent background that will hold the final image.
|
||||
- paste the thumbnail on the transparent background and center it.
|
||||
|
||||
:param base64_source: base64-encoded version of the source
|
||||
image; if False, returns False
|
||||
:param size: tuple(height, width)
|
||||
:param size: 2-tuple(width, height). A None value for any of width or
|
||||
height mean an automatically computed value based respectivelly
|
||||
on height or width of the source image.
|
||||
:param encoding: the output encoding
|
||||
:param filetype: the output filetype
|
||||
:param avoid_if_small: do not resize if image height and width
|
||||
@@ -61,8 +62,19 @@ def image_resize_image(base64_source, size=(1024, 1024), encoding='base64', file
|
||||
"""
|
||||
if not base64_source:
|
||||
return False
|
||||
if size == (None, None):
|
||||
return base64_source
|
||||
|
||||
image_stream = io.BytesIO(base64_source.decode(encoding))
|
||||
image = Image.open(image_stream)
|
||||
|
||||
asked_width, asked_height = size
|
||||
if asked_width is None:
|
||||
asked_width = int(image.size[0] * (float(asked_height) / image.size[1]))
|
||||
if asked_height is None:
|
||||
asked_height = int(image.size[1] * (float(asked_width) / image.size[0]))
|
||||
size = asked_width, asked_height
|
||||
|
||||
# check image size: do not create a thumbnail if avoiding smaller images
|
||||
if avoid_if_small and image.size[0] <= size[0] and image.size[1] <= size[1]:
|
||||
return base64_source
|
||||
|
||||
Reference in New Issue
Block a user