merge upstream
bzr revid: chs@openerp.com-20121210132406-9ro3amw6s6pzeez4
This commit is contained in:
commit
236df95b22
|
@ -1,19 +0,0 @@
|
|||
#! /usr/bin/env python
|
||||
# -*- coding: UTF-8 -*-
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
print '-' * 70
|
||||
print "DEPRECATED: you are starting the OpenERP server with its old path,"
|
||||
print "please use the new executable (available in the parent directory)."
|
||||
print '-' * 70
|
||||
|
||||
# Change to the parent directory ...
|
||||
os.chdir(os.path.normpath(os.path.dirname(__file__)))
|
||||
os.chdir('..')
|
||||
# ... and execute the new executable.
|
||||
os.execv('openerp-server', sys.argv)
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -23,6 +23,7 @@ Depends:
|
|||
python-libxslt1,
|
||||
python-lxml,
|
||||
python-mako,
|
||||
python-mock,
|
||||
python-openid,
|
||||
python-psutil,
|
||||
python-psycopg2,
|
||||
|
@ -33,6 +34,7 @@ Depends:
|
|||
python-reportlab,
|
||||
python-simplejson,
|
||||
python-tz,
|
||||
python-unittest2,
|
||||
python-vatnumber,
|
||||
python-vobject,
|
||||
python-webdav,
|
||||
|
|
|
@ -40,7 +40,6 @@ import service
|
|||
import sql_db
|
||||
import test
|
||||
import tools
|
||||
import wizard
|
||||
import workflow
|
||||
# backward compatilbility
|
||||
# TODO: This is for the web addons, can be removed later.
|
||||
|
|
|
@ -7,13 +7,13 @@ 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-05 20:42+0000\n"
|
||||
"Last-Translator: Santi (Pexego) <santiago@pexego.es>\n"
|
||||
"PO-Revision-Date: 2012-12-09 23:26+0000\n"
|
||||
"Last-Translator: lambdasoftware <development@lambdasoftware.net>\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-06 04:39+0000\n"
|
||||
"X-Launchpad-Export-Date: 2012-12-10 04:36+0000\n"
|
||||
"X-Generator: Launchpad (build 16341)\n"
|
||||
|
||||
#. module: base
|
||||
|
@ -541,7 +541,7 @@ msgstr "Nombre de relación"
|
|||
#. module: base
|
||||
#: view:ir.rule:0
|
||||
msgid "Create Access Right"
|
||||
msgstr ""
|
||||
msgstr "Otorge derecho de acceso"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.tv
|
||||
|
@ -581,7 +581,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: view:workflow.transition:0
|
||||
msgid "Workflow Transition"
|
||||
msgstr ""
|
||||
msgstr "Transición del flujo de trabajo"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.gf
|
||||
|
@ -704,7 +704,7 @@ msgstr "Colombia"
|
|||
#. module: base
|
||||
#: model:res.partner.title,name:base.res_partner_title_mister
|
||||
msgid "Mister"
|
||||
msgstr ""
|
||||
msgstr "Señor"
|
||||
|
||||
#. module: base
|
||||
#: help:res.country,code:0
|
||||
|
@ -733,7 +733,7 @@ msgstr "Sin traducir"
|
|||
#. module: base
|
||||
#: view:ir.mail_server:0
|
||||
msgid "Outgoing Mail Server"
|
||||
msgstr ""
|
||||
msgstr "Servidor de correo saliente"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.actions.act_window,context:0
|
||||
|
@ -927,7 +927,7 @@ msgstr "Sueco / svenska"
|
|||
#: field:base.language.export,name:0
|
||||
#: field:ir.attachment,datas_fname:0
|
||||
msgid "File Name"
|
||||
msgstr ""
|
||||
msgstr "Nombre del archivo"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.rs
|
||||
|
@ -1024,7 +1024,7 @@ msgstr "Preferencias 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' no parece ser una fecha valida para el campo '%%(field)s'"
|
||||
|
||||
#. module: base
|
||||
#: view:res.partner:0
|
||||
|
@ -1156,7 +1156,7 @@ msgstr "Usuarios google"
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_fleet
|
||||
msgid "Fleet Management"
|
||||
msgstr ""
|
||||
msgstr "Gestión de flotas"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.server.object.lines,value:0
|
||||
|
@ -2721,7 +2721,7 @@ msgstr "Nombre de acceso rápido"
|
|||
#. module: base
|
||||
#: field:res.partner,contact_address:0
|
||||
msgid "Complete Address"
|
||||
msgstr ""
|
||||
msgstr "Dirección completa"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.actions.act_window,limit:0
|
||||
|
@ -2793,7 +2793,7 @@ msgstr "ID de registro"
|
|||
#. module: base
|
||||
#: view:ir.filters:0
|
||||
msgid "My Filters"
|
||||
msgstr ""
|
||||
msgstr "Mis filtros"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.actions.server,email:0
|
||||
|
@ -2879,7 +2879,7 @@ msgstr "Responsable"
|
|||
#: code:addons/base/ir/ir_model.py:718
|
||||
#, python-format
|
||||
msgid "Sorry, you are not allowed to access this document."
|
||||
msgstr ""
|
||||
msgstr "Lo siento, no está autorizado para acceder a este documento."
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.py
|
||||
|
@ -2894,7 +2894,7 @@ msgstr "Fiji"
|
|||
#. module: base
|
||||
#: view:ir.actions.report.xml:0
|
||||
msgid "Report Xml"
|
||||
msgstr ""
|
||||
msgstr "Informe Xml"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_purchase
|
||||
|
@ -2965,7 +2965,7 @@ msgstr "Hededado"
|
|||
#: code:addons/base/ir/ir_fields.py:147
|
||||
#, python-format
|
||||
msgid "yes"
|
||||
msgstr ""
|
||||
msgstr "Sí"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.model.fields,serialization_field_id:0
|
||||
|
@ -3056,7 +3056,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_web_linkedin
|
||||
msgid "LinkedIn Integration"
|
||||
msgstr ""
|
||||
msgstr "Integración con LinkedIn"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/orm.py:2021
|
||||
|
@ -3370,7 +3370,7 @@ msgstr "Calendario"
|
|||
#. module: base
|
||||
#: model:ir.module.category,name:base.module_category_knowledge_management
|
||||
msgid "Knowledge"
|
||||
msgstr ""
|
||||
msgstr "Conocimiento"
|
||||
|
||||
#. module: base
|
||||
#: field:workflow.activity,signal_send:0
|
||||
|
@ -3535,7 +3535,7 @@ msgstr "workflow.actividad"
|
|||
#. module: base
|
||||
#: view:base.language.export:0
|
||||
msgid "Export Complete"
|
||||
msgstr ""
|
||||
msgstr "Exportación completada"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.ui.view_sc,res_id:0
|
||||
|
@ -3564,7 +3564,7 @@ msgstr "Finlandés / Suomi"
|
|||
#. module: base
|
||||
#: view:ir.config_parameter:0
|
||||
msgid "System Properties"
|
||||
msgstr ""
|
||||
msgstr "Propiedades de sistema"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.sequence,prefix:0
|
||||
|
@ -3621,7 +3621,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: field:base.language.export,modules:0
|
||||
msgid "Modules To Export"
|
||||
msgstr ""
|
||||
msgstr "Módulos a exportar"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.mt
|
||||
|
@ -3724,7 +3724,7 @@ msgstr "Antártida"
|
|||
#. module: base
|
||||
#: view:res.partner:0
|
||||
msgid "Persons"
|
||||
msgstr ""
|
||||
msgstr "Personas"
|
||||
|
||||
#. module: base
|
||||
#: view:base.language.import:0
|
||||
|
@ -3903,7 +3903,7 @@ msgstr "Tayiko / اردو"
|
|||
#: code:addons/orm.py:3870
|
||||
#, python-format
|
||||
msgid "Access Denied"
|
||||
msgstr ""
|
||||
msgstr "Acceso denegado"
|
||||
|
||||
#. module: base
|
||||
#: field:res.company,name:0
|
||||
|
@ -3996,7 +3996,7 @@ msgstr "%x - Representación apropiada de fecha."
|
|||
#. module: base
|
||||
#: view:res.partner:0
|
||||
msgid "Tag"
|
||||
msgstr ""
|
||||
msgstr "Etiqueta"
|
||||
|
||||
#. module: base
|
||||
#: view:res.lang:0
|
||||
|
@ -4048,7 +4048,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:res.country,name:base.sk
|
||||
msgid "Slovakia"
|
||||
msgstr ""
|
||||
msgstr "Eslovaquia"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.nr
|
||||
|
@ -4599,7 +4599,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: field:ir.module.module,latest_version:0
|
||||
msgid "Installed Version"
|
||||
msgstr ""
|
||||
msgstr "Versión instalada"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.module.module,license:0
|
||||
|
@ -4895,7 +4895,7 @@ msgstr "Flujos"
|
|||
#. module: base
|
||||
#: model:ir.ui.menu,name:base.next_id_73
|
||||
msgid "Purchase"
|
||||
msgstr ""
|
||||
msgstr "Compra"
|
||||
|
||||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
|
@ -5054,7 +5054,7 @@ msgstr "Establecer a NULL"
|
|||
#. module: base
|
||||
#: view:res.users:0
|
||||
msgid "Save"
|
||||
msgstr ""
|
||||
msgstr "Guardar"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.actions.report.xml,report_xml:0
|
||||
|
@ -5248,7 +5248,7 @@ msgstr "Tasas"
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_email_template
|
||||
msgid "Email Templates"
|
||||
msgstr ""
|
||||
msgstr "Plantillas de correo electrónico"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sy
|
||||
|
@ -5397,7 +5397,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: view:ir.rule:0
|
||||
msgid "Write Access Right"
|
||||
msgstr ""
|
||||
msgstr "Permiso de escritura"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.actions.act_window,help:base.action_res_groups
|
||||
|
@ -5432,7 +5432,7 @@ msgstr "Historial"
|
|||
#. module: base
|
||||
#: model:res.country,name:base.im
|
||||
msgid "Isle of Man"
|
||||
msgstr ""
|
||||
msgstr "Isla de Man"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.actions.client,res_model:0
|
||||
|
@ -6109,7 +6109,7 @@ msgstr "Nombre del recurso"
|
|||
#. module: base
|
||||
#: field:res.partner,is_company:0
|
||||
msgid "Is a Company"
|
||||
msgstr ""
|
||||
msgstr "¿Es una empresa?"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.cron,interval_type:0
|
||||
|
@ -6189,7 +6189,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: sql_constraint:ir.filters:0
|
||||
msgid "Filter names must be unique"
|
||||
msgstr ""
|
||||
msgstr "Los nombres de filtro deben ser únicos"
|
||||
|
||||
#. module: base
|
||||
#: help:multi_company.default,object_id:0
|
||||
|
@ -6204,7 +6204,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: field:ir.filters,is_default:0
|
||||
msgid "Default filter"
|
||||
msgstr ""
|
||||
msgstr "Filtro por defecto"
|
||||
|
||||
#. module: base
|
||||
#: report:ir.module.reference:0
|
||||
|
@ -6932,7 +6932,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_auth_anonymous
|
||||
msgid "Anonymous"
|
||||
msgstr ""
|
||||
msgstr "Anónimo"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_base_import
|
||||
|
@ -7439,7 +7439,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:res.partner.category,name:base.res_partner_category_14
|
||||
msgid "Manufacturer"
|
||||
msgstr ""
|
||||
msgstr "Fabricante"
|
||||
|
||||
#. module: base
|
||||
#: help:res.users,company_id:0
|
||||
|
@ -8055,7 +8055,7 @@ msgstr "Francés (CH) / Français (CH)"
|
|||
#. module: base
|
||||
#: model:res.partner.category,name:base.res_partner_category_13
|
||||
msgid "Distributor"
|
||||
msgstr ""
|
||||
msgstr "Distribuidor"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.actions.server,subject:0
|
||||
|
@ -15511,7 +15511,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: view:res.partner:0
|
||||
msgid "Internal Notes"
|
||||
msgstr ""
|
||||
msgstr "Notas internas"
|
||||
|
||||
#. module: base
|
||||
#: selection:res.partner.address,type:0
|
||||
|
@ -15553,7 +15553,7 @@ msgstr "Empresas: "
|
|||
#. module: base
|
||||
#: view:res.partner:0
|
||||
msgid "Is a Company?"
|
||||
msgstr ""
|
||||
msgstr "¿Es una empresa?"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/res/res_company.py:159
|
||||
|
@ -15575,7 +15575,7 @@ msgstr "Crear objeto"
|
|||
#. module: base
|
||||
#: model:res.country,name:base.ss
|
||||
msgid "South Sudan"
|
||||
msgstr ""
|
||||
msgstr "Sudán del Sur"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.filters,context:0
|
||||
|
|
|
@ -8,13 +8,13 @@ msgstr ""
|
|||
"Project-Id-Version: openobject-server\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-12-03 16:01+0000\n"
|
||||
"PO-Revision-Date: 2012-12-06 20:42+0000\n"
|
||||
"PO-Revision-Date: 2012-12-07 15:49+0000\n"
|
||||
"Last-Translator: Carlos Matos Villar <carlosmvillar19@hotmail.com>\n"
|
||||
"Language-Team: Spanish (Dominican Republic) <es_DO@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-07 04:34+0000\n"
|
||||
"X-Launchpad-Export-Date: 2012-12-08 04:58+0000\n"
|
||||
"X-Generator: Launchpad (build 16341)\n"
|
||||
|
||||
#. module: base
|
||||
|
@ -9141,7 +9141,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: view:res.lang:0
|
||||
msgid "2. %a ,%A ==> Fri, Friday"
|
||||
msgstr ""
|
||||
msgstr "2. %a ,%A ==> Vie, Viernes"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/ir/ir_translation.py:341
|
||||
|
@ -9150,6 +9150,8 @@ msgid ""
|
|||
"Translation features are unavailable until you install an extra OpenERP "
|
||||
"translation."
|
||||
msgstr ""
|
||||
"Las Características de Traducción no están disponibles hasta que usted "
|
||||
"instala una traducción extra en OpenERP."
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_l10n_nl
|
||||
|
@ -9193,16 +9195,56 @@ msgid ""
|
|||
"\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Este es el modulo para gestionar el plan de cuenta para Neerlandés en "
|
||||
"OpenERP.\n"
|
||||
"============================================================================="
|
||||
"=======\n"
|
||||
"\n"
|
||||
"Lea los cambios en el archivo __openerp__.py para la versión de "
|
||||
"información.\n"
|
||||
"Read changelog in file __openerp__.py for version information.\n"
|
||||
"Dit is een basismodule om een uitgebreid grootboek- en BTW schema voor\n"
|
||||
"Nederlandse bedrijven te installeren in OpenERP versie 7.0.\n"
|
||||
"\n"
|
||||
"De BTW rekeningen zijn waar nodig gekoppeld om de juiste rapportage te "
|
||||
"genereren,\n"
|
||||
"denk b.v. aan intracommunautaire verwervingen waarbij u 21% BTW moet "
|
||||
"opvoeren,\n"
|
||||
"maar tegelijkertijd ook 21% als voorheffing weer mag aftrekken.\n"
|
||||
"\n"
|
||||
"Na installatie van deze module word de configuratie wizard voor 'Accounting' "
|
||||
"aangeroepen.\n"
|
||||
"* U krijgt een lijst met grootboektemplates aangeboden waarin zich ook het\n"
|
||||
"Nederlandse grootboekschema bevind.\n"
|
||||
"\n"
|
||||
"* Als de configuratie wizard start, wordt u gevraagd om de naam van uw "
|
||||
"bedrijf\n"
|
||||
"in te voeren, welke grootboekschema te installeren, uit hoeveel cijfers een\n"
|
||||
"grootboekrekening mag bestaan, het rekeningnummer van uw bank en de "
|
||||
"currency\n"
|
||||
"om Journalen te creeren.\n"
|
||||
"\n"
|
||||
"Let op!! -> De template van het Nederlandse rekeningschema is opgebouwd uit "
|
||||
"4\n"
|
||||
"cijfers. Dit is het minimale aantal welk u moet invullen, u mag het aantal "
|
||||
"verhogen.\n"
|
||||
"De extra cijfers worden dan achter het rekeningnummer aangevult met "
|
||||
"'nullen'.\n"
|
||||
"\n"
|
||||
" "
|
||||
|
||||
#. module: base
|
||||
#: help:ir.rule,global:0
|
||||
msgid "If no group is specified the rule is global and applied to everyone"
|
||||
msgstr ""
|
||||
"Si no se especifica ningún grupo, la regla es global y se aplica a todo el "
|
||||
"mundo."
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.td
|
||||
msgid "Chad"
|
||||
msgstr ""
|
||||
msgstr "Chad"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.cron,priority:0
|
||||
|
@ -9210,31 +9252,33 @@ msgid ""
|
|||
"The priority of the job, as an integer: 0 means higher priority, 10 means "
|
||||
"lower priority."
|
||||
msgstr ""
|
||||
"Prioridad del trabajo, expresada con un entero: 0 significa la máxima "
|
||||
"prioridad, 10 significa la prioridad más baja."
|
||||
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_workflow_transition
|
||||
msgid "workflow.transition"
|
||||
msgstr ""
|
||||
msgstr "workflow.transition"
|
||||
|
||||
#. module: base
|
||||
#: view:res.lang:0
|
||||
msgid "%a - Abbreviated weekday name."
|
||||
msgstr ""
|
||||
msgstr "%a - Nombre abreviado del día de la semana."
|
||||
|
||||
#. module: base
|
||||
#: view:ir.ui.menu:0
|
||||
msgid "Submenus"
|
||||
msgstr ""
|
||||
msgstr "Submenús"
|
||||
|
||||
#. module: base
|
||||
#: report:ir.module.reference:0
|
||||
msgid "Introspection report on objects"
|
||||
msgstr ""
|
||||
msgstr "Introspección informe sobre los objetos"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_web_analytics
|
||||
msgid "Google Analytics"
|
||||
msgstr ""
|
||||
msgstr "Google Analytics"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_note
|
||||
|
@ -9257,83 +9301,84 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:res.country,name:base.dm
|
||||
msgid "Dominica"
|
||||
msgstr ""
|
||||
msgstr "Dominica"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.translation,name:0
|
||||
msgid "Translated field"
|
||||
msgstr ""
|
||||
msgstr "Campos Traducidos"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_stock_location
|
||||
msgid "Advanced Routes"
|
||||
msgstr ""
|
||||
msgstr "Rutas avanzadas"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_pad
|
||||
msgid "Collaborative Pads"
|
||||
msgstr ""
|
||||
msgstr "Pads colaborativos"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.np
|
||||
msgid "Nepal"
|
||||
msgstr ""
|
||||
msgstr "Nepal"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_document_page
|
||||
msgid "Document Page"
|
||||
msgstr ""
|
||||
msgstr "Página de Documentos"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_l10n_ar
|
||||
msgid "Argentina Localization Chart Account"
|
||||
msgstr ""
|
||||
msgstr "Plan de Cuenta Localización Argentina"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.module.module,description_html:0
|
||||
msgid "Description HTML"
|
||||
msgstr ""
|
||||
msgstr "HTML de Descripción"
|
||||
|
||||
#. module: base
|
||||
#: help:res.groups,implied_ids:0
|
||||
msgid "Users of this group automatically inherit those groups"
|
||||
msgstr ""
|
||||
"Los usuarios de este grupo automaticamente heredan de aquellos grupos"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,summary:base.module_note
|
||||
msgid "Sticky notes, Collaborative, Memos"
|
||||
msgstr ""
|
||||
msgstr "Notas adesivas, Colaborativas, Memos"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_hr_attendance
|
||||
#: model:res.groups,name:base.group_hr_attendance
|
||||
msgid "Attendances"
|
||||
msgstr ""
|
||||
msgstr "Asistencias"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_warning
|
||||
msgid "Warning Messages and Alerts"
|
||||
msgstr ""
|
||||
msgstr "Mensajes de aviso y alertas"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.actions.act_window,name:base.action_ui_view_custom
|
||||
#: model:ir.ui.menu,name:base.menu_action_ui_view_custom
|
||||
#: view:ir.ui.view.custom:0
|
||||
msgid "Customized Views"
|
||||
msgstr ""
|
||||
msgstr "Vistas personalizadas"
|
||||
|
||||
#. module: base
|
||||
#: view:base.module.import:0
|
||||
#: model:ir.actions.act_window,name:base.action_view_base_module_import
|
||||
msgid "Module Import"
|
||||
msgstr ""
|
||||
msgstr "Importación de módulo"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.actions.act_window,name:base.act_values_form_action
|
||||
#: model:ir.ui.menu,name:base.menu_values_form_action
|
||||
#: view:ir.values:0
|
||||
msgid "Action Bindings"
|
||||
msgstr ""
|
||||
msgstr "Enlaces de acciones"
|
||||
|
||||
#. module: base
|
||||
#: help:res.partner,lang:0
|
||||
|
@ -9341,6 +9386,9 @@ msgid ""
|
|||
"If the selected language is loaded in the system, all documents related to "
|
||||
"this contact will be printed in this language. If not, it will be English."
|
||||
msgstr ""
|
||||
"Si el lenguaje seleccionado esta cargado en el sistema, todos los documentos "
|
||||
"relacionados a este contacto serán impresos en este lenguaje. Si no, este "
|
||||
"será Ingles."
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_hr_evaluation
|
||||
|
@ -9378,7 +9426,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:ir.ui.menu,name:base.menu_view_base_module_update
|
||||
msgid "Update Modules List"
|
||||
msgstr ""
|
||||
msgstr "Actualizar lista de módulos"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/module/module.py:338
|
||||
|
@ -9386,11 +9434,13 @@ msgstr ""
|
|||
msgid ""
|
||||
"Unable to upgrade module \"%s\" because an external dependency is not met: %s"
|
||||
msgstr ""
|
||||
"Imposible actualizar el módulo \"%s\" porqué hay una dependencia externa no "
|
||||
"resuelta: %s"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_account
|
||||
msgid "eInvoicing"
|
||||
msgstr ""
|
||||
msgstr "Facturación electrónica"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/res/res_users.py:175
|
||||
|
@ -9401,37 +9451,41 @@ msgid ""
|
|||
"sure to save and close all forms before switching to a different company. "
|
||||
"(You can click on Cancel in the User Preferences now)"
|
||||
msgstr ""
|
||||
"Tenga en cuenta que los documentos que se muestran actualmente pueden no ser "
|
||||
"relevantes después de cambiar a otra compañía. Asegúrese de guardar y cerrar "
|
||||
"todas los formularios modificados antes de cambiar a una compañía diferente "
|
||||
"(ahora puede hacer clic en Cancelar en las preferencias del usuario)"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/orm.py:2821
|
||||
#, python-format
|
||||
msgid "The value \"%s\" for the field \"%s.%s\" is not in the selection"
|
||||
msgstr ""
|
||||
msgstr "El valor \"%s\" para el campo \"%s.%s\" no está en la selección"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.actions.configuration.wizard:0
|
||||
msgid "Continue"
|
||||
msgstr ""
|
||||
msgstr "Siguiente"
|
||||
|
||||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
msgid "Thai / ภาษาไทย"
|
||||
msgstr ""
|
||||
msgstr "Tailandés / ภาษาไทย"
|
||||
|
||||
#. module: base
|
||||
#: view:res.lang:0
|
||||
msgid "%j - Day of the year [001,366]."
|
||||
msgstr ""
|
||||
msgstr "%j - Día del año [001,366]."
|
||||
|
||||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
msgid "Slovenian / slovenščina"
|
||||
msgstr ""
|
||||
msgstr "Esloveno / slovenščina"
|
||||
|
||||
#. module: base
|
||||
#: field:res.currency,position:0
|
||||
msgid "Symbol Position"
|
||||
msgstr ""
|
||||
msgstr "Posición de Simbolo"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_l10n_de
|
||||
|
@ -9445,16 +9499,20 @@ msgid ""
|
|||
"German accounting chart and localization.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Plan de cuentas y localización alemana.\n"
|
||||
"===============================\n"
|
||||
" "
|
||||
|
||||
#. module: base
|
||||
#: field:ir.actions.report.xml,attachment_use:0
|
||||
msgid "Reload from Attachment"
|
||||
msgstr ""
|
||||
msgstr "Recargar desde adjunto"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.mx
|
||||
msgid "Mexico"
|
||||
msgstr ""
|
||||
msgstr "México"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/orm.py:3871
|
||||
|
@ -9465,11 +9523,15 @@ msgid ""
|
|||
"\n"
|
||||
"(Document type: %s)"
|
||||
msgstr ""
|
||||
"Para este tipo de documentos, usted solo puede acceder a archivos que usted "
|
||||
"mismo creó.\n"
|
||||
"\n"
|
||||
"(Tipo de Documento: %s)"
|
||||
|
||||
#. module: base
|
||||
#: view:base.language.export:0
|
||||
msgid "documentation"
|
||||
msgstr ""
|
||||
msgstr "documentación"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.model,osv_memory:0
|
||||
|
@ -9477,62 +9539,64 @@ msgid ""
|
|||
"This field specifies whether the model is transient or not (i.e. if records "
|
||||
"are automatically deleted from the database or not)"
|
||||
msgstr ""
|
||||
"Este campo especifica si el modelo es trascendente o no (Ejemplo: Si los "
|
||||
"archivos son automáticamente eliminados desde la base de datos o no)"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/ir/ir_mail_server.py:441
|
||||
#, python-format
|
||||
msgid "Missing SMTP Server"
|
||||
msgstr ""
|
||||
msgstr "Servidor SMTP ausente"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.attachment,name:0
|
||||
msgid "Attachment Name"
|
||||
msgstr ""
|
||||
msgstr "Nombre del adjunto"
|
||||
|
||||
#. module: base
|
||||
#: field:base.language.export,data:0
|
||||
#: field:base.language.import,data:0
|
||||
msgid "File"
|
||||
msgstr ""
|
||||
msgstr "Archivo"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.actions.act_window,name:base.action_view_base_module_upgrade_install
|
||||
msgid "Module Upgrade Install"
|
||||
msgstr ""
|
||||
msgstr "Instalar actualizar módulo"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_ir_actions_configuration_wizard
|
||||
msgid "ir.actions.configuration.wizard"
|
||||
msgstr ""
|
||||
msgstr "ir.actions.configuration.wizard"
|
||||
|
||||
#. module: base
|
||||
#: view:res.lang:0
|
||||
msgid "%b - Abbreviated month name."
|
||||
msgstr ""
|
||||
msgstr "%b - Nombre abreviado del mes."
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/ir/ir_model.py:721
|
||||
#, python-format
|
||||
msgid "Sorry, you are not allowed to delete this document."
|
||||
msgstr ""
|
||||
msgstr "Lo sentimos, usted no tiene permitod eliminar este documento."
|
||||
|
||||
#. module: base
|
||||
#: constraint:ir.rule:0
|
||||
msgid "Rules can not be applied on the Record Rules model."
|
||||
msgstr ""
|
||||
msgstr "Las reglas no pueden ser aplicadas en el archivo modelo de reglas."
|
||||
|
||||
#. module: base
|
||||
#: field:res.partner,supplier:0
|
||||
#: field:res.partner.address,is_supplier_add:0
|
||||
#: model:res.partner.category,name:base.res_partner_category_1
|
||||
msgid "Supplier"
|
||||
msgstr ""
|
||||
msgstr "Proveedor"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.actions.server:0
|
||||
#: selection:ir.actions.server,state:0
|
||||
msgid "Multi Actions"
|
||||
msgstr ""
|
||||
msgstr "Multi acciones"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,summary:base.module_mail
|
||||
|
@ -9559,91 +9623,109 @@ msgid ""
|
|||
"* Show all costs associated to a vehicle or to a type of service\n"
|
||||
"* Analysis graph for costs\n"
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Vehículo, Alquiler, seguros, costos\n"
|
||||
"================================\n"
|
||||
"Con este modulo, OpenERP les ayuda a administrar todos sus vehículos, \n"
|
||||
"los contratos asociados a esos vehículos así como el servicio, log de\n"
|
||||
"entradas de combustible, costos y algunas otras características necesarias "
|
||||
"para la gestión \n"
|
||||
"de sus fletes de vehículo(s)\n"
|
||||
"\n"
|
||||
"Características Principales\n"
|
||||
"------------------\n"
|
||||
"*Agrega vehículos a sus fletes\n"
|
||||
"*Gestiona contratos para vehículos\n"
|
||||
"*Recordatorios cuando un contrato alcanza su fecha de expiración\n"
|
||||
"*Agregar servicios, entradas de log de combustible, valores de cuenta "
|
||||
"kilómetros para todos los vehículos\n"
|
||||
"*Muestra todo el costo asociado a un vehículo o a un tipo de servicio\n"
|
||||
"*Análisis gráficos para costos\n"
|
||||
|
||||
#. module: base
|
||||
#: field:multi_company.default,company_dest_id:0
|
||||
msgid "Default Company"
|
||||
msgstr ""
|
||||
msgstr "Compañía por defecto"
|
||||
|
||||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
msgid "Spanish (EC) / Español (EC)"
|
||||
msgstr ""
|
||||
msgstr "Español (EC) / Español (EC)"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.ui.view,xml_id:0
|
||||
msgid "ID of the view defined in xml file"
|
||||
msgstr ""
|
||||
msgstr "El ID de la vista definido en el archivo xml."
|
||||
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_base_module_import
|
||||
msgid "Import Module"
|
||||
msgstr ""
|
||||
msgstr "Importar módulo"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.as
|
||||
msgid "American Samoa"
|
||||
msgstr ""
|
||||
msgstr "Samoa Americana"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.attachment:0
|
||||
msgid "My Document(s)"
|
||||
msgstr ""
|
||||
msgstr "Mis Documentos"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.actions.act_window,res_model:0
|
||||
msgid "Model name of the object to open in the view window"
|
||||
msgstr ""
|
||||
msgstr "Nombre del modelo del objeto a abrir en la ventana de la vista."
|
||||
|
||||
#. module: base
|
||||
#: field:ir.model.fields,selectable:0
|
||||
msgid "Selectable"
|
||||
msgstr ""
|
||||
msgstr "Seleccionable"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/ir/ir_mail_server.py:220
|
||||
#, python-format
|
||||
msgid "Everything seems properly set up!"
|
||||
msgstr ""
|
||||
msgstr "¡Todo parece correctamente configurado!"
|
||||
|
||||
#. module: base
|
||||
#: view:res.request.link:0
|
||||
msgid "Request Link"
|
||||
msgstr ""
|
||||
msgstr "Solicitar enlace"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.attachment:0
|
||||
#: selection:ir.attachment,type:0
|
||||
#: field:ir.module.module,url:0
|
||||
msgid "URL"
|
||||
msgstr ""
|
||||
msgstr "URL"
|
||||
|
||||
#. module: base
|
||||
#: help:res.country,name:0
|
||||
msgid "The full name of the country."
|
||||
msgstr ""
|
||||
msgstr "El nombre completo del país."
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.actions.server,state:0
|
||||
msgid "Iteration"
|
||||
msgstr ""
|
||||
msgstr "Iteración"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/orm.py:4213
|
||||
#: code:addons/orm.py:4314
|
||||
#, python-format
|
||||
msgid "UserError"
|
||||
msgstr ""
|
||||
msgstr "Error de usuario"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,summary:base.module_project_issue
|
||||
msgid "Support, Bug Tracker, Helpdesk"
|
||||
msgstr ""
|
||||
msgstr "Soporte, Rastreador de Bug, Ayudante de Escritorio"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.ae
|
||||
msgid "United Arab Emirates"
|
||||
msgstr ""
|
||||
msgstr "Emiratos Árabes Unidos"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.ui.menu,needaction_enabled:0
|
||||
|
@ -9659,21 +9741,23 @@ msgstr ""
|
|||
msgid ""
|
||||
"Unable to delete this document because it is used as a default property"
|
||||
msgstr ""
|
||||
"No se ha podido eliminar este documento ya que se utiliza como una propiedad "
|
||||
"por defecto"
|
||||
|
||||
#. module: base
|
||||
#: model:res.partner.category,name:base.res_partner_category_5
|
||||
msgid "Silver"
|
||||
msgstr ""
|
||||
msgstr "Plateado"
|
||||
|
||||
#. module: base
|
||||
#: field:res.partner.title,shortcut:0
|
||||
msgid "Abbreviation"
|
||||
msgstr ""
|
||||
msgstr "Abreviación"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.ui.menu,name:base.menu_crm_case_job_req_main
|
||||
msgid "Recruitment"
|
||||
msgstr ""
|
||||
msgstr "Proceso de selección"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_l10n_gr
|
||||
|
@ -9685,11 +9769,17 @@ msgid ""
|
|||
"Greek accounting chart and localization.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Módulo para administrar el plan de cuentas para Grecia.\n"
|
||||
"============================================\n"
|
||||
"\n"
|
||||
"Plan de cuentas y localización griego.\n"
|
||||
" "
|
||||
|
||||
#. module: base
|
||||
#: view:ir.values:0
|
||||
msgid "Action Reference"
|
||||
msgstr ""
|
||||
msgstr "Referencia de la acción"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_auth_ldap
|
||||
|
|
|
@ -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-11-24 21:10+0000\n"
|
||||
"PO-Revision-Date: 2012-12-09 19:36+0000\n"
|
||||
"Last-Translator: Goran Kliska <gkliska@gmail.com>\n"
|
||||
"Language-Team: openerp-translators\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-12-04 04:59+0000\n"
|
||||
"X-Generator: Launchpad (build 16335)\n"
|
||||
"X-Launchpad-Export-Date: 2012-12-10 04:36+0000\n"
|
||||
"X-Generator: Launchpad (build 16341)\n"
|
||||
"Language: hr\n"
|
||||
|
||||
#. module: base
|
||||
|
@ -532,7 +532,7 @@ msgstr "Naziv relacije"
|
|||
#. module: base
|
||||
#: view:ir.rule:0
|
||||
msgid "Create Access Right"
|
||||
msgstr ""
|
||||
msgstr "Kreiraj pravo pristupa"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.tv
|
||||
|
@ -582,7 +582,7 @@ msgstr "Francuska Guyana"
|
|||
#. module: base
|
||||
#: model:ir.module.module,summary:base.module_hr
|
||||
msgid "Jobs, Departments, Employees Details"
|
||||
msgstr ""
|
||||
msgstr "Radna mjesta, odjeli, podaci o radnicima"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_analytic
|
||||
|
@ -695,7 +695,7 @@ msgstr "Kolumbija"
|
|||
#. module: base
|
||||
#: model:res.partner.title,name:base.res_partner_title_mister
|
||||
msgid "Mister"
|
||||
msgstr ""
|
||||
msgstr "Gospodin"
|
||||
|
||||
#. module: base
|
||||
#: help:res.country,code:0
|
||||
|
@ -828,6 +828,11 @@ msgid ""
|
|||
"This module provides the Integration of the LinkedIn with OpenERP.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
"OpenERP Web LinkedIn modul.\n"
|
||||
"============================\n"
|
||||
"Integracija LinkedIn-a sa OpenERP-om.\n"
|
||||
" "
|
||||
|
||||
#. module: base
|
||||
#: help:ir.actions.act_window,src_model:0
|
||||
|
@ -1132,7 +1137,7 @@ msgstr "Google korisnici"
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_fleet
|
||||
msgid "Fleet Management"
|
||||
msgstr ""
|
||||
msgstr "Upravljanje flotom (automobili)"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.server.object.lines,value:0
|
||||
|
@ -1234,7 +1239,7 @@ msgstr "Zadnja verzija"
|
|||
#. module: base
|
||||
#: view:ir.rule:0
|
||||
msgid "Delete Access Right"
|
||||
msgstr ""
|
||||
msgstr "Prava brisanja"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/ir/ir_mail_server.py:213
|
||||
|
@ -1304,7 +1309,7 @@ msgstr "Vidljivo"
|
|||
#. module: base
|
||||
#: model:ir.actions.client,name:base.action_client_base_menu
|
||||
msgid "Open Settings Menu"
|
||||
msgstr ""
|
||||
msgstr "Otvori izbornik postavki"
|
||||
|
||||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
|
@ -1427,7 +1432,7 @@ msgstr "Haiti"
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_l10n_fr_hr_payroll
|
||||
msgid "French Payroll"
|
||||
msgstr ""
|
||||
msgstr "Francuska plaća"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.ui.view:0
|
||||
|
@ -2571,7 +2576,7 @@ msgstr "Grčka / Ελληνικά"
|
|||
#. module: base
|
||||
#: field:res.company,custom_footer:0
|
||||
msgid "Custom Footer"
|
||||
msgstr ""
|
||||
msgstr "Prilagođeno podnožje"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_sale_crm
|
||||
|
@ -2879,7 +2884,7 @@ msgstr "Naslijeđeno"
|
|||
#: code:addons/base/ir/ir_fields.py:147
|
||||
#, python-format
|
||||
msgid "yes"
|
||||
msgstr ""
|
||||
msgstr "da"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.model.fields,serialization_field_id:0
|
||||
|
@ -14826,7 +14831,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: field:ir.sequence,implementation:0
|
||||
msgid "Implementation"
|
||||
msgstr ""
|
||||
msgstr "Implementacija"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_l10n_ve
|
||||
|
@ -14931,7 +14936,7 @@ msgstr "Ažuriranje sustava"
|
|||
#: field:ir.actions.report.xml,report_sxw_content:0
|
||||
#: field:ir.actions.report.xml,report_sxw_content_data:0
|
||||
msgid "SXW Content"
|
||||
msgstr ""
|
||||
msgstr "SXW Sadržaj"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.sequence,prefix:0
|
||||
|
@ -14946,7 +14951,7 @@ msgstr "Sejšeli"
|
|||
#. module: base
|
||||
#: model:res.partner.category,name:base.res_partner_category_4
|
||||
msgid "Gold"
|
||||
msgstr ""
|
||||
msgstr "Zlatni"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/res/res_company.py:159
|
||||
|
@ -14973,7 +14978,7 @@ msgstr "Opći podaci"
|
|||
#. module: base
|
||||
#: field:ir.model.data,complete_name:0
|
||||
msgid "Complete ID"
|
||||
msgstr ""
|
||||
msgstr "Puni ID"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.tc
|
||||
|
@ -14985,7 +14990,7 @@ msgstr "Turks and Caicos Islands"
|
|||
msgid ""
|
||||
"Tax Identification Number. Check the box if this contact is subjected to "
|
||||
"taxes. Used by the some of the legal statements."
|
||||
msgstr ""
|
||||
msgstr "Porezni broj (OIB) ."
|
||||
|
||||
#. module: base
|
||||
#: field:res.partner.bank,partner_id:0
|
||||
|
@ -15070,7 +15075,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: view:res.partner:0
|
||||
msgid "Internal Notes"
|
||||
msgstr ""
|
||||
msgstr "Interne bilješke"
|
||||
|
||||
#. module: base
|
||||
#: selection:res.partner.address,type:0
|
||||
|
@ -15086,7 +15091,7 @@ msgstr "Corp."
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_purchase_requisition
|
||||
msgid "Purchase Requisitions"
|
||||
msgstr ""
|
||||
msgstr "Natječaji u nabavi"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.actions.act_window,target:0
|
||||
|
@ -15112,7 +15117,7 @@ msgstr "Partneri: "
|
|||
#. module: base
|
||||
#: view:res.partner:0
|
||||
msgid "Is a Company?"
|
||||
msgstr ""
|
||||
msgstr "Je tvrtka="
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/res/res_company.py:159
|
||||
|
@ -15134,7 +15139,7 @@ msgstr "Kreiraj objekt"
|
|||
#. module: base
|
||||
#: model:res.country,name:base.ss
|
||||
msgid "South Sudan"
|
||||
msgstr ""
|
||||
msgstr "Južni Sudan"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.filters,context:0
|
||||
|
|
|
@ -7,14 +7,14 @@ msgstr ""
|
|||
"Project-Id-Version: OpenERP Server 5.0.4\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2012-12-03 16:01+0000\n"
|
||||
"PO-Revision-Date: 2012-12-01 13:29+0000\n"
|
||||
"Last-Translator: Sergio Corato <Unknown>\n"
|
||||
"PO-Revision-Date: 2012-12-09 12:05+0000\n"
|
||||
"Last-Translator: Davide Corio - agilebg.com <davide.corio@agilebg.com>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-12-04 04:57+0000\n"
|
||||
"X-Generator: Launchpad (build 16335)\n"
|
||||
"X-Launchpad-Export-Date: 2012-12-10 04:36+0000\n"
|
||||
"X-Generator: Launchpad (build 16341)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
@ -92,7 +92,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:ir.module.module,summary:base.module_point_of_sale
|
||||
msgid "Touchscreen Interface for Shops"
|
||||
msgstr "Interfaccia Touchscreen per Negozzi"
|
||||
msgstr "Interfaccia Touchscreen per Negozi"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_l10n_in_hr_payroll
|
||||
|
@ -140,6 +140,9 @@ msgid ""
|
|||
"The module adds google user in res user.\n"
|
||||
"========================================\n"
|
||||
msgstr ""
|
||||
"\n"
|
||||
"Aggiunge lo username Google a res.user.\n"
|
||||
"========================================\n"
|
||||
|
||||
#. module: base
|
||||
#: help:res.partner,employee:0
|
||||
|
@ -322,7 +325,7 @@ msgstr "Gestione Vendite"
|
|||
msgid ""
|
||||
"The internal user that is in charge of communicating with this contact if "
|
||||
"any."
|
||||
msgstr ""
|
||||
msgstr "L'utente responsabile delle comunicazioni con questo contatto."
|
||||
|
||||
#. module: base
|
||||
#: view:res.partner:0
|
||||
|
|
|
@ -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-04 13:10+0000\n"
|
||||
"PO-Revision-Date: 2012-12-07 10:59+0000\n"
|
||||
"Last-Translator: Rui Franco (multibase.pt) <Unknown>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-12-05 05:18+0000\n"
|
||||
"X-Generator: Launchpad (build 16335)\n"
|
||||
"X-Launchpad-Export-Date: 2012-12-08 04:58+0000\n"
|
||||
"X-Generator: Launchpad (build 16341)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_account_check_writing
|
||||
|
@ -268,7 +268,7 @@ msgstr "Criado"
|
|||
#. module: base
|
||||
#: field:ir.actions.report.xml,report_xsl:0
|
||||
msgid "XSL Path"
|
||||
msgstr ""
|
||||
msgstr "Caminho do XSL"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_l10n_tr
|
||||
|
@ -570,7 +570,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: view:workflow.transition:0
|
||||
msgid "Workflow Transition"
|
||||
msgstr ""
|
||||
msgstr "Transição do fluxo de trabalho"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.gf
|
||||
|
@ -580,7 +580,7 @@ msgstr "Guiana Francesa"
|
|||
#. module: base
|
||||
#: model:ir.module.module,summary:base.module_hr
|
||||
msgid "Jobs, Departments, Employees Details"
|
||||
msgstr ""
|
||||
msgstr "Funções, departamentos, informações sobre os empregados"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_analytic
|
||||
|
@ -872,7 +872,7 @@ msgstr "Roménia - Contabilidade"
|
|||
#. module: base
|
||||
#: model:ir.model,name:base.model_res_config_settings
|
||||
msgid "res.config.settings"
|
||||
msgstr ""
|
||||
msgstr "res.config.settings"
|
||||
|
||||
#. module: base
|
||||
#: help:res.partner,image_small:0
|
||||
|
@ -1271,7 +1271,7 @@ msgstr "Ilhas Caimão"
|
|||
#. module: base
|
||||
#: view:ir.rule:0
|
||||
msgid "Record Rule"
|
||||
msgstr ""
|
||||
msgstr "Regra de gravação"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.kr
|
||||
|
@ -1314,7 +1314,7 @@ msgstr "Visível"
|
|||
#. module: base
|
||||
#: model:ir.actions.client,name:base.action_client_base_menu
|
||||
msgid "Open Settings Menu"
|
||||
msgstr ""
|
||||
msgstr "Abrir o menu de configuração"
|
||||
|
||||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
|
@ -1616,7 +1616,7 @@ msgstr "%Y - ."
|
|||
#. module: base
|
||||
#: view:res.company:0
|
||||
msgid "Report Footer Configuration"
|
||||
msgstr ""
|
||||
msgstr "Configuração do rodapé de relatório"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.translation,comments:0
|
||||
|
@ -1775,7 +1775,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: field:res.partner,image_small:0
|
||||
msgid "Small-sized image"
|
||||
msgstr ""
|
||||
msgstr "Imagem pequena"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_stock
|
||||
|
@ -1897,7 +1897,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: selection:ir.translation,state:0
|
||||
msgid "Translation in Progress"
|
||||
msgstr ""
|
||||
msgstr "Tradução em progresso"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_ir_rule
|
||||
|
@ -1912,7 +1912,7 @@ msgstr "Dias"
|
|||
#. module: base
|
||||
#: model:ir.module.module,summary:base.module_fleet
|
||||
msgid "Vehicle, leasing, insurances, costs"
|
||||
msgstr ""
|
||||
msgstr "Veículo, 'leasing', seguros, custos"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.model.access:0
|
||||
|
@ -1999,7 +1999,7 @@ msgstr "Criar tarefas no SO"
|
|||
#: code:addons/base/ir/ir_model.py:316
|
||||
#, python-format
|
||||
msgid "This column contains module data and cannot be removed!"
|
||||
msgstr ""
|
||||
msgstr "Esta coluna contém dados do módulo e não pode ser removida!"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.attachment,res_model:0
|
||||
|
@ -2744,7 +2744,7 @@ msgstr "ID do Registo"
|
|||
#. module: base
|
||||
#: view:ir.filters:0
|
||||
msgid "My Filters"
|
||||
msgstr ""
|
||||
msgstr "Os meus filtros"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.actions.server,email:0
|
||||
|
@ -2797,7 +2797,7 @@ msgstr "Argumentos enviados ao cliente junto com a tag vista"
|
|||
#. module: base
|
||||
#: model:ir.module.module,summary:base.module_contacts
|
||||
msgid "Contacts, People and Companies"
|
||||
msgstr ""
|
||||
msgstr "Contactos, pessoas e empresas"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.tt
|
||||
|
@ -2830,7 +2830,7 @@ msgstr "Gestor"
|
|||
#: code:addons/base/ir/ir_model.py:718
|
||||
#, python-format
|
||||
msgid "Sorry, you are not allowed to access this document."
|
||||
msgstr ""
|
||||
msgstr "Infelizmente, não está autorizado a aceder a este documento."
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.py
|
||||
|
@ -2845,7 +2845,7 @@ msgstr "Fiji"
|
|||
#. module: base
|
||||
#: view:ir.actions.report.xml:0
|
||||
msgid "Report Xml"
|
||||
msgstr ""
|
||||
msgstr "XML de Relatório"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_purchase
|
||||
|
@ -2916,7 +2916,7 @@ msgstr "Herdado"
|
|||
#: code:addons/base/ir/ir_fields.py:147
|
||||
#, python-format
|
||||
msgid "yes"
|
||||
msgstr ""
|
||||
msgstr "sim"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.model.fields,serialization_field_id:0
|
||||
|
@ -3007,7 +3007,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_web_linkedin
|
||||
msgid "LinkedIn Integration"
|
||||
msgstr ""
|
||||
msgstr "Integração com o LinkedIn"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/orm.py:2021
|
||||
|
@ -3252,7 +3252,7 @@ msgstr "Suécia"
|
|||
#. module: base
|
||||
#: field:ir.actions.report.xml,report_file:0
|
||||
msgid "Report File"
|
||||
msgstr ""
|
||||
msgstr "Ficheiro de relatório"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.actions.act_window.view,view_mode:0
|
||||
|
@ -3285,7 +3285,7 @@ msgstr ""
|
|||
#: code:addons/orm.py:3839
|
||||
#, python-format
|
||||
msgid "Missing document(s)"
|
||||
msgstr ""
|
||||
msgstr "Documento(s) em falta"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_res_partner_bank_type
|
||||
|
@ -3300,6 +3300,8 @@ msgid ""
|
|||
"For more details about translating OpenERP in your language, please refer to "
|
||||
"the"
|
||||
msgstr ""
|
||||
"Para mais informações acerca da tradução do OpenERP para o seu idioma, por "
|
||||
"favor, veja em"
|
||||
|
||||
#. module: base
|
||||
#: field:res.partner,image:0
|
||||
|
@ -3486,7 +3488,7 @@ msgstr "workflow.activity"
|
|||
#. module: base
|
||||
#: view:base.language.export:0
|
||||
msgid "Export Complete"
|
||||
msgstr ""
|
||||
msgstr "Exportação concluída"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.ui.view_sc,res_id:0
|
||||
|
@ -3577,6 +3579,8 @@ msgstr "Malta"
|
|||
msgid ""
|
||||
"Only users with the following access level are currently allowed to do that"
|
||||
msgstr ""
|
||||
"Apenas utilizadores com o nível de acesso indicado de seguida estão "
|
||||
"autorizados a fazer isso"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.actions.server,fields_lines:0
|
||||
|
@ -3667,7 +3671,7 @@ msgstr "Antártida"
|
|||
#. module: base
|
||||
#: view:res.partner:0
|
||||
msgid "Persons"
|
||||
msgstr ""
|
||||
msgstr "Pessoas"
|
||||
|
||||
#. module: base
|
||||
#: view:base.language.import:0
|
||||
|
@ -4144,7 +4148,7 @@ msgstr "Partilhar qualquer documento"
|
|||
#. module: base
|
||||
#: model:ir.module.module,summary:base.module_crm
|
||||
msgid "Leads, Opportunities, Phone Calls"
|
||||
msgstr ""
|
||||
msgstr "Dicas, oportunidades, telefonemas"
|
||||
|
||||
#. module: base
|
||||
#: view:res.lang:0
|
||||
|
@ -4269,7 +4273,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 "Gestão de veículos e contratos"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_base_setup
|
||||
|
@ -4519,7 +4523,7 @@ msgstr "Tipo de Sequência"
|
|||
#. module: base
|
||||
#: view:base.language.export:0
|
||||
msgid "Unicode/UTF-8"
|
||||
msgstr ""
|
||||
msgstr "Unicode/UTF-8"
|
||||
|
||||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
|
@ -4531,12 +4535,12 @@ 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 "Carregar uma tradução"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.module.module,latest_version:0
|
||||
msgid "Installed Version"
|
||||
msgstr ""
|
||||
msgstr "Versão instalada"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.module.module,license:0
|
||||
|
@ -4679,7 +4683,7 @@ msgstr "ir.actions.report.xml"
|
|||
#. module: base
|
||||
#: model:res.country,name:base.ps
|
||||
msgid "Palestinian Territory, Occupied"
|
||||
msgstr ""
|
||||
msgstr "Palestina"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_l10n_ch
|
||||
|
@ -4836,12 +4840,12 @@ msgstr ""
|
|||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
msgid "Portuguese (BR) / Português (BR)"
|
||||
msgstr ""
|
||||
msgstr "Português (BR)"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_ir_needaction_mixin
|
||||
msgid "ir.needaction_mixin"
|
||||
msgstr ""
|
||||
msgstr "ir.needaction_mixin"
|
||||
|
||||
#. module: base
|
||||
#: view:base.language.export:0
|
||||
|
@ -4861,13 +4865,13 @@ msgstr "Aplicações para uma Indústria Específica"
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_google_docs
|
||||
msgid "Google Docs integration"
|
||||
msgstr ""
|
||||
msgstr "Integração com o Google Docs"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/ir/ir_fields.py:328
|
||||
#, python-format
|
||||
msgid "name"
|
||||
msgstr ""
|
||||
msgstr "nome"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_mrp_operations
|
||||
|
@ -4923,7 +4927,7 @@ msgstr "Lesoto"
|
|||
#. module: base
|
||||
#: view:base.language.export:0
|
||||
msgid ", or your preferred text editor"
|
||||
msgstr ""
|
||||
msgstr ", ou o seu editor de texto preferido"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_crm_partner_assign
|
||||
|
@ -4990,12 +4994,12 @@ msgstr "Definir como Nulo"
|
|||
#. module: base
|
||||
#: view:res.users:0
|
||||
msgid "Save"
|
||||
msgstr ""
|
||||
msgstr "Gravar"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.actions.report.xml,report_xml:0
|
||||
msgid "XML Path"
|
||||
msgstr ""
|
||||
msgstr "Caminho do XML"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.bj
|
||||
|
@ -5077,7 +5081,7 @@ msgstr "Segurança"
|
|||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
msgid "Portuguese / Português"
|
||||
msgstr ""
|
||||
msgstr "Português"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/ir/ir_model.py:364
|
||||
|
@ -5168,7 +5172,7 @@ msgstr "Taxas"
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_email_template
|
||||
msgid "Email Templates"
|
||||
msgstr ""
|
||||
msgstr "Modelos de mensagens"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sy
|
||||
|
@ -5273,7 +5277,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: view:res.company:0
|
||||
msgid "Preview Header/Footer"
|
||||
msgstr ""
|
||||
msgstr "Antevisão do Cabeçalho/Rodapé"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.ui.menu,parent_id:0
|
||||
|
@ -5311,7 +5315,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:ir.model,name:base.model_res_partner_address
|
||||
msgid "res.partner.address"
|
||||
msgstr ""
|
||||
msgstr "res.partner.address"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.rule:0
|
||||
|
@ -5350,7 +5354,7 @@ msgstr "Histórico"
|
|||
#. module: base
|
||||
#: model:res.country,name:base.im
|
||||
msgid "Isle of Man"
|
||||
msgstr ""
|
||||
msgstr "Ilha de Man"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.actions.client,res_model:0
|
||||
|
@ -5513,7 +5517,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:res.groups,name:base.group_sale_salesman_all_leads
|
||||
msgid "See all Leads"
|
||||
msgstr ""
|
||||
msgstr "Ver todas as dicas"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.ci
|
||||
|
@ -5590,7 +5594,7 @@ msgstr "Configuração de precisão decimal"
|
|||
#: model:ir.model,name:base.model_ir_actions_act_url
|
||||
#: selection:ir.ui.menu,action:0
|
||||
msgid "ir.actions.act_url"
|
||||
msgstr ""
|
||||
msgstr "ir.actions.act_ur"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.ui.menu,name:base.menu_translation_app
|
||||
|
@ -5743,7 +5747,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: view:ir.translation:0
|
||||
msgid "Comments"
|
||||
msgstr ""
|
||||
msgstr "Comentários"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.et
|
||||
|
@ -5753,7 +5757,7 @@ msgstr "Etiópia"
|
|||
#. module: base
|
||||
#: model:ir.module.category,name:base.module_category_authentication
|
||||
msgid "Authentication"
|
||||
msgstr ""
|
||||
msgstr "Autenticação"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sj
|
||||
|
@ -5787,7 +5791,7 @@ msgstr "Título"
|
|||
#: code:addons/base/ir/ir_fields.py:147
|
||||
#, python-format
|
||||
msgid "true"
|
||||
msgstr ""
|
||||
msgstr "verdadeiro"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_base_language_install
|
||||
|
@ -5797,7 +5801,7 @@ msgstr "Instalar Idioma"
|
|||
#. module: base
|
||||
#: model:res.partner.category,name:base.res_partner_category_11
|
||||
msgid "Services"
|
||||
msgstr ""
|
||||
msgstr "Serviços"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.translation:0
|
||||
|
@ -5901,7 +5905,7 @@ msgstr ""
|
|||
#: code:addons/base/ir/workflow/workflow.py:99
|
||||
#, python-format
|
||||
msgid "Operation forbidden"
|
||||
msgstr ""
|
||||
msgstr "Operação proibida"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.actions.server:0
|
||||
|
@ -5993,7 +5997,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_l10n_be_hr_payroll
|
||||
msgid "Belgium - Payroll"
|
||||
msgstr ""
|
||||
msgstr "Bélgica - folha de pagamentos"
|
||||
|
||||
#. module: base
|
||||
#: view:workflow.activity:0
|
||||
|
@ -6025,7 +6029,7 @@ msgstr "Nome do Recurso"
|
|||
#. module: base
|
||||
#: field:res.partner,is_company:0
|
||||
msgid "Is a Company"
|
||||
msgstr ""
|
||||
msgstr "É uma empresa"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.cron,interval_type:0
|
||||
|
@ -6076,6 +6080,7 @@ msgstr ""
|
|||
msgid ""
|
||||
"Administrative divisions of a country. E.g. Fed. State, Departement, Canton"
|
||||
msgstr ""
|
||||
"Divisões administrativas de um país. Exemplos: Distrito, Concelho, Freguesia"
|
||||
|
||||
#. module: base
|
||||
#: view:res.partner.bank:0
|
||||
|
@ -6105,7 +6110,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: sql_constraint:ir.filters:0
|
||||
msgid "Filter names must be unique"
|
||||
msgstr ""
|
||||
msgstr "Os nomes dos filtros devem ser únicos"
|
||||
|
||||
#. module: base
|
||||
#: help:multi_company.default,object_id:0
|
||||
|
@ -6120,7 +6125,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: field:ir.filters,is_default:0
|
||||
msgid "Default filter"
|
||||
msgstr ""
|
||||
msgstr "Filtro padrão"
|
||||
|
||||
#. module: base
|
||||
#: report:ir.module.reference:0
|
||||
|
@ -6237,7 +6242,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_auth_reset_password
|
||||
msgid "Reset Password"
|
||||
msgstr ""
|
||||
msgstr "Repor a senha"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.attachment:0
|
||||
|
@ -6254,7 +6259,7 @@ msgstr "Malásia"
|
|||
#: code:addons/base/ir/ir_sequence.py:131
|
||||
#, python-format
|
||||
msgid "Increment number must not be zero."
|
||||
msgstr ""
|
||||
msgstr "O valor de incremento não pode ser zero."
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_account_cancel
|
||||
|
@ -6454,7 +6459,7 @@ msgstr "ir.cron"
|
|||
#. module: base
|
||||
#: model:res.country,name:base.cw
|
||||
msgid "Curaçao"
|
||||
msgstr ""
|
||||
msgstr "Curaçao"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.sequence:0
|
||||
|
@ -6601,7 +6606,7 @@ msgstr "Israel"
|
|||
#: code:addons/base/res/res_config.py:444
|
||||
#, python-format
|
||||
msgid "Cannot duplicate configuration!"
|
||||
msgstr ""
|
||||
msgstr "Não se pode duplicar a configuração!"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_l10n_syscohada
|
||||
|
@ -6655,7 +6660,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:res.groups,name:base.group_partner_manager
|
||||
msgid "Contact Creation"
|
||||
msgstr ""
|
||||
msgstr "Criação de contacto"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.module.module:0
|
||||
|
@ -6711,7 +6716,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: view:ir.sequence:0
|
||||
msgid "Week of the Year: %(woy)s"
|
||||
msgstr ""
|
||||
msgstr "Semana (do ano): %(woy)s"
|
||||
|
||||
#. module: base
|
||||
#: field:res.users,id:0
|
||||
|
@ -6780,7 +6785,7 @@ msgstr "Títulos do Parceiro"
|
|||
#: code:addons/base/ir/ir_fields.py:228
|
||||
#, python-format
|
||||
msgid "Use the format '%s'"
|
||||
msgstr ""
|
||||
msgstr "Use o formato '%s'"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.actions.act_window,auto_refresh:0
|
||||
|
@ -6812,12 +6817,12 @@ msgstr "WorkItems"
|
|||
#: code:addons/base/res/res_bank.py:195
|
||||
#, python-format
|
||||
msgid "Invalid Bank Account Type Name format."
|
||||
msgstr ""
|
||||
msgstr "Formato inválido para o nome do tipo de conta bancária."
|
||||
|
||||
#. module: base
|
||||
#: view:ir.filters:0
|
||||
msgid "Filters visible only for one user"
|
||||
msgstr ""
|
||||
msgstr "Filtros visíveis só para o utilizador"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_ir_attachment
|
||||
|
@ -6837,7 +6842,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_auth_anonymous
|
||||
msgid "Anonymous"
|
||||
msgstr ""
|
||||
msgstr "Anónimo"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_base_import
|
||||
|
@ -6993,7 +6998,7 @@ msgstr "Somália"
|
|||
#. module: base
|
||||
#: model:res.partner.title,shortcut:base.res_partner_title_doctor
|
||||
msgid "Dr."
|
||||
msgstr ""
|
||||
msgstr "Dr."
|
||||
|
||||
#. module: base
|
||||
#: model:res.groups,name:base.group_user
|
||||
|
@ -7058,7 +7063,7 @@ msgstr "Copiar de"
|
|||
#. module: base
|
||||
#: field:ir.model.data,display_name:0
|
||||
msgid "Record Name"
|
||||
msgstr ""
|
||||
msgstr "Nome do registo"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_ir_actions_client
|
||||
|
@ -7074,7 +7079,7 @@ msgstr "Território Britânico do Oceano Índico"
|
|||
#. module: base
|
||||
#: model:ir.actions.server,name:base.action_server_module_immediate_install
|
||||
msgid "Module Immediate Install"
|
||||
msgstr ""
|
||||
msgstr "Instalação imediata do módulo"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.actions.server:0
|
||||
|
@ -7201,7 +7206,7 @@ msgstr "Em múltiplos docs"
|
|||
#: view:res.users:0
|
||||
#: view:wizard.ir.model.menu.create:0
|
||||
msgid "or"
|
||||
msgstr ""
|
||||
msgstr "ou"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_account_accountant
|
||||
|
@ -7344,7 +7349,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:res.partner.category,name:base.res_partner_category_14
|
||||
msgid "Manufacturer"
|
||||
msgstr ""
|
||||
msgstr "Fabricante"
|
||||
|
||||
#. module: base
|
||||
#: help:res.users,company_id:0
|
||||
|
@ -7794,7 +7799,7 @@ msgstr "init"
|
|||
#: view:res.partner:0
|
||||
#: field:res.partner,user_id:0
|
||||
msgid "Salesperson"
|
||||
msgstr ""
|
||||
msgstr "Vendedor"
|
||||
|
||||
#. module: base
|
||||
#: view:res.lang:0
|
||||
|
@ -7852,7 +7857,7 @@ msgstr "Password opcional para autenticação SMTP"
|
|||
#: code:addons/base/ir/ir_model.py:719
|
||||
#, python-format
|
||||
msgid "Sorry, you are not allowed to modify this document."
|
||||
msgstr ""
|
||||
msgstr "Infelizmente, não pode alterar este documento."
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/res/res_config.py:350
|
||||
|
@ -7942,7 +7947,7 @@ msgstr "Francês (CH) / Français (CH)"
|
|||
#. module: base
|
||||
#: model:res.partner.category,name:base.res_partner_category_13
|
||||
msgid "Distributor"
|
||||
msgstr ""
|
||||
msgstr "Distribuidor"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.actions.server,subject:0
|
||||
|
@ -8157,7 +8162,7 @@ msgstr "Contabilidade Analítica"
|
|||
#. module: base
|
||||
#: model:ir.model,name:base.model_ir_model_constraint
|
||||
msgid "ir.model.constraint"
|
||||
msgstr ""
|
||||
msgstr "ir.model.constraint"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_web_graph
|
||||
|
@ -8190,7 +8195,7 @@ msgstr "Bélgica - Contabilidade"
|
|||
#. module: base
|
||||
#: view:ir.model.access:0
|
||||
msgid "Access Control"
|
||||
msgstr ""
|
||||
msgstr "Controlo de acesso"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.kw
|
||||
|
@ -8294,7 +8299,7 @@ msgstr "Filipinas"
|
|||
#. module: base
|
||||
#: model:ir.module.module,summary:base.module_hr_timesheet_sheet
|
||||
msgid "Timesheets, Attendances, Activities"
|
||||
msgstr ""
|
||||
msgstr "Folhas de horas, assiduidade, atividades"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.ma
|
||||
|
@ -8407,7 +8412,7 @@ msgstr "Relatório de introspeção em objetos"
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_web_analytics
|
||||
msgid "Google Analytics"
|
||||
msgstr ""
|
||||
msgstr "Google Analytics"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_note
|
||||
|
@ -8435,7 +8440,7 @@ msgstr "Dominica"
|
|||
#. module: base
|
||||
#: field:ir.translation,name:0
|
||||
msgid "Translated field"
|
||||
msgstr ""
|
||||
msgstr "Traduzir campo"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_stock_location
|
||||
|
@ -8649,7 +8654,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: view:base.language.export:0
|
||||
msgid "documentation"
|
||||
msgstr ""
|
||||
msgstr "documentação"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.model,osv_memory:0
|
||||
|
@ -8694,7 +8699,7 @@ msgstr "%b - Nome do mês abreviado."
|
|||
#: code:addons/base/ir/ir_model.py:721
|
||||
#, python-format
|
||||
msgid "Sorry, you are not allowed to delete this document."
|
||||
msgstr ""
|
||||
msgstr "Infelizmente, não pode apagar este documento."
|
||||
|
||||
#. module: base
|
||||
#: constraint:ir.rule:0
|
||||
|
@ -8768,7 +8773,7 @@ msgstr "Samoa Americana"
|
|||
#. module: base
|
||||
#: view:ir.attachment:0
|
||||
msgid "My Document(s)"
|
||||
msgstr ""
|
||||
msgstr "Os meus documentos"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.actions.act_window,res_model:0
|
||||
|
@ -8850,7 +8855,7 @@ msgstr ""
|
|||
#. 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
|
||||
|
@ -9047,7 +9052,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:ir.module.category,name:base.module_category_warehouse_management
|
||||
msgid "Warehouse"
|
||||
msgstr ""
|
||||
msgstr "Armazém"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.exports,resource:0
|
||||
|
@ -9096,7 +9101,7 @@ msgstr "Relatório"
|
|||
#. module: base
|
||||
#: model:res.partner.title,shortcut:base.res_partner_title_prof
|
||||
msgid "Prof."
|
||||
msgstr ""
|
||||
msgstr "Prof."
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/ir/ir_mail_server.py:239
|
||||
|
@ -9228,7 +9233,7 @@ msgstr "Ref. do Parceiro"
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_hr_expense
|
||||
msgid "Expense Management"
|
||||
msgstr ""
|
||||
msgstr "Gestão de despesas"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.attachment,create_date:0
|
||||
|
@ -9307,7 +9312,7 @@ msgstr "Modelos"
|
|||
#: code:addons/base/module/module.py:472
|
||||
#, python-format
|
||||
msgid "The `base` module cannot be uninstalled"
|
||||
msgstr ""
|
||||
msgstr "O módulo \"base\" não pode ser desinstalado"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/ir/ir_cron.py:390
|
||||
|
@ -9334,7 +9339,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 "Infelizmente, não pode criar este tipo de documento."
|
||||
|
||||
#. module: base
|
||||
#: field:base.language.export,lang:0
|
||||
|
@ -9376,7 +9381,7 @@ msgstr "%H - Hora (24-horas) [00,23]."
|
|||
#. module: base
|
||||
#: field:ir.model.fields,on_delete:0
|
||||
msgid "On Delete"
|
||||
msgstr ""
|
||||
msgstr "Ao apagar"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/ir/ir_model.py:340
|
||||
|
@ -9584,7 +9589,7 @@ msgstr "O código da moeda deve ser único por empresa!"
|
|||
#: code:addons/base/module/wizard/base_export_language.py:38
|
||||
#, python-format
|
||||
msgid "New Language (Empty translation template)"
|
||||
msgstr ""
|
||||
msgstr "Novo idioma (modelo de tradução limpo)"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_auth_reset_password
|
||||
|
@ -9654,7 +9659,7 @@ msgstr "Egipto"
|
|||
#. module: base
|
||||
#: view:ir.attachment:0
|
||||
msgid "Creation"
|
||||
msgstr ""
|
||||
msgstr "Criação"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.actions.server,model_id:0
|
||||
|
@ -9726,7 +9731,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: field:res.partner,use_parent_address:0
|
||||
msgid "Use Company Address"
|
||||
msgstr ""
|
||||
msgstr "Usar o endereço da empresa"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,summary:base.module_hr_holidays
|
||||
|
@ -10107,7 +10112,7 @@ msgstr "Gravar Regras"
|
|||
#. module: base
|
||||
#: view:multi_company.default:0
|
||||
msgid "Multi Company"
|
||||
msgstr ""
|
||||
msgstr "Multi-empresas"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.category,name:base.module_category_portal
|
||||
|
@ -10118,13 +10123,13 @@ msgstr "Portal"
|
|||
#. module: base
|
||||
#: selection:ir.translation,state:0
|
||||
msgid "To Translate"
|
||||
msgstr ""
|
||||
msgstr "Para traduzir"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/ir/ir_fields.py:295
|
||||
#, python-format
|
||||
msgid "See all possible values"
|
||||
msgstr ""
|
||||
msgstr "Ver todos os valores possíveis"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_claim_from_delivery
|
||||
|
@ -10291,7 +10296,7 @@ msgstr "Itália"
|
|||
#. module: base
|
||||
#: model:res.groups,name:base.group_sale_salesman
|
||||
msgid "See Own Leads"
|
||||
msgstr ""
|
||||
msgstr "Ver as próprias dicas"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.actions.todo:0
|
||||
|
@ -10523,7 +10528,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_sale_stock
|
||||
msgid "Sales and Warehouse Management"
|
||||
msgstr ""
|
||||
msgstr "Gestão de vendas e de armazém"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.model.fields,model:0
|
||||
|
@ -10664,7 +10669,7 @@ msgstr ""
|
|||
#: code:addons/base/ir/ir_model.py:1023
|
||||
#, python-format
|
||||
msgid "Permission Denied"
|
||||
msgstr ""
|
||||
msgstr "Permissão negada"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.ui.menu,child_id:0
|
||||
|
@ -10809,7 +10814,7 @@ msgstr ""
|
|||
#. module: base
|
||||
#: model:res.country,name:base.bl
|
||||
msgid "Saint Barthélémy"
|
||||
msgstr ""
|
||||
msgstr "Saint Barthélémy"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.module.module,license:0
|
||||
|
@ -10890,12 +10895,12 @@ msgstr ""
|
|||
#: model:ir.actions.act_window,name:base.action_inventory_form
|
||||
#: model:ir.ui.menu,name:base.menu_action_inventory_form
|
||||
msgid "Default Company per Object"
|
||||
msgstr ""
|
||||
msgstr "Empresa padrão por objeto"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.ui.menu,needaction_counter:0
|
||||
msgid "Number of actions the user has to perform"
|
||||
msgstr ""
|
||||
msgstr "Número de ações que o utilizador tem de realizar"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_web_hello
|
||||
|
@ -11195,7 +11200,7 @@ msgstr "Operações de Produção"
|
|||
#. module: base
|
||||
#: view:base.language.export:0
|
||||
msgid "Here is the exported translation file:"
|
||||
msgstr ""
|
||||
msgstr "Aqui está o ficheiro de tradução:"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.actions.report.xml,report_rml_content:0
|
||||
|
@ -11465,7 +11470,7 @@ msgstr "ir.fields.converter"
|
|||
#: code:addons/base/res/res_partner.py:436
|
||||
#, python-format
|
||||
msgid "Couldn't create contact without email address !"
|
||||
msgstr ""
|
||||
msgstr "Não foi possível criar o contacto sem o endereço eletrónico!"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.category,name:base.module_category_manufacturing
|
||||
|
@ -11717,7 +11722,7 @@ msgstr "UK - Contabilidade"
|
|||
#. module: base
|
||||
#: model:res.partner.title,shortcut:base.res_partner_title_madam
|
||||
msgid "Mrs."
|
||||
msgstr ""
|
||||
msgstr "Srª."
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/ir/ir_model.py:424
|
||||
|
@ -11753,7 +11758,7 @@ msgstr "Repetição da Expressão"
|
|||
#. module: base
|
||||
#: model:res.partner.category,name:base.res_partner_category_16
|
||||
msgid "Retailer"
|
||||
msgstr ""
|
||||
msgstr "Revendedor"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.model.fields:0
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -42,7 +42,7 @@ class actions(osv.osv):
|
|||
_order = 'name'
|
||||
_columns = {
|
||||
'name': fields.char('Name', size=64, required=True),
|
||||
'type': fields.char('Action Type', required=True, size=32,readonly=True),
|
||||
'type': fields.char('Action Type', required=True, size=32),
|
||||
'usage': fields.char('Action Usage', size=32),
|
||||
'help': fields.text('Action description',
|
||||
help='Optional help text for the users with a description of the target view, such as its usage and purpose.',
|
||||
|
|
|
@ -31,7 +31,6 @@ import netsvc
|
|||
import openerp
|
||||
import pooler
|
||||
import tools
|
||||
from openerp.cron import WAKE_UP_NOW
|
||||
from osv import fields, osv
|
||||
from tools import DEFAULT_SERVER_DATETIME_FORMAT
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
|
@ -142,130 +141,6 @@ class ir_cron(osv.osv):
|
|||
except Exception, e:
|
||||
self._handle_callback_exception(cr, uid, model_name, method_name, args, job_id, e)
|
||||
|
||||
def _run_job(self, cr, job, now):
|
||||
""" Run a given job taking care of the repetition.
|
||||
|
||||
The cursor has a lock on the job (aquired by _run_jobs_multithread()) and this
|
||||
method is run in a worker thread (spawned by _run_jobs_multithread())).
|
||||
|
||||
:param job: job to be run (as a dictionary).
|
||||
:param now: timestamp (result of datetime.now(), no need to call it multiple time).
|
||||
|
||||
"""
|
||||
try:
|
||||
nextcall = datetime.strptime(job['nextcall'], DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
numbercall = job['numbercall']
|
||||
|
||||
ok = False
|
||||
while nextcall < now and numbercall:
|
||||
if numbercall > 0:
|
||||
numbercall -= 1
|
||||
if not ok or job['doall']:
|
||||
self._callback(cr, job['user_id'], job['model'], job['function'], job['args'], job['id'])
|
||||
if numbercall:
|
||||
nextcall += _intervalTypes[job['interval_type']](job['interval_number'])
|
||||
ok = True
|
||||
addsql = ''
|
||||
if not numbercall:
|
||||
addsql = ', active=False'
|
||||
cr.execute("UPDATE ir_cron SET nextcall=%s, numbercall=%s"+addsql+" WHERE id=%s",
|
||||
(nextcall.strftime(DEFAULT_SERVER_DATETIME_FORMAT), numbercall, job['id']))
|
||||
|
||||
if numbercall:
|
||||
# Reschedule our own main cron thread if necessary.
|
||||
# This is really needed if this job runs longer than its rescheduling period.
|
||||
nextcall = calendar.timegm(nextcall.timetuple())
|
||||
openerp.cron.schedule_wakeup(nextcall, cr.dbname)
|
||||
finally:
|
||||
cr.commit()
|
||||
cr.close()
|
||||
openerp.cron.release_thread_slot()
|
||||
|
||||
def _run_jobs_multithread(self):
|
||||
# TODO remove 'check' argument from addons/base_action_rule/base_action_rule.py
|
||||
""" Process the cron jobs by spawning worker threads.
|
||||
|
||||
This selects in database all the jobs that should be processed. It then
|
||||
tries to lock each of them and, if it succeeds, spawns a thread to run
|
||||
the cron job (if it doesn't succeed, it means the job was already
|
||||
locked to be taken care of by another thread).
|
||||
|
||||
The cursor used to lock the job in database is given to the worker
|
||||
thread (which has to close it itself).
|
||||
|
||||
"""
|
||||
db = self.pool.db
|
||||
cr = db.cursor()
|
||||
db_name = db.dbname
|
||||
try:
|
||||
jobs = {} # mapping job ids to jobs for all jobs being processed.
|
||||
now = datetime.now()
|
||||
# Careful to compare timestamps with 'UTC' - everything is UTC as of v6.1.
|
||||
cr.execute("""SELECT * FROM ir_cron
|
||||
WHERE numbercall != 0
|
||||
AND active AND nextcall <= (now() at time zone 'UTC')
|
||||
ORDER BY priority""")
|
||||
for job in cr.dictfetchall():
|
||||
if not openerp.cron.get_thread_slots():
|
||||
break
|
||||
jobs[job['id']] = job
|
||||
|
||||
task_cr = db.cursor()
|
||||
try:
|
||||
# Try to grab an exclusive lock on the job row from within the task transaction
|
||||
acquired_lock = False
|
||||
task_cr.execute("""SELECT *
|
||||
FROM ir_cron
|
||||
WHERE id=%s
|
||||
FOR UPDATE NOWAIT""",
|
||||
(job['id'],), log_exceptions=False)
|
||||
acquired_lock = True
|
||||
except psycopg2.OperationalError, e:
|
||||
if e.pgcode == '55P03':
|
||||
# Class 55: Object not in prerequisite state; 55P03: lock_not_available
|
||||
_logger.debug('Another process/thread is already busy executing job `%s`, skipping it.', job['name'])
|
||||
continue
|
||||
else:
|
||||
# Unexpected OperationalError
|
||||
raise
|
||||
finally:
|
||||
if not acquired_lock:
|
||||
# we're exiting due to an exception while acquiring the lot
|
||||
task_cr.close()
|
||||
|
||||
# Got the lock on the job row, now spawn a thread to execute it in the transaction with the lock
|
||||
task_thread = threading.Thread(target=self._run_job, name=job['name'], args=(task_cr, job, now))
|
||||
# force non-daemon task threads (the runner thread must be daemon, and this property is inherited by default)
|
||||
task_thread.setDaemon(False)
|
||||
openerp.cron.take_thread_slot()
|
||||
task_thread.start()
|
||||
_logger.debug('Cron execution thread for job `%s` spawned', job['name'])
|
||||
|
||||
# Find next earliest job ignoring currently processed jobs (by this and other cron threads)
|
||||
find_next_time_query = """SELECT min(nextcall) AS min_next_call
|
||||
FROM ir_cron WHERE numbercall != 0 AND active"""
|
||||
if jobs:
|
||||
cr.execute(find_next_time_query + " AND id NOT IN %s", (tuple(jobs.keys()),))
|
||||
else:
|
||||
cr.execute(find_next_time_query)
|
||||
next_call = cr.dictfetchone()['min_next_call']
|
||||
|
||||
if next_call:
|
||||
next_call = calendar.timegm(time.strptime(next_call, DEFAULT_SERVER_DATETIME_FORMAT))
|
||||
else:
|
||||
# no matching cron job found in database, re-schedule arbitrarily in 1 day,
|
||||
# this delay will likely be modified when running jobs complete their tasks
|
||||
next_call = time.time() + (24*3600)
|
||||
|
||||
openerp.cron.schedule_wakeup(next_call, db_name)
|
||||
|
||||
except Exception, ex:
|
||||
_logger.warning('Exception in cron:', exc_info=True)
|
||||
|
||||
finally:
|
||||
cr.commit()
|
||||
cr.close()
|
||||
|
||||
def _process_job(self, cr, job):
|
||||
""" Run a given job taking care of the repetition.
|
||||
|
||||
|
@ -365,19 +240,6 @@ class ir_cron(osv.osv):
|
|||
|
||||
return False
|
||||
|
||||
def update_running_cron(self, cr):
|
||||
""" Schedule as soon as possible a wake-up for this database. """
|
||||
# Verify whether the server is already started and thus whether we need to commit
|
||||
# immediately our changes and restart the cron agent in order to apply the change
|
||||
# immediately. The commit() is needed because as soon as the cron is (re)started it
|
||||
# will query the database with its own cursor, possibly before the end of the
|
||||
# current transaction.
|
||||
# This commit() is not an issue in most cases, but we must absolutely avoid it
|
||||
# when the server is only starting or loading modules (hence the test on pool._init).
|
||||
if not self.pool._init:
|
||||
cr.commit()
|
||||
openerp.cron.schedule_wakeup(WAKE_UP_NOW, self.pool.db.dbname)
|
||||
|
||||
def _try_lock(self, cr, uid, ids, context=None):
|
||||
"""Try to grab a dummy exclusive write-lock to the rows with the given ids,
|
||||
to make sure a following write() or unlink() will not block due
|
||||
|
@ -393,20 +255,16 @@ class ir_cron(osv.osv):
|
|||
|
||||
def create(self, cr, uid, vals, context=None):
|
||||
res = super(ir_cron, self).create(cr, uid, vals, context=context)
|
||||
self.update_running_cron(cr)
|
||||
return res
|
||||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
self._try_lock(cr, uid, ids, context)
|
||||
res = super(ir_cron, self).write(cr, uid, ids, vals, context=context)
|
||||
self.update_running_cron(cr)
|
||||
return res
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
self._try_lock(cr, uid, ids, context)
|
||||
res = super(ir_cron, self).unlink(cr, uid, ids, context=context)
|
||||
self.update_running_cron(cr)
|
||||
return res
|
||||
ir_cron()
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -43,7 +43,7 @@ class ir_ui_menu(osv.osv):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.cache_lock = threading.RLock()
|
||||
self.clear_cache()
|
||||
self._cache = {}
|
||||
r = super(ir_ui_menu, self).__init__(*args, **kwargs)
|
||||
self.pool.get('ir.model.access').register_cache_clearing_method(self._name, 'clear_cache')
|
||||
return r
|
||||
|
@ -51,6 +51,10 @@ class ir_ui_menu(osv.osv):
|
|||
def clear_cache(self):
|
||||
with self.cache_lock:
|
||||
# radical but this doesn't frequently happen
|
||||
if self._cache:
|
||||
# Normally this is done by openerp.tools.ormcache
|
||||
# but since we do not use it, set it by ourself.
|
||||
self.pool._any_cache_cleared = True
|
||||
self._cache = {}
|
||||
|
||||
def _filter_visible_menus(self, cr, uid, ids, context=None):
|
||||
|
@ -265,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')
|
||||
|
@ -286,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. "\
|
||||
|
@ -296,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=[
|
||||
|
@ -317,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"
|
||||
|
||||
|
|
|
@ -406,6 +406,8 @@ class module(osv.osv):
|
|||
# Mark them to be installed.
|
||||
if to_install_ids:
|
||||
self.button_install(cr, uid, to_install_ids, context=context)
|
||||
|
||||
openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
|
||||
return dict(ACTION_DICT, name=_('Install'))
|
||||
|
||||
def button_immediate_install(self, cr, uid, ids, context=None):
|
||||
|
|
|
@ -258,7 +258,7 @@
|
|||
</group>
|
||||
<group>
|
||||
<field name="customer"/>
|
||||
<field name="supplier" invisible="not context.get('default_supplier')"/>
|
||||
<field name="supplier"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="ref"/>
|
||||
|
|
|
@ -2,6 +2,8 @@ import logging
|
|||
import sys
|
||||
|
||||
import openerp
|
||||
from openerp import tools
|
||||
from openerp.modules import module
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -32,8 +34,25 @@ import server
|
|||
|
||||
def main():
|
||||
args = sys.argv[1:]
|
||||
|
||||
# The only shared option is '--addons-path=' needed to discover additional
|
||||
# commands from modules
|
||||
if len(args) > 1 and args[0].startswith('--addons-path=') and not args[1].startswith("-"):
|
||||
tools.config.parse_config([args[0]])
|
||||
args = args[1:]
|
||||
|
||||
# Default legacy command
|
||||
command = "server"
|
||||
|
||||
# Subcommand discovery
|
||||
if len(args) and not args[0].startswith("-"):
|
||||
for m in module.get_modules():
|
||||
m = 'openerp.addons.' + m
|
||||
__import__(m)
|
||||
#try:
|
||||
#except Exception, e:
|
||||
# raise
|
||||
# print e
|
||||
command = args[0]
|
||||
args = args[1:]
|
||||
|
||||
|
|
|
@ -35,10 +35,6 @@ must be used.
|
|||
|
||||
import deprecation
|
||||
|
||||
# Maximum number of threads processing concurrently cron jobs.
|
||||
max_cron_threads = 4 # Actually the default value here is meaningless,
|
||||
# look at tools.config for the default value.
|
||||
|
||||
# Paths to search for OpenERP addons.
|
||||
addons_paths = []
|
||||
|
||||
|
|
212
openerp/cron.py
212
openerp/cron.py
|
@ -1,212 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2011 OpenERP SA (<http://www.openerp.com>)
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
""" Cron jobs scheduling
|
||||
|
||||
Cron jobs are defined in the ir_cron table/model. This module deals with all
|
||||
cron jobs, for all databases of a single OpenERP server instance.
|
||||
|
||||
It defines a single master thread that will spawn (a bounded number of)
|
||||
threads to process individual cron jobs.
|
||||
|
||||
The thread runs forever, checking every 60 seconds for new
|
||||
'database wake-ups'. It maintains a heapq of database wake-ups. At each
|
||||
wake-up, it will call ir_cron._run_jobs_multithread() for the given database. _run_jobs_multithread
|
||||
will check the jobs defined in the ir_cron table and spawn accordingly threads
|
||||
to process them.
|
||||
|
||||
This module's behavior depends on the following configuration variable:
|
||||
openerp.conf.max_cron_threads.
|
||||
|
||||
"""
|
||||
|
||||
import heapq
|
||||
import logging
|
||||
import threading
|
||||
import time
|
||||
|
||||
import openerp
|
||||
import tools
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
# Heapq of database wake-ups. Note that 'database wake-up' meaning is in
|
||||
# the context of the cron management. This is not originally about loading
|
||||
# a database, although having the database name in the queue will
|
||||
# cause it to be loaded when the schedule time is reached, even if it was
|
||||
# unloaded in the mean time. Normally a database's wake-up is cancelled by
|
||||
# the RegistryManager when the database is unloaded - so this should not
|
||||
# cause it to be reloaded.
|
||||
#
|
||||
# TODO: perhaps in the future we could consider a flag on ir.cron jobs
|
||||
# that would cause database wake-up even if the database has not been
|
||||
# loaded yet or was already unloaded (e.g. 'force_db_wakeup' or something)
|
||||
#
|
||||
# Each element is a triple (timestamp, database-name, boolean). The boolean
|
||||
# specifies if the wake-up is canceled (so a wake-up can be canceled without
|
||||
# relying on the heapq implementation detail; no need to remove the job from
|
||||
# the heapq).
|
||||
_wakeups = []
|
||||
|
||||
# Mapping of database names to the wake-up defined in the heapq,
|
||||
# so that we can cancel the wake-up without messing with the heapq
|
||||
# invariant: lookup the wake-up by database-name, then set
|
||||
# its third element to True.
|
||||
_wakeup_by_db = {}
|
||||
|
||||
# Re-entrant lock to protect the above _wakeups and _wakeup_by_db variables.
|
||||
# We could use a simple (non-reentrant) lock if the runner function below
|
||||
# was more fine-grained, but we are fine with the loop owning the lock
|
||||
# while spawning a few threads.
|
||||
_wakeups_lock = threading.RLock()
|
||||
|
||||
# Maximum number of threads allowed to process cron jobs concurrently. This
|
||||
# variable is set by start_master_thread using openerp.conf.max_cron_threads.
|
||||
_thread_slots = None
|
||||
|
||||
# A (non re-entrant) lock to protect the above _thread_slots variable.
|
||||
_thread_slots_lock = threading.Lock()
|
||||
|
||||
# Sleep duration limits - must not loop too quickly, but can't sleep too long
|
||||
# either, because a new job might be inserted in ir_cron with a much sooner
|
||||
# execution date than current known ones. We won't see it until we wake!
|
||||
MAX_SLEEP = 60 # 1 min
|
||||
MIN_SLEEP = 1 # 1 sec
|
||||
|
||||
# Dummy wake-up timestamp that can be used to force a database wake-up asap
|
||||
WAKE_UP_NOW = 1
|
||||
|
||||
def get_thread_slots():
|
||||
""" Return the number of available thread slots """
|
||||
return _thread_slots
|
||||
|
||||
|
||||
def release_thread_slot():
|
||||
""" Increment the number of available thread slots """
|
||||
global _thread_slots
|
||||
with _thread_slots_lock:
|
||||
_thread_slots += 1
|
||||
|
||||
|
||||
def take_thread_slot():
|
||||
""" Decrement the number of available thread slots """
|
||||
global _thread_slots
|
||||
with _thread_slots_lock:
|
||||
_thread_slots -= 1
|
||||
|
||||
|
||||
def cancel(db_name):
|
||||
""" Cancel the next wake-up of a given database, if any.
|
||||
|
||||
:param db_name: database name for which the wake-up is canceled.
|
||||
|
||||
"""
|
||||
_logger.debug("Cancel next wake-up for database '%s'.", db_name)
|
||||
with _wakeups_lock:
|
||||
if db_name in _wakeup_by_db:
|
||||
_wakeup_by_db[db_name][2] = True
|
||||
|
||||
|
||||
def cancel_all():
|
||||
""" Cancel all database wake-ups. """
|
||||
_logger.debug("Cancel all database wake-ups")
|
||||
global _wakeups
|
||||
global _wakeup_by_db
|
||||
with _wakeups_lock:
|
||||
_wakeups = []
|
||||
_wakeup_by_db = {}
|
||||
|
||||
|
||||
def schedule_wakeup(timestamp, db_name):
|
||||
""" Schedule a new wake-up for a database.
|
||||
|
||||
If an earlier wake-up is already defined, the new wake-up is discarded.
|
||||
If another wake-up is defined, that wake-up is discarded and the new one
|
||||
is scheduled.
|
||||
|
||||
:param db_name: database name for which a new wake-up is scheduled.
|
||||
:param timestamp: when the wake-up is scheduled.
|
||||
|
||||
"""
|
||||
if not timestamp:
|
||||
return
|
||||
with _wakeups_lock:
|
||||
if db_name in _wakeup_by_db:
|
||||
task = _wakeup_by_db[db_name]
|
||||
if not task[2] and timestamp > task[0]:
|
||||
# existing wakeup is valid and occurs earlier than new one
|
||||
return
|
||||
task[2] = True # cancel existing task
|
||||
task = [timestamp, db_name, False]
|
||||
heapq.heappush(_wakeups, task)
|
||||
_wakeup_by_db[db_name] = task
|
||||
_logger.debug("Wake-up scheduled for database '%s' @ %s", db_name,
|
||||
'NOW' if timestamp == WAKE_UP_NOW else timestamp)
|
||||
|
||||
def runner():
|
||||
"""Neverending function (intended to be run in a dedicated thread) that
|
||||
checks every 60 seconds the next database wake-up. TODO: make configurable
|
||||
"""
|
||||
while True:
|
||||
runner_body()
|
||||
|
||||
def runner_body():
|
||||
with _wakeups_lock:
|
||||
while _wakeups and _wakeups[0][0] < time.time() and get_thread_slots():
|
||||
task = heapq.heappop(_wakeups)
|
||||
timestamp, db_name, canceled = task
|
||||
if canceled:
|
||||
continue
|
||||
del _wakeup_by_db[db_name]
|
||||
registry = openerp.pooler.get_pool(db_name)
|
||||
if not registry._init:
|
||||
_logger.debug("Database '%s' wake-up! Firing multi-threaded cron job processing", db_name)
|
||||
registry['ir.cron']._run_jobs_multithread()
|
||||
amount = MAX_SLEEP
|
||||
with _wakeups_lock:
|
||||
# Sleep less than MAX_SLEEP if the next known wake-up will happen before that.
|
||||
if _wakeups and get_thread_slots():
|
||||
amount = min(MAX_SLEEP, max(MIN_SLEEP, _wakeups[0][0] - time.time()))
|
||||
_logger.debug("Going to sleep for %ss", amount)
|
||||
time.sleep(amount)
|
||||
|
||||
def start_master_thread():
|
||||
""" Start the above runner function in a daemon thread.
|
||||
|
||||
The thread is a typical daemon thread: it will never quit and must be
|
||||
terminated when the main process exits - with no consequence (the processing
|
||||
threads it spawns are not marked daemon).
|
||||
|
||||
"""
|
||||
global _thread_slots
|
||||
_thread_slots = openerp.conf.max_cron_threads
|
||||
db_maxconn = tools.config['db_maxconn']
|
||||
if _thread_slots >= tools.config.get('db_maxconn', 64):
|
||||
_logger.warning("Connection pool size (%s) is set lower than max number of cron threads (%s), "
|
||||
"this may cause trouble if you reach that number of parallel cron tasks.",
|
||||
db_maxconn, _thread_slots)
|
||||
t = threading.Thread(target=runner, name="openerp.cron.master_thread")
|
||||
t.setDaemon(True)
|
||||
t.start()
|
||||
_logger.debug("Master cron daemon started!")
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -434,8 +434,9 @@ def get_modules():
|
|||
return name
|
||||
|
||||
def is_really_module(name):
|
||||
name = opj(dir, name)
|
||||
return os.path.isdir(name) or zipfile.is_zipfile(name)
|
||||
manifest_name = opj(dir, name, '__openerp__.py')
|
||||
zipfile_name = opj(dir, name)
|
||||
return os.path.isfile(manifest_name) or zipfile.is_zipfile(zipfile_name)
|
||||
return map(clean, filter(is_really_module, os.listdir(dir)))
|
||||
|
||||
plist = []
|
||||
|
|
|
@ -28,7 +28,6 @@ import threading
|
|||
|
||||
import openerp.sql_db
|
||||
import openerp.osv.orm
|
||||
import openerp.cron
|
||||
import openerp.tools
|
||||
import openerp.modules.db
|
||||
import openerp.tools.config
|
||||
|
@ -58,6 +57,21 @@ class Registry(object):
|
|||
self.db_name = db_name
|
||||
self.db = openerp.sql_db.db_connect(db_name)
|
||||
|
||||
# In monoprocess cron jobs flag (pooljobs)
|
||||
self.cron = False
|
||||
|
||||
# Inter-process signaling (used only when openerp.multi_process is True):
|
||||
# The `base_registry_signaling` sequence indicates the whole registry
|
||||
# must be reloaded.
|
||||
# The `base_cache_signaling sequence` indicates all caches must be
|
||||
# invalidated (i.e. cleared).
|
||||
self.base_registry_signaling_sequence = 1
|
||||
self.base_cache_signaling_sequence = 1
|
||||
|
||||
# Flag indicating if at least one model cache has been cleared.
|
||||
# Useful only in a multi-process context.
|
||||
self._any_cache_cleared = False
|
||||
|
||||
cr = self.db.cursor()
|
||||
has_unaccent = openerp.modules.db.has_unaccent(cr)
|
||||
if openerp.tools.config['unaccent'] and not has_unaccent:
|
||||
|
@ -112,7 +126,7 @@ class Registry(object):
|
|||
monitor the ir.cron model for future jobs. See openerp.cron for
|
||||
details.
|
||||
"""
|
||||
openerp.cron.schedule_wakeup(openerp.cron.WAKE_UP_NOW, self.db.dbname)
|
||||
self.cron = True
|
||||
|
||||
def clear_caches(self):
|
||||
""" Clear the caches
|
||||
|
@ -121,6 +135,36 @@ class Registry(object):
|
|||
"""
|
||||
for model in self.models.itervalues():
|
||||
model.clear_caches()
|
||||
# Special case for ir_ui_menu which does not use openerp.tools.ormcache.
|
||||
ir_ui_menu = self.models.get('ir.ui.menu')
|
||||
if ir_ui_menu:
|
||||
ir_ui_menu.clear_cache()
|
||||
|
||||
|
||||
# Useful only in a multi-process context.
|
||||
def reset_any_cache_cleared(self):
|
||||
self._any_cache_cleared = False
|
||||
|
||||
# Useful only in a multi-process context.
|
||||
def any_cache_cleared(self):
|
||||
return self._any_cache_cleared
|
||||
|
||||
@classmethod
|
||||
def setup_multi_process_signaling(cls, cr):
|
||||
if not openerp.multi_process:
|
||||
return
|
||||
|
||||
# Inter-process signaling:
|
||||
# The `base_registry_signaling` sequence indicates the whole registry
|
||||
# must be reloaded.
|
||||
# The `base_cache_signaling sequence` indicates all caches must be
|
||||
# invalidated (i.e. cleared).
|
||||
cr.execute("""SELECT sequence_name FROM information_schema.sequences WHERE sequence_name='base_registry_signaling'""")
|
||||
if not cr.fetchall():
|
||||
cr.execute("""CREATE SEQUENCE base_registry_signaling INCREMENT BY 1 START WITH 1""")
|
||||
cr.execute("""SELECT nextval('base_registry_signaling')""")
|
||||
cr.execute("""CREATE SEQUENCE base_cache_signaling INCREMENT BY 1 START WITH 1""")
|
||||
cr.execute("""SELECT nextval('base_cache_signaling')""")
|
||||
|
||||
@contextmanager
|
||||
def cursor(self, auto_commit=True):
|
||||
|
@ -182,6 +226,7 @@ class RegistryManager(object):
|
|||
|
||||
cr = registry.db.cursor()
|
||||
try:
|
||||
Registry.setup_multi_process_signaling(cr)
|
||||
registry.do_parent_store(cr)
|
||||
registry.get('ir.actions.report.xml').register_all(cr)
|
||||
cr.commit()
|
||||
|
@ -195,20 +240,11 @@ class RegistryManager(object):
|
|||
|
||||
@classmethod
|
||||
def delete(cls, db_name):
|
||||
"""Delete the registry linked to a given database.
|
||||
|
||||
This also cleans the associated caches. For good measure this also
|
||||
cancels the associated cron job. But please note that the cron job can
|
||||
be running and take some time before ending, and that you should not
|
||||
remove a registry if it can still be used by some thread. So it might
|
||||
be necessary to call yourself openerp.cron.Agent.cancel(db_name) and
|
||||
and join (i.e. wait for) the thread.
|
||||
"""
|
||||
"""Delete the registry linked to a given database. """
|
||||
with cls.registries_lock:
|
||||
if db_name in cls.registries:
|
||||
cls.registries[db_name].clear_caches()
|
||||
del cls.registries[db_name]
|
||||
openerp.cron.cancel(db_name)
|
||||
|
||||
@classmethod
|
||||
def delete_all(cls):
|
||||
|
@ -232,5 +268,71 @@ class RegistryManager(object):
|
|||
if db_name in cls.registries:
|
||||
cls.registries[db_name].clear_caches()
|
||||
|
||||
@classmethod
|
||||
def check_registry_signaling(cls, db_name):
|
||||
if openerp.multi_process and db_name in cls.registries:
|
||||
registry = cls.get(db_name, pooljobs=False)
|
||||
cr = registry.db.cursor()
|
||||
try:
|
||||
cr.execute("""
|
||||
SELECT base_registry_signaling.last_value,
|
||||
base_cache_signaling.last_value
|
||||
FROM base_registry_signaling, base_cache_signaling""")
|
||||
r, c = cr.fetchone()
|
||||
# Check if the model registry must be reloaded (e.g. after the
|
||||
# database has been updated by another process).
|
||||
if registry.base_registry_signaling_sequence != r:
|
||||
_logger.info("Reloading the model registry after database signaling.")
|
||||
# Don't run the cron in the Gunicorn worker.
|
||||
registry = cls.new(db_name, pooljobs=False)
|
||||
registry.base_registry_signaling_sequence = r
|
||||
# Check if the model caches must be invalidated (e.g. after a write
|
||||
# occured on another process). Don't clear right after a registry
|
||||
# has been reload.
|
||||
elif registry.base_cache_signaling_sequence != c:
|
||||
_logger.info("Invalidating all model caches after database signaling.")
|
||||
registry.base_cache_signaling_sequence = c
|
||||
registry.clear_caches()
|
||||
registry.reset_any_cache_cleared()
|
||||
# One possible reason caches have been invalidated is the
|
||||
# use of decimal_precision.write(), in which case we need
|
||||
# to refresh fields.float columns.
|
||||
for model in registry.models.values():
|
||||
for column in model._columns.values():
|
||||
if hasattr(column, 'digits_change'):
|
||||
column.digits_change(cr)
|
||||
finally:
|
||||
cr.close()
|
||||
|
||||
@classmethod
|
||||
def signal_caches_change(cls, db_name):
|
||||
if openerp.multi_process and db_name in cls.registries:
|
||||
# Check the registries if any cache has been cleared and signal it
|
||||
# through the database to other processes.
|
||||
registry = cls.get(db_name, pooljobs=False)
|
||||
if registry.any_cache_cleared():
|
||||
_logger.info("At least one model cache has been cleared, signaling through the database.")
|
||||
cr = registry.db.cursor()
|
||||
r = 1
|
||||
try:
|
||||
cr.execute("select nextval('base_cache_signaling')")
|
||||
r = cr.fetchone()[0]
|
||||
finally:
|
||||
cr.close()
|
||||
registry.base_cache_signaling_sequence = r
|
||||
registry.reset_any_cache_cleared()
|
||||
|
||||
@classmethod
|
||||
def signal_registry_change(cls, db_name):
|
||||
if openerp.multi_process and db_name in cls.registries:
|
||||
registry = cls.get(db_name, pooljobs=False)
|
||||
cr = registry.db.cursor()
|
||||
r = 1
|
||||
try:
|
||||
cr.execute("select nextval('base_registry_signaling')")
|
||||
r = cr.fetchone()[0]
|
||||
finally:
|
||||
cr.close()
|
||||
registry.base_registry_signaling_sequence = r
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -20,6 +20,9 @@
|
|||
#
|
||||
##############################################################################
|
||||
|
||||
#.apidoc title: Common Services: netsvc
|
||||
#.apidoc module-mods: member-order: bysource
|
||||
|
||||
import errno
|
||||
import logging
|
||||
import logging.handlers
|
||||
|
@ -40,6 +43,7 @@ import openerp
|
|||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def close_socket(sock):
|
||||
""" Closes a socket instance cleanly
|
||||
|
||||
|
@ -58,18 +62,14 @@ def close_socket(sock):
|
|||
raise
|
||||
sock.close()
|
||||
|
||||
|
||||
#.apidoc title: Common Services: netsvc
|
||||
#.apidoc module-mods: member-order: bysource
|
||||
|
||||
def abort_response(dummy_1, description, dummy_2, details):
|
||||
# TODO Replace except_{osv,orm} with these directly.
|
||||
raise openerp.osv.osv.except_osv(description, details)
|
||||
|
||||
class Service(object):
|
||||
""" Base class for *Local* services
|
||||
|
||||
Functionality here is trusted, no authentication.
|
||||
""" Base class for Local services
|
||||
Functionality here is trusted, no authentication.
|
||||
Workflow engine and reports subclass this.
|
||||
"""
|
||||
_services = {}
|
||||
def __init__(self, name):
|
||||
|
@ -145,7 +145,6 @@ class ColoredFormatter(DBFormatter):
|
|||
record.levelname = COLOR_PATTERN % (30 + fg_color, 40 + bg_color, record.levelname)
|
||||
return DBFormatter.format(self, record)
|
||||
|
||||
|
||||
def init_logger():
|
||||
from tools.translate import resetlocale
|
||||
resetlocale()
|
||||
|
@ -246,85 +245,6 @@ def init_alternative_logger():
|
|||
logger.addHandler(handler)
|
||||
logger.setLevel(logging.ERROR)
|
||||
|
||||
class Server:
|
||||
""" Generic interface for all servers with an event loop etc.
|
||||
Override this to impement http, net-rpc etc. servers.
|
||||
|
||||
Servers here must have threaded behaviour. start() must not block,
|
||||
there is no run().
|
||||
"""
|
||||
__is_started = False
|
||||
__servers = []
|
||||
__starter_threads = []
|
||||
|
||||
# we don't want blocking server calls (think select()) to
|
||||
# wait forever and possibly prevent exiting the process,
|
||||
# but instead we want a form of polling/busy_wait pattern, where
|
||||
# _server_timeout should be used as the default timeout for
|
||||
# all I/O blocking operations
|
||||
_busywait_timeout = 0.5
|
||||
|
||||
def __init__(self):
|
||||
Server.__servers.append(self)
|
||||
if Server.__is_started:
|
||||
# raise Exception('All instances of servers must be inited before the startAll()')
|
||||
# Since the startAll() won't be called again, allow this server to
|
||||
# init and then start it after 1sec (hopefully). Register that
|
||||
# timer thread in a list, so that we can abort the start if quitAll
|
||||
# is called in the meantime
|
||||
t = threading.Timer(1.0, self._late_start)
|
||||
t.name = 'Late start timer for %s' % str(self.__class__)
|
||||
Server.__starter_threads.append(t)
|
||||
t.start()
|
||||
|
||||
def start(self):
|
||||
_logger.debug("called stub Server.start")
|
||||
|
||||
def _late_start(self):
|
||||
self.start()
|
||||
for thr in Server.__starter_threads:
|
||||
if thr.finished.is_set():
|
||||
Server.__starter_threads.remove(thr)
|
||||
|
||||
def stop(self):
|
||||
_logger.debug("called stub Server.stop")
|
||||
|
||||
def stats(self):
|
||||
""" This function should return statistics about the server """
|
||||
return "%s: No statistics" % str(self.__class__)
|
||||
|
||||
@classmethod
|
||||
def startAll(cls):
|
||||
if cls.__is_started:
|
||||
return
|
||||
_logger.info("Starting %d services" % len(cls.__servers))
|
||||
for srv in cls.__servers:
|
||||
srv.start()
|
||||
cls.__is_started = True
|
||||
|
||||
@classmethod
|
||||
def quitAll(cls):
|
||||
if not cls.__is_started:
|
||||
return
|
||||
_logger.info("Stopping %d services" % len(cls.__servers))
|
||||
for thr in cls.__starter_threads:
|
||||
if not thr.finished.is_set():
|
||||
thr.cancel()
|
||||
cls.__starter_threads.remove(thr)
|
||||
|
||||
for srv in cls.__servers:
|
||||
srv.stop()
|
||||
cls.__is_started = False
|
||||
|
||||
@classmethod
|
||||
def allStats(cls):
|
||||
res = ["Servers %s" % ('stopped', 'started')[cls.__is_started]]
|
||||
res.extend(srv.stats() for srv in cls.__servers)
|
||||
return '\n'.join(res)
|
||||
|
||||
def _close_socket(self):
|
||||
close_socket(self.socket)
|
||||
|
||||
def replace_request_password(args):
|
||||
# password is always 3rd argument in a request, we replace it in RPC logs
|
||||
# so it's easier to forward logs for diagnostics/debugging purposes...
|
||||
|
|
|
@ -163,7 +163,7 @@ class boolean(_column):
|
|||
_logger.debug(
|
||||
"required=True is deprecated: making a boolean field"
|
||||
" `required` has no effect, as NULL values are "
|
||||
"automatically turned into False.")
|
||||
"automatically turned into False. args: %r",args)
|
||||
|
||||
class integer(_column):
|
||||
_type = 'integer'
|
||||
|
|
|
@ -2509,6 +2509,7 @@ class BaseModel(object):
|
|||
try:
|
||||
getattr(self, '_ormcache')
|
||||
self._ormcache = {}
|
||||
self.pool._any_cache_cleared = True
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
|
@ -3234,7 +3235,7 @@ class BaseModel(object):
|
|||
|
||||
|
||||
def _create_table(self, cr):
|
||||
cr.execute('CREATE TABLE "%s" (id SERIAL NOT NULL, PRIMARY KEY(id)) WITHOUT OIDS' % (self._table,))
|
||||
cr.execute('CREATE TABLE "%s" (id SERIAL NOT NULL, PRIMARY KEY(id))' % (self._table,))
|
||||
cr.execute(("COMMENT ON TABLE \"%s\" IS %%s" % self._table), (self._description,))
|
||||
_schema.debug("Table '%s': created", self._table)
|
||||
|
||||
|
@ -3318,7 +3319,7 @@ class BaseModel(object):
|
|||
raise except_orm('Programming Error', ('Many2Many destination model does not exist: `%s`') % (f._obj,))
|
||||
dest_model = self.pool.get(f._obj)
|
||||
ref = dest_model._table
|
||||
cr.execute('CREATE TABLE "%s" ("%s" INTEGER NOT NULL, "%s" INTEGER NOT NULL, UNIQUE("%s","%s")) WITH OIDS' % (m2m_tbl, col1, col2, col1, col2))
|
||||
cr.execute('CREATE TABLE "%s" ("%s" INTEGER NOT NULL, "%s" INTEGER NOT NULL, UNIQUE("%s","%s"))' % (m2m_tbl, col1, col2, col1, col2))
|
||||
# create foreign key references with ondelete=cascade, unless the targets are SQL views
|
||||
cr.execute("SELECT relkind FROM pg_class WHERE relkind IN ('v') AND relname=%s", (ref,))
|
||||
if not cr.fetchall():
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
|
||||
from functools import wraps
|
||||
import logging
|
||||
import threading
|
||||
|
||||
from psycopg2 import IntegrityError, errorcodes
|
||||
|
||||
import orm
|
||||
|
@ -168,6 +170,7 @@ class object_proxy(object):
|
|||
|
||||
@check
|
||||
def execute(self, db, uid, obj, method, *args, **kw):
|
||||
threading.currentThread().dbname = db
|
||||
cr = pooler.get_db(db).cursor()
|
||||
try:
|
||||
try:
|
||||
|
|
|
@ -28,16 +28,17 @@ import sys
|
|||
import threading
|
||||
import time
|
||||
|
||||
import cron
|
||||
import netrpc_server
|
||||
import web_services
|
||||
import web_services
|
||||
import wsgi_server
|
||||
|
||||
import openerp.cron
|
||||
import openerp.modules
|
||||
import openerp.netsvc
|
||||
import openerp.osv
|
||||
from openerp.release import nt_service_name
|
||||
import openerp.tools
|
||||
import openerp.service.wsgi_server
|
||||
|
||||
#.apidoc title: RPC Services
|
||||
|
||||
|
@ -73,37 +74,32 @@ def start_internal():
|
|||
return
|
||||
openerp.netsvc.init_logger()
|
||||
openerp.modules.loading.open_openerp_namespace()
|
||||
|
||||
# Instantiate local services (this is a legacy design).
|
||||
openerp.osv.osv.start_object_proxy()
|
||||
# Export (for RPC) services.
|
||||
web_services.start_web_services()
|
||||
web_services.start_service()
|
||||
|
||||
load_server_wide_modules()
|
||||
start_internal_done = True
|
||||
|
||||
def start_services():
|
||||
""" Start all services including http, netrpc and cron """
|
||||
start_internal()
|
||||
|
||||
# Initialize the HTTP stack.
|
||||
netrpc_server.init_servers()
|
||||
|
||||
# Start the main cron thread.
|
||||
if openerp.conf.max_cron_threads:
|
||||
openerp.cron.start_master_thread()
|
||||
|
||||
# Start the top-level servers threads (normally HTTP, HTTPS, and NETRPC).
|
||||
openerp.netsvc.Server.startAll()
|
||||
|
||||
# Initialize the NETRPC server.
|
||||
netrpc_server.start_service()
|
||||
# Start the WSGI server.
|
||||
openerp.service.wsgi_server.start_server()
|
||||
wsgi_server.start_service()
|
||||
# Start the main cron thread.
|
||||
cron.start_service()
|
||||
|
||||
def stop_services():
|
||||
""" Stop all services. """
|
||||
# stop scheduling new jobs; we will have to wait for the jobs to complete below
|
||||
openerp.cron.cancel_all()
|
||||
# stop services
|
||||
cron.stop_service()
|
||||
netrpc_server.stop_service()
|
||||
wsgi_server.stop_service()
|
||||
|
||||
openerp.netsvc.Server.quitAll()
|
||||
openerp.service.wsgi_server.stop_server()
|
||||
_logger.info("Initiating shutdown")
|
||||
_logger.info("Hit CTRL-C again or send a second signal to force the shutdown.")
|
||||
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2011 OpenERP SA (<http://www.openerp.com>)
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
""" Cron jobs scheduling
|
||||
|
||||
Cron jobs are defined in the ir_cron table/model. This module deals with all
|
||||
cron jobs, for all databases of a single OpenERP server instance.
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
import threading
|
||||
import time
|
||||
|
||||
import openerp
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
SLEEP_INTERVAL = 60 # 1 min
|
||||
|
||||
def cron_runner(number):
|
||||
while True:
|
||||
time.sleep(SLEEP_INTERVAL + number) # Steve Reich timing style
|
||||
registries = openerp.modules.registry.RegistryManager.registries
|
||||
_logger.debug('cron%d polling for jobs', number)
|
||||
for db_name, registry in registries.items():
|
||||
while True and registry.cron:
|
||||
# acquired = openerp.addons.base.ir.ir_cron.ir_cron._acquire_job(db_name)
|
||||
# TODO why isnt openerp.addons.base defined ?
|
||||
import sys
|
||||
base = sys.modules['addons.base']
|
||||
acquired = base.ir.ir_cron.ir_cron._acquire_job(db_name)
|
||||
if not acquired:
|
||||
break
|
||||
|
||||
def start_service():
|
||||
""" Start the above runner function in a daemon thread.
|
||||
|
||||
The thread is a typical daemon thread: it will never quit and must be
|
||||
terminated when the main process exits - with no consequence (the processing
|
||||
threads it spawns are not marked daemon).
|
||||
|
||||
"""
|
||||
for i in range(openerp.tools.config['max_cron_threads']):
|
||||
def target():
|
||||
cron_runner(i)
|
||||
t = threading.Thread(target=target, name="openerp.service.cron.cron%d" % i)
|
||||
t.setDaemon(True)
|
||||
t.start()
|
||||
_logger.debug("cron%d started!" % i)
|
||||
|
||||
def stop_service():
|
||||
pass
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -176,6 +176,4 @@ class OpenERPAuthProvider(AuthProvider):
|
|||
self.auth_tries += 1
|
||||
raise AuthRequiredExc(atype='Basic', realm=self.realm)
|
||||
|
||||
#eof
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -22,8 +22,6 @@
|
|||
#.apidoc title: NET-RPC Server
|
||||
|
||||
""" This file contains instance of the net-rpc server
|
||||
|
||||
|
||||
"""
|
||||
import logging
|
||||
import select
|
||||
|
@ -38,6 +36,85 @@ import openerp.tools as tools
|
|||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class Server:
|
||||
""" Generic interface for all servers with an event loop etc.
|
||||
Override this to impement http, net-rpc etc. servers.
|
||||
|
||||
Servers here must have threaded behaviour. start() must not block,
|
||||
there is no run().
|
||||
"""
|
||||
__is_started = False
|
||||
__servers = []
|
||||
__starter_threads = []
|
||||
|
||||
# we don't want blocking server calls (think select()) to
|
||||
# wait forever and possibly prevent exiting the process,
|
||||
# but instead we want a form of polling/busy_wait pattern, where
|
||||
# _server_timeout should be used as the default timeout for
|
||||
# all I/O blocking operations
|
||||
_busywait_timeout = 0.5
|
||||
|
||||
def __init__(self):
|
||||
Server.__servers.append(self)
|
||||
if Server.__is_started:
|
||||
# raise Exception('All instances of servers must be inited before the startAll()')
|
||||
# Since the startAll() won't be called again, allow this server to
|
||||
# init and then start it after 1sec (hopefully). Register that
|
||||
# timer thread in a list, so that we can abort the start if quitAll
|
||||
# is called in the meantime
|
||||
t = threading.Timer(1.0, self._late_start)
|
||||
t.name = 'Late start timer for %s' % str(self.__class__)
|
||||
Server.__starter_threads.append(t)
|
||||
t.start()
|
||||
|
||||
def start(self):
|
||||
_logger.debug("called stub Server.start")
|
||||
|
||||
def _late_start(self):
|
||||
self.start()
|
||||
for thr in Server.__starter_threads:
|
||||
if thr.finished.is_set():
|
||||
Server.__starter_threads.remove(thr)
|
||||
|
||||
def stop(self):
|
||||
_logger.debug("called stub Server.stop")
|
||||
|
||||
def stats(self):
|
||||
""" This function should return statistics about the server """
|
||||
return "%s: No statistics" % str(self.__class__)
|
||||
|
||||
@classmethod
|
||||
def startAll(cls):
|
||||
if cls.__is_started:
|
||||
return
|
||||
_logger.info("Starting %d services" % len(cls.__servers))
|
||||
for srv in cls.__servers:
|
||||
srv.start()
|
||||
cls.__is_started = True
|
||||
|
||||
@classmethod
|
||||
def quitAll(cls):
|
||||
if not cls.__is_started:
|
||||
return
|
||||
_logger.info("Stopping %d services" % len(cls.__servers))
|
||||
for thr in cls.__starter_threads:
|
||||
if not thr.finished.is_set():
|
||||
thr.cancel()
|
||||
cls.__starter_threads.remove(thr)
|
||||
|
||||
for srv in cls.__servers:
|
||||
srv.stop()
|
||||
cls.__is_started = False
|
||||
|
||||
@classmethod
|
||||
def allStats(cls):
|
||||
res = ["Servers %s" % ('stopped', 'started')[cls.__is_started]]
|
||||
res.extend(srv.stats() for srv in cls.__servers)
|
||||
return '\n'.join(res)
|
||||
|
||||
def _close_socket(self):
|
||||
netsvc.close_socket(self.socket)
|
||||
|
||||
class TinySocketClientThread(threading.Thread):
|
||||
def __init__(self, sock, threads):
|
||||
spn = sock and sock.getpeername()
|
||||
|
@ -99,10 +176,10 @@ def netrpc_handle_exception_legacy(e):
|
|||
return 'AccessDenied ' + str(e)
|
||||
return openerp.tools.exception_to_unicode(e)
|
||||
|
||||
class TinySocketServerThread(threading.Thread,netsvc.Server):
|
||||
class TinySocketServerThread(threading.Thread,Server):
|
||||
def __init__(self, interface, port, secure=False):
|
||||
threading.Thread.__init__(self, name="NetRPCDaemon-%d"%port)
|
||||
netsvc.Server.__init__(self)
|
||||
Server.__init__(self)
|
||||
self.__port = port
|
||||
self.__interface = interface
|
||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
|
@ -157,11 +234,12 @@ class TinySocketServerThread(threading.Thread,netsvc.Server):
|
|||
|
||||
netrpcd = None
|
||||
|
||||
def init_servers():
|
||||
def start_service():
|
||||
global netrpcd
|
||||
if tools.config.get('netrpc', False):
|
||||
netrpcd = TinySocketServerThread(
|
||||
tools.config.get('netrpc_interface', ''),
|
||||
int(tools.config.get('netrpc_port', 8070)))
|
||||
netrpcd = TinySocketServerThread(tools.config.get('netrpc_interface', ''), int(tools.config.get('netrpc_port', 8070)))
|
||||
|
||||
def stop_service():
|
||||
Server.quitAll()
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -168,6 +168,7 @@ class db(netsvc.ExportService):
|
|||
|
||||
def exp_duplicate_database(self, db_original_name, db_name):
|
||||
_logger.info('Duplicate database `%s` to `%s`.', db_original_name, db_name)
|
||||
sql_db.close_db(db_original_name)
|
||||
db = sql_db.db_connect('postgres')
|
||||
cr = db.cursor()
|
||||
try:
|
||||
|
@ -606,68 +607,13 @@ class objects_proxy(netsvc.ExportService):
|
|||
raise NameError("Method not available %s" % method)
|
||||
security.check(db,uid,passwd)
|
||||
assert openerp.osv.osv.service, "The object_proxy class must be started with start_object_proxy."
|
||||
openerp.modules.registry.RegistryManager.check_registry_signaling(db)
|
||||
fn = getattr(openerp.osv.osv.service, method)
|
||||
res = fn(db, uid, *params)
|
||||
openerp.modules.registry.RegistryManager.signal_caches_change(db)
|
||||
return res
|
||||
|
||||
|
||||
#
|
||||
# Wizard ID: 1
|
||||
# - None = end of wizard
|
||||
#
|
||||
# Wizard Type: 'form'
|
||||
# - form
|
||||
# - print
|
||||
#
|
||||
# Wizard datas: {}
|
||||
# TODO: change local request to OSE request/reply pattern
|
||||
#
|
||||
class wizard(netsvc.ExportService):
|
||||
def __init__(self, name='wizard'):
|
||||
netsvc.ExportService.__init__(self,name)
|
||||
self.id = 0
|
||||
self.wiz_datas = {}
|
||||
self.wiz_name = {}
|
||||
self.wiz_uid = {}
|
||||
|
||||
def dispatch(self, method, params):
|
||||
(db, uid, passwd ) = params[0:3]
|
||||
threading.current_thread().uid = uid
|
||||
params = params[3:]
|
||||
if method not in ['execute','create']:
|
||||
raise KeyError("Method not supported %s" % method)
|
||||
security.check(db,uid,passwd)
|
||||
fn = getattr(self, 'exp_'+method)
|
||||
res = fn(db, uid, *params)
|
||||
return res
|
||||
|
||||
def _execute(self, db, uid, wiz_id, datas, action, context):
|
||||
self.wiz_datas[wiz_id].update(datas)
|
||||
wiz = netsvc.LocalService('wizard.'+self.wiz_name[wiz_id])
|
||||
return wiz.execute(db, uid, self.wiz_datas[wiz_id], action, context)
|
||||
|
||||
def exp_create(self, db, uid, wiz_name, datas=None):
|
||||
if not datas:
|
||||
datas={}
|
||||
#FIXME: this is not thread-safe
|
||||
self.id += 1
|
||||
self.wiz_datas[self.id] = {}
|
||||
self.wiz_name[self.id] = wiz_name
|
||||
self.wiz_uid[self.id] = uid
|
||||
return self.id
|
||||
|
||||
def exp_execute(self, db, uid, wiz_id, datas, action='init', context=None):
|
||||
if not context:
|
||||
context={}
|
||||
|
||||
if wiz_id in self.wiz_uid:
|
||||
if self.wiz_uid[wiz_id] == uid:
|
||||
return self._execute(db, uid, wiz_id, datas, action, context)
|
||||
else:
|
||||
raise openerp.exceptions.AccessDenied()
|
||||
else:
|
||||
raise openerp.exceptions.Warning('Wizard not found.')
|
||||
|
||||
#
|
||||
# TODO: set a maximum report number per user to avoid DOS attacks
|
||||
#
|
||||
|
@ -689,8 +635,10 @@ class report_spool(netsvc.ExportService):
|
|||
if method not in ['report', 'report_get', 'render_report']:
|
||||
raise KeyError("Method not supported %s" % method)
|
||||
security.check(db,uid,passwd)
|
||||
openerp.modules.registry.RegistryManager.check_registry_signaling(db)
|
||||
fn = getattr(self, 'exp_' + method)
|
||||
res = fn(db, uid, *params)
|
||||
openerp.modules.registry.RegistryManager.signal_caches_change(db)
|
||||
return res
|
||||
|
||||
def exp_render_report(self, db, uid, object, ids, datas=None, context=None):
|
||||
|
@ -802,13 +750,11 @@ class report_spool(netsvc.ExportService):
|
|||
raise Exception, 'ReportNotFound'
|
||||
|
||||
|
||||
def start_web_services():
|
||||
def start_service():
|
||||
db()
|
||||
common()
|
||||
objects_proxy()
|
||||
wizard()
|
||||
report_spool()
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
|
|
|
@ -16,6 +16,10 @@ import sys
|
|||
import time
|
||||
|
||||
import werkzeug.serving
|
||||
try:
|
||||
from setproctitle import setproctitle
|
||||
except ImportError:
|
||||
setproctitle = lambda x: None
|
||||
|
||||
import openerp
|
||||
import openerp.tools.config as config
|
||||
|
@ -134,7 +138,7 @@ class Multicorn(object):
|
|||
def process_spawn(self):
|
||||
while len(self.workers_http) < self.population:
|
||||
self.worker_spawn(WorkerHTTP, self.workers_http)
|
||||
while len(self.workers_cron) < 1: # config option ?
|
||||
while len(self.workers_cron) < config['max_cron_threads']:
|
||||
self.worker_spawn(WorkerCron, self.workers_cron)
|
||||
|
||||
def sleep(self):
|
||||
|
@ -189,8 +193,7 @@ class Multicorn(object):
|
|||
for pid in self.workers.keys():
|
||||
self.worker_kill(pid, signal.SIGTERM)
|
||||
self.socket.close()
|
||||
import __main__
|
||||
__main__.quit_signals_received = 1
|
||||
openerp.cli.server.quit_signals_received = 1
|
||||
|
||||
def run(self):
|
||||
self.start()
|
||||
|
@ -252,7 +255,7 @@ class Worker(object):
|
|||
# Reset the worker if it consumes too much memory (e.g. caused by a memory leak).
|
||||
rss, vms = psutil.Process(os.getpid()).get_memory_info()
|
||||
if vms > config['limit_memory_soft']:
|
||||
_logger.info('Virtual memory consumption too high, rebooting the worker.')
|
||||
_logger.info('Worker (%d) virtual memory limit (%s) reached.', self.pid, vms)
|
||||
self.alive = False # Commit suicide after the request.
|
||||
|
||||
# VMS and RLIMIT_AS are the same thing: virtual memory, a.k.a. address space
|
||||
|
@ -263,7 +266,8 @@ class Worker(object):
|
|||
r = resource.getrusage(resource.RUSAGE_SELF)
|
||||
cpu_time = r.ru_utime + r.ru_stime
|
||||
def time_expired(n, stack):
|
||||
_logger.info('CPU time limit exceeded.')
|
||||
_logger.info('Worker (%d) CPU time limit (%s) reached.', config['limit_time_cpu'])
|
||||
# We dont suicide in such case
|
||||
raise Exception('CPU time limit exceeded.')
|
||||
signal.signal(signal.SIGXCPU, time_expired)
|
||||
soft, hard = resource.getrlimit(resource.RLIMIT_CPU)
|
||||
|
@ -274,6 +278,7 @@ class Worker(object):
|
|||
|
||||
def start(self):
|
||||
self.pid = os.getpid()
|
||||
setproctitle('openerp: %s %s' % (self.__class__.__name__, self.pid))
|
||||
_logger.info("Worker %s (%s) alive", self.__class__.__name__, self.pid)
|
||||
# Reseed the random number generator
|
||||
random.seed()
|
||||
|
@ -346,19 +351,27 @@ class WorkerBaseWSGIServer(werkzeug.serving.BaseWSGIServer):
|
|||
class WorkerCron(Worker):
|
||||
""" Cron workers """
|
||||
def sleep(self):
|
||||
time.sleep(60)
|
||||
interval = 60 + self.pid % 10 # chorus effect
|
||||
time.sleep(interval)
|
||||
|
||||
def process_work(self):
|
||||
_logger.debug("WorkerCron (%s) polling for jobs", self.pid)
|
||||
if config['db_name']:
|
||||
db_names = config['db_name'].split(',')
|
||||
else:
|
||||
db_names = openerp.netsvc.ExportService._services['db'].exp_list(True)
|
||||
for db_name in db_names:
|
||||
while True:
|
||||
# TODO Each job should be considered as one request in multiprocessing
|
||||
acquired = openerp.addons.base.ir.ir_cron.ir_cron._acquire_job(db_name)
|
||||
# acquired = openerp.addons.base.ir.ir_cron.ir_cron._acquire_job(db_name)
|
||||
# TODO why isnt openerp.addons.base defined ?
|
||||
import base
|
||||
acquired = base.ir.ir_cron.ir_cron._acquire_job(db_name)
|
||||
if not acquired:
|
||||
break
|
||||
# dont keep cursors in multi database mode
|
||||
if len(db_names) > 1:
|
||||
openerp.sql_db.close_db(db_name)
|
||||
# TODO Each job should be considered as one request instead of each db
|
||||
self.request_count += 1
|
||||
|
||||
def start(self):
|
||||
|
|
|
@ -428,14 +428,14 @@ def serve():
|
|||
_logger.info('HTTP service (werkzeug) running on %s:%s', interface, port)
|
||||
httpd.serve_forever()
|
||||
|
||||
def start_server():
|
||||
def start_service():
|
||||
""" Call serve() in its own thread.
|
||||
|
||||
The WSGI server can be shutdown with stop_server() below.
|
||||
"""
|
||||
threading.Thread(target=serve).start()
|
||||
|
||||
def stop_server():
|
||||
def stop_service():
|
||||
""" Initiate the shutdown of the WSGI server.
|
||||
|
||||
The server is supposed to have been started by start_server() above.
|
||||
|
|
|
@ -57,10 +57,12 @@ class ormcache(object):
|
|||
try:
|
||||
key = args[self.skiparg-2:]
|
||||
del d[key]
|
||||
self2.pool._any_cache_cleared = True
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
d.clear()
|
||||
self2.pool._any_cache_cleared = True
|
||||
|
||||
class ormcache_multi(ormcache):
|
||||
def __init__(self, skiparg=2, size=8192, multi=3):
|
||||
|
|
|
@ -146,8 +146,9 @@ class configmanager(object):
|
|||
help="specify the TCP IP address for the NETRPC protocol")
|
||||
group.add_option("--netrpc-port", dest="netrpc_port", my_default=8070,
|
||||
help="specify the TCP port for the NETRPC protocol", type="int")
|
||||
group.add_option("--no-netrpc", dest="netrpc", action="store_false", my_default=True,
|
||||
help="disable the NETRPC protocol")
|
||||
# Needed a few day for runbot and saas
|
||||
group.add_option("--no-netrpc", dest="netrpc", action="store_false", my_default=False, help="disable the NETRPC protocol")
|
||||
group.add_option("--netrpc", dest="netrpc", action="store_true", my_default=False, help="enable the NETRPC protocol")
|
||||
parser.add_option_group(group)
|
||||
|
||||
# WEB
|
||||
|
@ -269,8 +270,8 @@ class configmanager(object):
|
|||
"osv_memory tables. This is a decimal value expressed in hours, "
|
||||
"and the default is 1 hour.",
|
||||
type="float")
|
||||
group.add_option("--max-cron-threads", dest="max_cron_threads", my_default=4,
|
||||
help="Maximum number of threads processing concurrently cron jobs.",
|
||||
group.add_option("--max-cron-threads", dest="max_cron_threads", my_default=2,
|
||||
help="Maximum number of threads processing concurrently cron jobs (default 2).",
|
||||
type="int")
|
||||
group.add_option("--unaccent", dest="unaccent", my_default=False, action="store_true",
|
||||
help="Use the unaccent function provided by the database when available.")
|
||||
|
@ -282,19 +283,19 @@ class configmanager(object):
|
|||
help="Specify the number of workers, 0 disable prefork mode.",
|
||||
type="int")
|
||||
group.add_option("--limit-memory-soft", dest="limit_memory_soft", my_default=640 * 1024 * 1024,
|
||||
help="Maximum allowed virtual memory per worker, when reached the worker be reset after the current request.",
|
||||
help="Maximum allowed virtual memory per worker, when reached the worker be reset after the current request (default 640M).",
|
||||
type="int")
|
||||
group.add_option("--limit-memory-hard", dest="limit_memory_hard", my_default=768 * 1024 * 1024,
|
||||
help="Maximum allowed virtual memory per worker, when reached, any memory allocation will fail.",
|
||||
help="Maximum allowed virtual memory per worker, when reached, any memory allocation will fail (default 768M).",
|
||||
type="int")
|
||||
group.add_option("--limit-time-cpu", dest="limit_time_cpu", my_default=60,
|
||||
help="Maximum allowed CPU time per request.",
|
||||
help="Maximum allowed CPU time per request (default 60).",
|
||||
type="int")
|
||||
group.add_option("--limit-time-real", dest="limit_time_real", my_default=60,
|
||||
help="Maximum allowed Real time per request. ",
|
||||
group.add_option("--limit-time-real", dest="limit_time_real", my_default=120,
|
||||
help="Maximum allowed Real time per request (default 120).",
|
||||
type="int")
|
||||
group.add_option("--limit-request", dest="limit_request", my_default=8192,
|
||||
help="Maximum number of request to be processed per worker.",
|
||||
help="Maximum number of request to be processed per worker (default 8192).",
|
||||
type="int")
|
||||
parser.add_option_group(group)
|
||||
|
||||
|
@ -479,8 +480,6 @@ class configmanager(object):
|
|||
if opt.save:
|
||||
self.save()
|
||||
|
||||
openerp.conf.max_cron_threads = self.options['max_cron_threads']
|
||||
|
||||
openerp.conf.addons_paths = self.options['addons_path'].split(',')
|
||||
if opt.server_wide_modules:
|
||||
openerp.conf.server_wide_modules = map(lambda m: m.strip(), opt.server_wide_modules.split(','))
|
||||
|
|
|
@ -68,7 +68,7 @@ def image_resize_image(base64_source, size=(1024, 1024), encoding='base64', file
|
|||
return base64_source
|
||||
# create a thumbnail: will resize and keep ratios, then sharpen for better looking result
|
||||
image.thumbnail(size, Image.ANTIALIAS)
|
||||
sharpener = ImageEnhance.Sharpness(image.convert('RGB'))
|
||||
sharpener = ImageEnhance.Sharpness(image.convert('RGBA'))
|
||||
image = sharpener.enhance(2.0)
|
||||
# create a transparent image for background
|
||||
background = Image.new('RGBA', size, (255, 255, 255, 0))
|
||||
|
|
|
@ -1,269 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#Copyright (c) 2004-2005, CherryPy Team (team@cherrypy.org)
|
||||
#All rights reserved.
|
||||
#
|
||||
#Redistribution and use in source and binary forms, with or without
|
||||
#modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of the CherryPy Team nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
#ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
#WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
#DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
#FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
#DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
#SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
#CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
#OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
#OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# This is a backport of Python-2.4's threading.local() implementation
|
||||
|
||||
"""Thread-local objects
|
||||
|
||||
(Note that this module provides a Python version of thread
|
||||
threading.local class. Depending on the version of Python you're
|
||||
using, there may be a faster one available. You should always import
|
||||
the local class from threading.)
|
||||
|
||||
Thread-local objects support the management of thread-local data.
|
||||
If you have data that you want to be local to a thread, simply create
|
||||
a thread-local object and use its attributes:
|
||||
|
||||
>>> mydata = local()
|
||||
>>> mydata.number = 42
|
||||
>>> mydata.number
|
||||
42
|
||||
|
||||
You can also access the local-object's dictionary:
|
||||
|
||||
>>> mydata.__dict__
|
||||
{'number': 42}
|
||||
>>> mydata.__dict__.setdefault('widgets', [])
|
||||
[]
|
||||
>>> mydata.widgets
|
||||
[]
|
||||
|
||||
What's important about thread-local objects is that their data are
|
||||
local to a thread. If we access the data in a different thread:
|
||||
|
||||
>>> log = []
|
||||
>>> def f():
|
||||
... items = mydata.__dict__.items()
|
||||
... items.sort()
|
||||
... log.append(items)
|
||||
... mydata.number = 11
|
||||
... log.append(mydata.number)
|
||||
|
||||
>>> import threading
|
||||
>>> thread = threading.Thread(target=f)
|
||||
>>> thread.start()
|
||||
>>> thread.join()
|
||||
>>> log
|
||||
[[], 11]
|
||||
|
||||
we get different data. Furthermore, changes made in the other thread
|
||||
don't affect data seen in this thread:
|
||||
|
||||
>>> mydata.number
|
||||
42
|
||||
|
||||
Of course, values you get from a local object, including a __dict__
|
||||
attribute, are for whatever thread was current at the time the
|
||||
attribute was read. For that reason, you generally don't want to save
|
||||
these values across threads, as they apply only to the thread they
|
||||
came from.
|
||||
|
||||
You can create custom local objects by subclassing the local class:
|
||||
|
||||
>>> class MyLocal(local):
|
||||
... number = 2
|
||||
... initialized = False
|
||||
... def __init__(self, **kw):
|
||||
... if self.initialized:
|
||||
... raise SystemError('__init__ called too many times')
|
||||
... self.initialized = True
|
||||
... self.__dict__.update(kw)
|
||||
... def squared(self):
|
||||
... return self.number ** 2
|
||||
|
||||
This can be useful to support default values, methods and
|
||||
initialization. Note that if you define an __init__ method, it will be
|
||||
called each time the local object is used in a separate thread. This
|
||||
is necessary to initialize each thread's dictionary.
|
||||
|
||||
Now if we create a local object:
|
||||
|
||||
>>> mydata = MyLocal(color='red')
|
||||
|
||||
Now we have a default number:
|
||||
|
||||
>>> mydata.number
|
||||
2
|
||||
|
||||
an initial color:
|
||||
|
||||
>>> mydata.color
|
||||
'red'
|
||||
>>> del mydata.color
|
||||
|
||||
And a method that operates on the data:
|
||||
|
||||
>>> mydata.squared()
|
||||
4
|
||||
|
||||
As before, we can access the data in a separate thread:
|
||||
|
||||
>>> log = []
|
||||
>>> thread = threading.Thread(target=f)
|
||||
>>> thread.start()
|
||||
>>> thread.join()
|
||||
>>> log
|
||||
[[('color', 'red'), ('initialized', True)], 11]
|
||||
|
||||
without affecting this thread's data:
|
||||
|
||||
>>> mydata.number
|
||||
2
|
||||
>>> mydata.color
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
AttributeError: 'MyLocal' object has no attribute 'color'
|
||||
|
||||
Note that subclasses can define slots, but they are not thread
|
||||
local. They are shared across threads:
|
||||
|
||||
>>> class MyLocal(local):
|
||||
... __slots__ = 'number'
|
||||
|
||||
>>> mydata = MyLocal()
|
||||
>>> mydata.number = 42
|
||||
>>> mydata.color = 'red'
|
||||
|
||||
So, the separate thread:
|
||||
|
||||
>>> thread = threading.Thread(target=f)
|
||||
>>> thread.start()
|
||||
>>> thread.join()
|
||||
|
||||
affects what we see:
|
||||
|
||||
>>> mydata.number
|
||||
11
|
||||
|
||||
>>> del mydata
|
||||
"""
|
||||
|
||||
# Threading import is at end
|
||||
|
||||
class _localbase(object):
|
||||
__slots__ = '_local__key', '_local__args', '_local__lock'
|
||||
|
||||
def __new__(cls, *args, **kw):
|
||||
self = object.__new__(cls)
|
||||
key = '_local__key', 'thread.local.' + str(id(self))
|
||||
object.__setattr__(self, '_local__key', key)
|
||||
object.__setattr__(self, '_local__args', (args, kw))
|
||||
object.__setattr__(self, '_local__lock', RLock())
|
||||
|
||||
if args or kw and (cls.__init__ is object.__init__):
|
||||
raise TypeError("Initialization arguments are not supported")
|
||||
|
||||
# We need to create the thread dict in anticipation of
|
||||
# __init__ being called, to make sure we don't call it
|
||||
# again ourselves.
|
||||
dict = object.__getattribute__(self, '__dict__')
|
||||
currentThread().__dict__[key] = dict
|
||||
|
||||
return self
|
||||
|
||||
def _patch(self):
|
||||
key = object.__getattribute__(self, '_local__key')
|
||||
d = currentThread().__dict__.get(key)
|
||||
if d is None:
|
||||
d = {}
|
||||
currentThread().__dict__[key] = d
|
||||
object.__setattr__(self, '__dict__', d)
|
||||
|
||||
# we have a new instance dict, so call out __init__ if we have
|
||||
# one
|
||||
cls = type(self)
|
||||
if cls.__init__ is not object.__init__:
|
||||
args, kw = object.__getattribute__(self, '_local__args')
|
||||
cls.__init__(self, *args, **kw)
|
||||
else:
|
||||
object.__setattr__(self, '__dict__', d)
|
||||
|
||||
class local(_localbase):
|
||||
|
||||
def __getattribute__(self, name):
|
||||
lock = object.__getattribute__(self, '_local__lock')
|
||||
lock.acquire()
|
||||
try:
|
||||
_patch(self)
|
||||
return object.__getattribute__(self, name)
|
||||
finally:
|
||||
lock.release()
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
lock = object.__getattribute__(self, '_local__lock')
|
||||
lock.acquire()
|
||||
try:
|
||||
_patch(self)
|
||||
return object.__setattr__(self, name, value)
|
||||
finally:
|
||||
lock.release()
|
||||
|
||||
def __delattr__(self, name):
|
||||
lock = object.__getattribute__(self, '_local__lock')
|
||||
lock.acquire()
|
||||
try:
|
||||
_patch(self)
|
||||
return object.__delattr__(self, name)
|
||||
finally:
|
||||
lock.release()
|
||||
|
||||
|
||||
def __del__():
|
||||
threading_enumerate = enumerate
|
||||
__getattribute__ = object.__getattribute__
|
||||
|
||||
def __del__(self):
|
||||
key = __getattribute__(self, '_local__key')
|
||||
|
||||
try:
|
||||
threads = list(threading_enumerate())
|
||||
except:
|
||||
# if enumerate fails, as it seems to do during
|
||||
# shutdown, we'll skip cleanup under the assumption
|
||||
# that there is nothing to clean up
|
||||
return
|
||||
|
||||
for thread in threads:
|
||||
try:
|
||||
__dict__ = thread.__dict__
|
||||
except AttributeError:
|
||||
# Thread is dying, rest in peace
|
||||
continue
|
||||
|
||||
if key in __dict__:
|
||||
try:
|
||||
del __dict__[key]
|
||||
except KeyError:
|
||||
pass # didn't have anything in this thread
|
||||
|
||||
return __del__
|
||||
__del__ = __del__()
|
||||
|
||||
from threading import currentThread, enumerate, RLock
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
@ -1,191 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import copy
|
||||
import logging
|
||||
|
||||
import openerp.netsvc as netsvc
|
||||
from openerp.tools.misc import UpdateableStr, UpdateableDict
|
||||
from openerp.tools.translate import translate
|
||||
from lxml import etree
|
||||
|
||||
import openerp.pooler as pooler
|
||||
|
||||
from openerp.osv.osv import except_osv
|
||||
from openerp.osv.orm import except_orm
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class except_wizard(Exception):
|
||||
def __init__(self, name, value):
|
||||
self.name = name
|
||||
self.value = value
|
||||
self.args = (name, value)
|
||||
|
||||
class interface(netsvc.Service):
|
||||
"""
|
||||
This is the base class used to implement Wizards. This class is deprecated
|
||||
and `openerp.osv.TransientModel` must be used instead.
|
||||
"""
|
||||
states = {}
|
||||
|
||||
def __init__(self, name):
|
||||
assert not self.exists('wizard.'+name), 'The wizard "%s" already exists!' % (name,)
|
||||
_logger.warning(
|
||||
"The wizard %s uses the deprecated openerp.wizard.interface class.\n"
|
||||
"It must use the openerp.osv.TransientModel class instead." % \
|
||||
name)
|
||||
super(interface, self).__init__('wizard.'+name)
|
||||
self.wiz_name = name
|
||||
|
||||
def translate_view(self, cr, node, state, lang):
|
||||
if node.get('string'):
|
||||
trans = translate(cr, self.wiz_name+','+state, 'wizard_view', lang, node.get('string').encode('utf8'))
|
||||
if trans:
|
||||
node.set('string', trans)
|
||||
for n in node:
|
||||
self.translate_view(cr, n, state, lang)
|
||||
|
||||
def execute_cr(self, cr, uid, data, state='init', context=None):
|
||||
if not context:
|
||||
context={}
|
||||
res = {}
|
||||
try:
|
||||
state_def = self.states[state]
|
||||
|
||||
result_def = state_def.get('result', {})
|
||||
|
||||
actions_res = {}
|
||||
# iterate through the list of actions defined for this state
|
||||
for action in state_def.get('actions', []):
|
||||
# execute them
|
||||
action_res = action(self, cr, uid, data, context)
|
||||
assert isinstance(action_res, dict), 'The return value of wizard actions should be a dictionary'
|
||||
actions_res.update(action_res)
|
||||
|
||||
res = copy.copy(result_def)
|
||||
res['datas'] = actions_res
|
||||
|
||||
lang = context.get('lang', False)
|
||||
if result_def['type'] == 'action':
|
||||
res['action'] = result_def['action'](self, cr, uid, data, context)
|
||||
elif result_def['type'] == 'form':
|
||||
fields = copy.deepcopy(result_def['fields'])
|
||||
arch = copy.copy(result_def['arch'])
|
||||
button_list = copy.copy(result_def['state'])
|
||||
|
||||
if isinstance(fields, UpdateableDict):
|
||||
fields = fields.dict
|
||||
if isinstance(arch, UpdateableStr):
|
||||
arch = arch.string
|
||||
|
||||
# fetch user-set defaut values for the field... shouldn't we pass it the uid?
|
||||
ir_values_obj = pooler.get_pool(cr.dbname).get('ir.values')
|
||||
defaults = ir_values_obj.get(cr, uid, 'default', False, [('wizard.'+self.wiz_name, False)])
|
||||
default_values = dict([(x[1], x[2]) for x in defaults])
|
||||
for val in fields.keys():
|
||||
if 'default' in fields[val]:
|
||||
# execute default method for this field
|
||||
if callable(fields[val]['default']):
|
||||
fields[val]['value'] = fields[val]['default'](uid, data, state)
|
||||
else:
|
||||
fields[val]['value'] = fields[val]['default']
|
||||
del fields[val]['default']
|
||||
else:
|
||||
# if user has set a default value for the field, use it
|
||||
if val in default_values:
|
||||
fields[val]['value'] = default_values[val]
|
||||
if 'selection' in fields[val]:
|
||||
if not isinstance(fields[val]['selection'], (tuple, list)):
|
||||
fields[val] = copy.copy(fields[val])
|
||||
fields[val]['selection'] = fields[val]['selection'](self, cr, uid, context)
|
||||
elif lang:
|
||||
res_name = "%s,%s,%s" % (self.wiz_name, state, val)
|
||||
trans = lambda x: translate(cr, res_name, 'selection', lang, x) or x
|
||||
for idx, (key, val2) in enumerate(fields[val]['selection']):
|
||||
fields[val]['selection'][idx] = (key, trans(val2))
|
||||
|
||||
if lang:
|
||||
# translate fields
|
||||
for field in fields:
|
||||
res_name = "%s,%s,%s" % (self.wiz_name, state, field)
|
||||
|
||||
trans = translate(cr, res_name, 'wizard_field', lang)
|
||||
if trans:
|
||||
fields[field]['string'] = trans
|
||||
|
||||
if 'help' in fields[field]:
|
||||
t = translate(cr, res_name, 'help', lang, fields[field]['help'])
|
||||
if t:
|
||||
fields[field]['help'] = t
|
||||
|
||||
# translate arch
|
||||
if not isinstance(arch, UpdateableStr):
|
||||
doc = etree.XML(arch)
|
||||
self.translate_view(cr, doc, state, lang)
|
||||
arch = etree.tostring(doc)
|
||||
|
||||
# translate buttons
|
||||
button_list = list(button_list)
|
||||
for i, aa in enumerate(button_list):
|
||||
button_name = aa[0]
|
||||
trans = translate(cr, self.wiz_name+','+state+','+button_name, 'wizard_button', lang)
|
||||
if trans:
|
||||
aa = list(aa)
|
||||
aa[1] = trans
|
||||
button_list[i] = aa
|
||||
|
||||
res['fields'] = fields
|
||||
res['arch'] = arch
|
||||
res['state'] = button_list
|
||||
|
||||
elif result_def['type'] == 'choice':
|
||||
next_state = result_def['next_state'](self, cr, uid, data, context)
|
||||
return self.execute_cr(cr, uid, data, next_state, context)
|
||||
|
||||
except Exception, e:
|
||||
if isinstance(e, except_wizard) \
|
||||
or isinstance(e, except_osv) \
|
||||
or isinstance(e, except_orm):
|
||||
netsvc.abort_response(2, e.name, 'warning', e.value)
|
||||
else:
|
||||
_logger.exception('Exception in call:')
|
||||
raise
|
||||
|
||||
return res
|
||||
|
||||
def execute(self, db, uid, data, state='init', context=None):
|
||||
if not context:
|
||||
context={}
|
||||
cr = pooler.get_db(db).cursor()
|
||||
try:
|
||||
try:
|
||||
res = self.execute_cr(cr, uid, data, state, context)
|
||||
cr.commit()
|
||||
except Exception:
|
||||
cr.rollback()
|
||||
raise
|
||||
finally:
|
||||
cr.close()
|
||||
return res
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
2
setup.py
2
setup.py
|
@ -108,6 +108,7 @@ setuptools.setup(
|
|||
'gdata',
|
||||
'lxml < 3', # windows binary http://www.lfd.uci.edu/~gohlke/pythonlibs/
|
||||
'mako',
|
||||
'mock',
|
||||
'PIL', # windows binary http://www.lfd.uci.edu/~gohlke/pythonlibs/
|
||||
'psutil', # windows binary code.google.com/p/psutil/downloads/list
|
||||
'psycopg2',
|
||||
|
@ -120,6 +121,7 @@ setuptools.setup(
|
|||
'pyyaml',
|
||||
'reportlab', # windows binary pypi.python.org/pypi/reportlab
|
||||
'simplejson',
|
||||
'unittest2',
|
||||
'vatnumber',
|
||||
'vobject',
|
||||
'werkzeug',
|
||||
|
|
Loading…
Reference in New Issue