[MERGE] Merge with latest.

bzr revid: vba@tinyerp.com-20121212045613-bhhp0we60u643ckv
This commit is contained in:
Vijaykumar Baladaniya
2012-12-12 10:26:13 +05:30
29 changed files with 3652 additions and 1569 deletions
+1
View File
@@ -10,3 +10,4 @@ Miscellanous
06_misc_need_action_specs.rst
06_misc_user_img_specs.rst
06_misc_import.rst
06_misc_auto_join.rst
+75
View File
@@ -0,0 +1,75 @@
.. _performing_joins_in_select:
Perfoming joins in select
=========================
.. versionadded:: 7.0
Starting with OpenERP 7.0, an ``auto_join`` attribute is added on *many2one* and
*one2many* fields. The purpose is to allow the automatic generation of joins in
select queries. This attribute is set to False by default, therefore not changing
the default behavior. Please note that we consider this feature as still experimental
and should be used only if you understand its limitations and targets.
Without ``_auto_join``, the behavior of expression.parse() is the same as before.
Leafs holding a path beginning with many2one or one2many fields perform a search
on the relational table. The result is then used to replace the leaf content.
For example, if you have on res.partner a domain like ``[('bank_ids.name',
'like', 'foo')]`` with bank_ids linking to res.partner.bank, 3 queries will be
performed :
- 1 on res_partner_bank, with domain ``[('name', '=', 'foo')]``, that returns a
list of res.partner.bank ids (bids)
- 1 on res_partner, with a domain ``['bank_ids', 'in', bids)]``, that returns a
list of res.partner ids (pids)
- 1 on res_partner, with a domain ``[('id', 'in', pids)]``
When the ``auto_join`` attribute is True on a relational field, the destination
table will be joined to produce only one query.
- the relational table is accessed using an alias: ``'"res_partner_bank"
as res_partner__bank_ids``. The alias is generated using the relational field
name. This allows to have multiple joins with different join conditions on the
same table, depending on the domain.
- there is a join condition between the destination table and the main table:
``res_partner__bank_ids."partner_id"=res_partner."id"``
- the condition is then written on the relational table:
``res_partner__bank_ids."name" = 'foo'``
This manipulation is performed in expression.parse(). It checks leafs that
contain a path, i.e. any domain containing a '.'. It then checks whether the
first item of the path is a *many2one* or *one2many* field with the ``auto_join``
attribute set. If set, it adds a join query and recursively analyzes the
remaining of the leaf, using the same behavior. If the remaining path also holds
a path with auto_join fields, it will add all tables and add every necessary
join conditions.
Chaining joins allows to reduce the number of queries performed, and to avoid
having too long equivalent leaf replacement in domains. Indeed, the internal
queries produced by this behavior can be very costly, because they were generally
select queries without limit that could lead to huge ('id', 'in', [...])
leafs to analyze and execute.
Some limitations exist on this feature that limits its current use as of version
7.0. **This feature is therefore considered as experimental, and used
to speedup some precise bottlenecks in OpenERP**.
List of known issues and limitations:
- using ``auto_join`` bypasses the business logic; no name search is performed,
only direct matches between ids using join conditions
- ir.rules are not taken into account when analyzing and adding the join
conditions
List of already-supported corner cases :
- one2many fields having a domain attribute. Static domains as well as dynamic
domain are supported
- auto_join leading to functional searchable fields
Typical use in OpenERP 7.0:
- in mail module: notification_ids field on mail_message, allowing to speedup
the display of the various mailboxes
- in mail module: message_ids field on mail_thread, allowing to speedup the
display of needaction counters and documents having unread messages
File diff suppressed because it is too large Load Diff
+144 -92
View File
@@ -7,14 +7,14 @@ msgstr ""
"Project-Id-Version: OpenERP Server 5.0.4\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2012-12-03 16:01+0000\n"
"PO-Revision-Date: 2012-12-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
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+55 -49
View File
@@ -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"
+31 -3
View File
@@ -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
+3 -1
View File
@@ -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)
+2 -2
View File
@@ -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)
+31 -12
View File
@@ -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"
+8 -4
View File
@@ -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)
+367 -2
View File
@@ -1,11 +1,33 @@
import unittest2
from openerp.osv.orm import BaseModel
import openerp.tests.common as common
class test_expression(common.TransactionCase):
def test_in_not_in_m2m(self):
def _reinit_mock(self):
self.query_list = list()
def _mock_base_model_where_calc(self, model, *args, **kwargs):
""" Mock build_email to be able to test its values. Store them into
some internal variable for latter processing. """
self.query_list.append(self._base_model_where_calc(model, *args, **kwargs))
# return the lastly stored query, the one the ORM wants to perform
return self.query_list[-1]
def setUp(self):
super(test_expression, self).setUp()
# Mock BaseModel._where_calc(), to be able to proceed to some tests about generated expression
self._reinit_mock()
self._base_model_where_calc = BaseModel._where_calc
BaseModel._where_calc = lambda model, cr, uid, args, context: self._mock_base_model_where_calc(model, cr, uid, args, context)
def tearDown(self):
# Remove mocks
BaseModel._where_calc = self._base_model_where_calc
super(test_expression, self).tearDown()
def test_00_in_not_in_m2m(self):
registry, cr, uid = self.registry, self.cr, self.uid
# Create 4 partners with no category, or one or two categories (out of two categories).
@@ -70,3 +92,346 @@ class test_expression(common.TransactionCase):
# self.assertTrue(a not in with_any_other_than_a, "Search for category_id with any other than cat_a failed (1).")
# self.assertTrue(ab in with_any_other_than_a, "Search for category_id with any other than cat_a failed (2).")
def test_10_expression_parse(self):
# TDE note: those tests have been added when refactoring the expression.parse() method.
# They come in addition to the already existing test_osv_expression.yml; maybe some tests
# will be a bit redundant
registry, cr, uid = self.registry, self.cr, self.uid
users_obj = registry('res.users')
# Create users
a = users_obj.create(cr, uid, {'name': 'test_A', 'login': 'test_A'})
b1 = users_obj.create(cr, uid, {'name': 'test_B', 'login': 'test_B'})
b1_user = users_obj.browse(cr, uid, [b1])[0]
b2 = users_obj.create(cr, uid, {'name': 'test_B2', 'login': 'test_B2', 'parent_id': b1_user.partner_id.id})
# Test1: simple inheritance
user_ids = users_obj.search(cr, uid, [('name', 'like', 'test')])
self.assertEqual(set(user_ids), set([a, b1, b2]), 'searching through inheritance failed')
user_ids = users_obj.search(cr, uid, [('name', '=', 'test_B')])
self.assertEqual(set(user_ids), set([b1]), 'searching through inheritance failed')
# Test2: inheritance + relational fields
user_ids = users_obj.search(cr, uid, [('child_ids.name', 'like', 'test_B')])
self.assertEqual(set(user_ids), set([b1]), 'searching through inheritance failed')
def test_20_auto_join(self):
registry, cr, uid = self.registry, self.cr, self.uid
# Get models
partner_obj = registry('res.partner')
state_obj = registry('res.country.state')
bank_obj = registry('res.partner.bank')
# Get test columns
partner_state_id_col = partner_obj._columns.get('state_id') # many2one on res.partner to res.country.state
partner_parent_id_col = partner_obj._columns.get('parent_id') # many2one on res.partner to res.partner
state_country_id_col = state_obj._columns.get('country_id') # many2one on res.country.state on res.country
partner_child_ids_col = partner_obj._columns.get('child_ids') # one2many on res.partner to res.partner
partner_bank_ids_col = partner_obj._columns.get('bank_ids') # one2many on res.partner to res.partner.bank
category_id_col = partner_obj._columns.get('category_id') # many2many on res.partner to res.partner.category
# Get the first bank account type to be able to create a res.partner.bank
bank_type = bank_obj._bank_type_get(cr, uid)[0]
# Get country/state data
country_us_id = registry('res.country').search(cr, uid, [('code', 'like', 'US')])[0]
state_ids = registry('res.country.state').search(cr, uid, [('country_id', '=', country_us_id)], limit=2)
# Create demo data: partners and bank object
p_a = partner_obj.create(cr, uid, {'name': 'test__A', 'state_id': state_ids[0]})
p_b = partner_obj.create(cr, uid, {'name': 'test__B', 'state_id': state_ids[1]})
p_aa = partner_obj.create(cr, uid, {'name': 'test__AA', 'parent_id': p_a, 'state_id': state_ids[0]})
p_ab = partner_obj.create(cr, uid, {'name': 'test__AB', 'parent_id': p_a, 'state_id': state_ids[1]})
p_ba = partner_obj.create(cr, uid, {'name': 'test__BA', 'parent_id': p_b, 'state_id': state_ids[0]})
b_aa = bank_obj.create(cr, uid, {'name': '__bank_test_a', 'state': bank_type[0], 'partner_id': p_aa, 'acc_number': '1234'})
b_ab = bank_obj.create(cr, uid, {'name': '__bank_test_b', 'state': bank_type[0], 'partner_id': p_ab, 'acc_number': '5678'})
b_ba = bank_obj.create(cr, uid, {'name': '__bank_test_b', 'state': bank_type[0], 'partner_id': p_ba, 'acc_number': '9876'})
# --------------------------------------------------
# Test1: basics about the attribute
# --------------------------------------------------
category_id_col._auto_join = True
self.assertRaises(NotImplementedError, partner_obj.search, cr, uid, [('category_id.name', '=', 'foo')])
category_id_col._auto_join = False
# --------------------------------------------------
# Test2: one2many
# --------------------------------------------------
name_test = 'test_a'
# Do: one2many without _auto_join
self._reinit_mock()
partner_ids = partner_obj.search(cr, uid, [('bank_ids.name', 'like', name_test)])
# Test result
self.assertEqual(set(partner_ids), set([p_aa]),
"_auto_join off: ('bank_ids.name', 'like', '..'): incorrect result")
# Test produced queries
self.assertEqual(len(self.query_list), 3,
"_auto_join off: ('bank_ids.name', 'like', '..') should produce 3 queries (1 in res_partner_bank, 2 on res_partner)")
sql_query = self.query_list[0].get_sql()
self.assertIn('res_partner_bank', sql_query[0],
"_auto_join off: ('bank_ids.name', 'like', '..') first query incorrect main table")
self.assertIn('"res_partner_bank"."name" like %s', sql_query[1],
"_auto_join off: ('bank_ids.name', 'like', '..') first query incorrect where condition")
self.assertEqual(set(['%' + name_test + '%']), set(sql_query[2]),
"_auto_join off: ('bank_ids.name', 'like', '..') first query incorrect parameter")
sql_query = self.query_list[2].get_sql()
self.assertIn('res_partner', sql_query[0],
"_auto_join off: ('bank_ids.name', 'like', '..') third query incorrect main table")
self.assertIn('"res_partner"."id" in (%s)', sql_query[1],
"_auto_join off: ('bank_ids.name', 'like', '..') third query incorrect where condition")
self.assertEqual(set([p_aa]), set(sql_query[2]),
"_auto_join off: ('bank_ids.name', 'like', '..') third query incorrect parameter")
# Do: cascaded one2many without _auto_join
self._reinit_mock()
partner_ids = partner_obj.search(cr, uid, [('child_ids.bank_ids.id', 'in', [b_aa, b_ba])])
# Test result
self.assertEqual(set(partner_ids), set([p_a, p_b]),
"_auto_join off: ('child_ids.bank_ids.id', 'in', [..]): incorrect result")
# Test produced queries
self.assertEqual(len(self.query_list), 5,
"_auto_join off: ('child_ids.bank_ids.id', 'in', [..]) should produce 5 queries (1 in res_partner_bank, 4 on res_partner)")
# Do: one2many with _auto_join
partner_bank_ids_col._auto_join = True
self._reinit_mock()
partner_ids = partner_obj.search(cr, uid, [('bank_ids.name', 'like', 'test_a')])
# Test result
self.assertEqual(set(partner_ids), set([p_aa]),
"_auto_join on: ('bank_ids.name', 'like', '..') incorrect result")
# Test produced queries
self.assertEqual(len(self.query_list), 1,
"_auto_join on: ('bank_ids.name', 'like', '..') should produce 1 query")
sql_query = self.query_list[0].get_sql()
self.assertIn('"res_partner"', sql_query[0],
"_auto_join on: ('bank_ids.name', 'like', '..') query incorrect main table")
self.assertIn('"res_partner_bank" as "res_partner__bank_ids"', sql_query[0],
"_auto_join on: ('bank_ids.name', 'like', '..') query incorrect join")
self.assertIn('"res_partner__bank_ids"."name" like %s', sql_query[1],
"_auto_join on: ('bank_ids.name', 'like', '..') query incorrect where condition")
self.assertIn('"res_partner"."id"="res_partner__bank_ids"."partner_id"', sql_query[1],
"_auto_join on: ('bank_ids.name', 'like', '..') query incorrect join condition")
self.assertEqual(set(['%' + name_test + '%']), set(sql_query[2]),
"_auto_join on: ('bank_ids.name', 'like', '..') query incorrect parameter")
# Do: one2many with _auto_join, test final leaf is an id
self._reinit_mock()
partner_ids = partner_obj.search(cr, uid, [('bank_ids.id', 'in', [b_aa, b_ab])])
# Test result
self.assertEqual(set(partner_ids), set([p_aa, p_ab]),
"_auto_join on: ('bank_ids.id', 'in', [..]) incorrect result")
# Test produced queries
self.assertEqual(len(self.query_list), 1,
"_auto_join on: ('bank_ids.id', 'in', [..]) should produce 1 query")
sql_query = self.query_list[0].get_sql()
self.assertIn('"res_partner"', sql_query[0],
"_auto_join on: ('bank_ids.id', 'in', [..]) query incorrect main table")
self.assertIn('"res_partner__bank_ids"."id" in (%s,%s)', sql_query[1],
"_auto_join on: ('bank_ids.id', 'in', [..]) query incorrect where condition")
self.assertEqual(set([b_aa, b_ab]), set(sql_query[2]),
"_auto_join on: ('bank_ids.id', 'in', [..]) query incorrect parameter")
# Do: 2 cascaded one2many with _auto_join, test final leaf is an id
partner_child_ids_col._auto_join = True
self._reinit_mock()
partner_ids = partner_obj.search(cr, uid, [('child_ids.bank_ids.id', 'in', [b_aa, b_ba])])
# Test result
self.assertEqual(set(partner_ids), set([p_a, p_b]),
"_auto_join on: ('child_ids.bank_ids.id', 'not in', [..]): incorrect result")
# # Test produced queries
self.assertEqual(len(self.query_list), 1,
"_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) should produce 1 query")
sql_query = self.query_list[0].get_sql()
self.assertIn('"res_partner"', sql_query[0],
"_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) incorrect main table")
self.assertIn('"res_partner" as "res_partner__child_ids"', sql_query[0],
"_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect join")
self.assertIn('"res_partner_bank" as "res_partner__child_ids__bank_ids"', sql_query[0],
"_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect join")
self.assertIn('"res_partner__child_ids__bank_ids"."id" in (%s,%s)', sql_query[1],
"_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect where condition")
self.assertIn('"res_partner"."id"="res_partner__child_ids"."parent_id"', sql_query[1],
"_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect join condition")
self.assertIn('"res_partner__child_ids"."id"="res_partner__child_ids__bank_ids"."partner_id"', sql_query[1],
"_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect join condition")
self.assertEqual(set([b_aa, b_ba]), set(sql_query[2]),
"_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect parameter")
# --------------------------------------------------
# Test3: many2one
# --------------------------------------------------
name_test = 'US'
# Do: many2one without _auto_join
self._reinit_mock()
partner_ids = partner_obj.search(cr, uid, [('state_id.country_id.code', 'like', name_test)])
# Test result: at least our added data + demo data
self.assertTrue(set([p_a, p_b, p_aa, p_ab, p_ba]).issubset(set(partner_ids)),
"_auto_join off: ('state_id.country_id.code', 'like', '..') incorrect result")
# Test produced queries
self.assertEqual(len(self.query_list), 3,
"_auto_join off: ('state_id.country_id.code', 'like', '..') should produce 3 queries (1 on res_country, 1 on res_country_state, 1 on res_partner)")
# Do: many2one with 1 _auto_join on the first many2one
partner_state_id_col._auto_join = True
self._reinit_mock()
partner_ids = partner_obj.search(cr, uid, [('state_id.country_id.code', 'like', name_test)])
# Test result: at least our added data + demo data
self.assertTrue(set([p_a, p_b, p_aa, p_ab, p_ba]).issubset(set(partner_ids)),
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') incorrect result")
# Test produced queries
self.assertEqual(len(self.query_list), 2,
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') should produce 2 query")
sql_query = self.query_list[0].get_sql()
self.assertIn('"res_country"', sql_query[0],
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect main table")
self.assertIn('"res_country"."code" like %s', sql_query[1],
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect where condition")
self.assertEqual(['%' + name_test + '%'], sql_query[2],
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect parameter")
sql_query = self.query_list[1].get_sql()
self.assertIn('"res_partner"', sql_query[0],
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect main table")
self.assertIn('"res_country_state" as "res_partner__state_id"', sql_query[0],
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect join")
self.assertIn('"res_partner__state_id"."country_id" in (%s)', sql_query[1],
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect where condition")
self.assertIn('"res_partner"."state_id"="res_partner__state_id"."id"', sql_query[1],
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect join condition")
# Do: many2one with 1 _auto_join on the second many2one
partner_state_id_col._auto_join = False
state_country_id_col._auto_join = True
self._reinit_mock()
partner_ids = partner_obj.search(cr, uid, [('state_id.country_id.code', 'like', name_test)])
# Test result: at least our added data + demo data
self.assertTrue(set([p_a, p_b, p_aa, p_ab, p_ba]).issubset(set(partner_ids)),
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') incorrect result")
# Test produced queries
self.assertEqual(len(self.query_list), 2,
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') should produce 2 query")
# -- first query
sql_query = self.query_list[0].get_sql()
self.assertIn('"res_country_state"', sql_query[0],
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect main table")
self.assertIn('"res_country" as "res_country_state__country_id"', sql_query[0],
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect join")
self.assertIn('"res_country_state__country_id"."code" like %s', sql_query[1],
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect where condition")
self.assertIn('"res_country_state"."country_id"="res_country_state__country_id"."id"', sql_query[1],
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect join condition")
self.assertEqual(['%' + name_test + '%'], sql_query[2],
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect parameter")
# -- second query
sql_query = self.query_list[1].get_sql()
self.assertIn('"res_partner"', sql_query[0],
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect main table")
self.assertIn('"res_partner"."state_id" in', sql_query[1],
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect where condition")
# Do: many2one with 2 _auto_join
partner_state_id_col._auto_join = True
state_country_id_col._auto_join = True
self._reinit_mock()
partner_ids = partner_obj.search(cr, uid, [('state_id.country_id.code', 'like', name_test)])
# Test result: at least our added data + demo data
self.assertTrue(set([p_a, p_b, p_aa, p_ab, p_ba]).issubset(set(partner_ids)),
"_auto_join on: ('state_id.country_id.code', 'like', '..') incorrect result")
# Test produced queries
self.assertEqual(len(self.query_list), 1,
"_auto_join on: ('state_id.country_id.code', 'like', '..') should produce 1 query")
sql_query = self.query_list[0].get_sql()
self.assertIn('"res_partner"', sql_query[0],
"_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect main table")
self.assertIn('"res_country_state" as "res_partner__state_id"', sql_query[0],
"_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join")
self.assertIn('"res_country" as "res_partner__state_id__country_id"', sql_query[0],
"_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join")
self.assertIn('"res_partner__state_id__country_id"."code" like %s', sql_query[1],
"_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect where condition")
self.assertIn('"res_partner"."state_id"="res_partner__state_id"."id"', sql_query[1],
"_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join condition")
self.assertIn('"res_partner__state_id"."country_id"="res_partner__state_id__country_id"."id"', sql_query[1],
"_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join condition")
self.assertEqual(['%' + name_test + '%'], sql_query[2],
"_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect parameter")
# --------------------------------------------------
# Test4: domain attribute on one2many fields
# --------------------------------------------------
partner_child_ids_col._auto_join = True
partner_bank_ids_col._auto_join = True
partner_child_ids_col._domain = lambda self: ['!', ('name', '=', self._name)]
partner_bank_ids_col._domain = [('acc_number', 'like', '1')]
# Do: 2 cascaded one2many with _auto_join, test final leaf is an id
self._reinit_mock()
partner_ids = partner_obj.search(cr, uid, ['&', (1, '=', 1), ('child_ids.bank_ids.id', 'in', [b_aa, b_ba])])
# Test result: at least one of our added data
self.assertTrue(set([p_a]).issubset(set(partner_ids)),
"_auto_join on one2many with domains incorrect result")
self.assertTrue(set([p_ab, p_ba]) not in set(partner_ids),
"_auto_join on one2many with domains incorrect result")
# Test produced queries that domains effectively present
sql_query = self.query_list[0].get_sql()
self.assertIn('"res_partner__child_ids__bank_ids"."acc_number" like %s', sql_query[1],
"_auto_join on one2many with domains incorrect result")
# TDE TODO: check first domain has a correct table name
self.assertIn('"res_partner__child_ids"."name" = %s', sql_query[1],
"_auto_join on one2many with domains incorrect result")
partner_child_ids_col._domain = lambda self: [('name', '=', '__%s' % self._name)]
self._reinit_mock()
partner_ids = partner_obj.search(cr, uid, ['&', (1, '=', 1), ('child_ids.bank_ids.id', 'in', [b_aa, b_ba])])
# Test result: no one
self.assertFalse(partner_ids,
"_auto_join on one2many with domains incorrect result")
# ----------------------------------------
# Test5: result-based tests
# ----------------------------------------
partner_bank_ids_col._auto_join = False
partner_child_ids_col._auto_join = False
partner_state_id_col._auto_join = False
partner_parent_id_col._auto_join = False
state_country_id_col._auto_join = False
partner_child_ids_col._domain = []
partner_bank_ids_col._domain = []
# Do: ('child_ids.state_id.country_id.code', 'like', '..') without _auto_join
self._reinit_mock()
partner_ids = partner_obj.search(cr, uid, [('child_ids.state_id.country_id.code', 'like', name_test)])
# Test result: at least our added data + demo data
self.assertTrue(set([p_a, p_b]).issubset(set(partner_ids)),
"_auto_join off: ('child_ids.state_id.country_id.code', 'like', '..') incorrect result")
# Test produced queries
self.assertEqual(len(self.query_list), 5,
"_auto_join off: ('child_ids.state_id.country_id.code', 'like', '..') number of queries incorrect")
# Do: ('child_ids.state_id.country_id.code', 'like', '..') with _auto_join
partner_child_ids_col._auto_join = True
partner_state_id_col._auto_join = True
state_country_id_col._auto_join = True
self._reinit_mock()
partner_ids = partner_obj.search(cr, uid, [('child_ids.state_id.country_id.code', 'like', name_test)])
# Test result: at least our added data + demo data
self.assertTrue(set([p_a, p_b]).issubset(set(partner_ids)),
"_auto_join on: ('child_ids.state_id.country_id.code', 'like', '..') incorrect result")
# Test produced queries
self.assertEqual(len(self.query_list), 1,
"_auto_join on: ('child_ids.state_id.country_id.code', 'like', '..') number of queries incorrect")
# Remove mocks and modifications
partner_bank_ids_col._auto_join = False
partner_child_ids_col._auto_join = False
partner_state_id_col._auto_join = False
partner_parent_id_col._auto_join = False
state_country_id_col._auto_join = False
if __name__ == '__main__':
unittest2.main()
+61 -12
View File
@@ -2,16 +2,17 @@ import unittest2
import openerp.tests.common as common
class test_expression(common.TransactionCase):
def test_search_order(self):
class test_search(common.TransactionCase):
def test_00_search_order(self):
registry, cr, uid = self.registry, self.cr, self.uid
# Create 6 partners with a given name, and a given creation order to
# ensure the order of their ID. Some are set as unactive to verify they
# are by default excluded from the searches and to provide a second
# `order` argument.
# Create 6 partners with a given name, and a given creation order to
# ensure the order of their ID. Some are set as unactive to verify they
# are by default excluded from the searches and to provide a second
# `order` argument.
partners = registry('res.partner')
c = partners.create(cr, uid, {'name': 'test_search_order_C'})
@@ -23,9 +24,9 @@ class test_expression(common.TransactionCase):
# The tests.
# The basic searches should exclude records that have active = False.
# The order of the returned ids should be given by the `order`
# parameter of search().
# The basic searches should exclude records that have active = False.
# The order of the returned ids should be given by the `order`
# parameter of search().
name_asc = partners.search(cr, uid, [('name', 'like', 'test_search_order%')], order="name asc")
self.assertEqual([a, ab, b, c], name_asc, "Search with 'NAME ASC' order failed.")
@@ -36,9 +37,9 @@ class test_expression(common.TransactionCase):
id_desc = partners.search(cr, uid, [('name', 'like', 'test_search_order%')], order="id desc")
self.assertEqual([ab, b, a, c], id_desc, "Search with 'ID DESC' order failed.")
# The inactive records shouldn't be excluded as soon as a condition on
# that field is present in the domain. The `order` parameter of
# search() should support any legal coma-separated values.
# The inactive records shouldn't be excluded as soon as a condition on
# that field is present in the domain. The `order` parameter of
# search() should support any legal coma-separated values.
active_asc_id_asc = partners.search(cr, uid, [('name', 'like', 'test_search_order%'), '|', ('active', '=', True), ('active', '=', False)], order="active asc, id asc")
self.assertEqual([d, e, c, a, b, ab], active_asc_id_asc, "Search with 'ACTIVE ASC, ID ASC' order failed.")
@@ -57,4 +58,52 @@ class test_expression(common.TransactionCase):
id_desc_active_desc = partners.search(cr, uid, [('name', 'like', 'test_search_order%'), '|', ('active', '=', True), ('active', '=', False)], order="id desc, active desc")
self.assertEqual([e, ab, b, a, d, c], id_desc_active_desc, "Search with 'ID DESC, ACTIVE DESC' order failed.")
def test_10_inherits_m2order(self):
registry, cr, uid = self.registry, self.cr, self.uid
users_obj = registry('res.users')
# Find Employee group
group_employee_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'base', 'group_user')
group_employee_id = group_employee_ref and group_employee_ref[1] or False
# Get country/state data
country_us_id = registry('res.country').search(cr, uid, [('code', 'like', 'US')])[0]
state_ids = registry('res.country.state').search(cr, uid, [('country_id', '=', country_us_id)], limit=2)
country_be_id = registry('res.country').search(cr, uid, [('code', 'like', 'BE')])[0]
# Create test users
search_user = users_obj.create(cr, uid, {'name': '__search', 'login': '__search', 'groups_id': [(6, 0, [group_employee_id])]})
a = users_obj.create(cr, uid, {'name': '__test_A', 'login': '__test_A', 'country_id': country_be_id, 'state_id': country_be_id})
b = users_obj.create(cr, uid, {'name': '__test_B', 'login': '__a_test_B', 'country_id': country_us_id, 'state_id': state_ids[1]})
c = users_obj.create(cr, uid, {'name': '__test_B', 'login': '__z_test_B', 'country_id': country_us_id, 'state_id': state_ids[0]})
# Do: search on res.users, order on a field on res.partner to try inherits'd fields, then res.users
user_ids = users_obj.search(cr, search_user, [], order='name asc, login desc')
expected_ids = [search_user, a, c, b]
test_user_ids = filter(lambda x: x in expected_ids, user_ids)
self.assertEqual(test_user_ids, expected_ids, 'search on res_users did not provide expected ids or expected order')
# Do: order on many2one and inherits'd fields
user_ids = users_obj.search(cr, search_user, [], order='state_id asc, country_id desc, name asc, login desc')
expected_ids = [c, b, a, search_user]
test_user_ids = filter(lambda x: x in expected_ids, user_ids)
self.assertEqual(test_user_ids, expected_ids, 'search on res_users did not provide expected ids or expected order')
# Do: order on many2one and inherits'd fields
user_ids = users_obj.search(cr, search_user, [], order='country_id desc, state_id desc, name asc, login desc')
expected_ids = [search_user, b, c, a]
test_user_ids = filter(lambda x: x in expected_ids, user_ids)
self.assertEqual(test_user_ids, expected_ids, 'search on res_users did not provide expected ids or expected order')
# Do: order on many2one, but not by specifying in order parameter of search, but by overriding _order of res_users
old_order = users_obj._order
users_obj._order = 'country_id desc, name asc, login desc'
user_ids = users_obj.search(cr, search_user, [])
expected_ids = [search_user, c, b, a]
test_user_ids = filter(lambda x: x in expected_ids, user_ids)
self.assertEqual(test_user_ids, expected_ids, 'search on res_users did not provide expected ids or expected order')
users_obj._order = old_order
if __name__ == '__main__':
unittest2.main()
+19 -2
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+8 -2
View File
@@ -27,6 +27,9 @@
Fields Attributes:
* _classic_read: is a classic sql fields
* _type : field type
* _auto_join: for one2many and many2one fields, tells whether select
queries will join the relational table instead of replacing the
field condition by an equivalent-one based on a search.
* readonly
* required
* size
@@ -67,6 +70,7 @@ class _column(object):
"""
_classic_read = True
_classic_write = True
_auto_join = False
_prefetch = True
_properties = False
_type = 'unknown'
@@ -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
View File
@@ -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
View File
@@ -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):
+1 -21
View File
@@ -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):
+2 -2
View File
@@ -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)
-25
View File
@@ -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:
+5 -2
View File
@@ -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:
+6 -5
View File
@@ -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
View File
@@ -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