[MERGE] from trunk

bzr revid: rco@openerp.com-20121221134259-scc92nby1dn91pe8
This commit is contained in:
Raphael Collet
2012-12-21 14:42:59 +01:00
56 changed files with 3079 additions and 919 deletions
+20 -13
View File
@@ -1,14 +1,21 @@
.*
*.egg-info
*.orig
*.vim
.*.swp
.bzrignore
.idea
.project
.pydevproject
.ropeproject
.settings
.DS_Store
openerp/addons/*
openerp/filestore*
.Python
*.pyc
*.pyo
bin/*
build/
RE:^bin/
RE:^dist/
RE:^include/
RE:^share/
RE:^man/
RE:^lib/
RE:^addons/\w+/doc/_build/
include/
lib/
share/
doc/_build/*
win32/*.bat
win32/meta.py
+5 -6
View File
@@ -3,7 +3,7 @@
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
# Copyright (C) 2010, 2012 OpenERP s.a. (<http://openerp.com>).
# Copyright (C) 2010-2012 OpenERP s.a. (<http://openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@@ -80,6 +80,7 @@ The kernel of OpenERP, needed for all installation.
'res/res_bank_view.xml',
'res/res_country_view.xml',
'res/res_currency_view.xml',
'res/wizard/change_password_wizard_view.xml',
'res/res_users_view.xml',
'res/res_partner_data.xml',
'res/ir_property_view.xml',
@@ -93,19 +94,17 @@ The kernel of OpenERP, needed for all installation.
'res/res_partner_image_demo.xml',
],
'test': [
'test/base_test.xml',
'test/base_test.yml',
'test/test_context.xml',
'test/bug_lp541545.xml',
'test/test_osv_expression.yml',
'test/test_ir_rule.yml', # <-- These tests modify/add/delete ir_rules.
# Commented because this takes some time.
# This must be (un)commented with the corresponding import statement
# in test/__init__.py.
# 'test/test_ir_cron.yml', # <-- These tests perform a roolback.
],
'installable': True,
'auto_install': True,
'css': ['static/src/css/modules.css'],
'js': [
'static/src/js/apps.js',
],
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+1 -1
View File
@@ -62,7 +62,7 @@
<record id="main_company" model="res.company">
<field name="name">Your Company</field>
<field name="partner_id" ref="main_partner"/>
<field name="rml_header1">Your Company Slogan</field>
<field name="rml_header1">Your Company Tagline</field>
<field name="currency_id" ref="base.EUR"/>
</record>
+10 -3
View File
@@ -8,14 +8,14 @@ msgstr ""
"Project-Id-Version: openobject-server\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-12-03 16:01+0000\n"
"PO-Revision-Date: 2012-12-17 20:41+0000\n"
"PO-Revision-Date: 2012-12-19 19:54+0000\n"
"Last-Translator: David Bowers <sales@skitzotek.com>\n"
"Language-Team: English (United Kingdom) <en_GB@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-12-18 04:58+0000\n"
"X-Generator: Launchpad (build 16372)\n"
"X-Launchpad-Export-Date: 2012-12-20 04:42+0000\n"
"X-Generator: Launchpad (build 16378)\n"
#. module: base
#: model:ir.module.module,description:base.module_account_check_writing
@@ -4199,6 +4199,13 @@ msgid ""
"invite.\n"
"\n"
msgstr ""
"\n"
"This module updates memos inside OpenERP when using an external pad\n"
"=====================================================================\n"
"\n"
"Use to update your text memo in real time with a user following that you "
"invite.\n"
"\n"
#. module: base
#: code:addons/base/module/module.py:609
File diff suppressed because it is too large Load Diff
+101 -36
View File
@@ -7,14 +7,14 @@ msgstr ""
"Project-Id-Version: OpenERP Server 6.0.0-rc1\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2012-12-03 16:01+0000\n"
"PO-Revision-Date: 2012-11-05 00:42+0000\n"
"PO-Revision-Date: 2012-12-20 19:00+0000\n"
"Last-Translator: Cristian Salamea (Gnuthink) <ovnicraft@gmail.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-12-04 05:03+0000\n"
"X-Generator: Launchpad (build 16335)\n"
"X-Launchpad-Export-Date: 2012-12-21 04:39+0000\n"
"X-Generator: Launchpad (build 16378)\n"
#. module: base
#: model:ir.module.module,description:base.module_account_check_writing
@@ -24,6 +24,10 @@ msgid ""
"================================================\n"
" "
msgstr ""
"\n"
"Módulo para la emisión e impresión de cheques\n"
"================================================\n"
" "
#. module: base
#: model:res.country,name:base.sh
@@ -59,7 +63,7 @@ msgstr "Código vista"
#. module: base
#: model:ir.module.module,summary:base.module_sale_stock
msgid "Quotation, Sale Orders, Delivery & Invoicing Control"
msgstr ""
msgstr "Presupuesto, órdenes de venta, entrega y control de facturación"
#. module: base
#: selection:ir.sequence,implementation:0
@@ -88,12 +92,12 @@ msgstr ""
#. module: base
#: model:ir.module.module,summary:base.module_point_of_sale
msgid "Touchscreen Interface for Shops"
msgstr ""
msgstr "Interfaz de pantalla táctil para tiendas"
#. module: base
#: model:ir.module.module,shortdesc:base.module_l10n_in_hr_payroll
msgid "Indian Payroll"
msgstr ""
msgstr "Nomina de la India"
#. module: base
#: help:ir.cron,model:0
@@ -123,6 +127,17 @@ msgid ""
" * Product Attributes\n"
" "
msgstr ""
"\n"
"Módulo que añade fabricantes y atributos en el formulario de producto.\n"
"===========================================================\n"
"\n"
"Puede definir los siguientes campos para un producto:\n"
"-------------------------------------------------\n"
"* Fabricante\n"
"* Nombre del producto del fabricante\n"
"* Código del producto del fabricante\n"
"* Atributos del producto\n"
" "
#. module: base
#: field:ir.actions.client,params:0
@@ -136,11 +151,14 @@ msgid ""
"The module adds google user in res user.\n"
"========================================\n"
msgstr ""
"\n"
"El módulo añade usuarios Google al registro de usuarios\n"
"============================================\n"
#. module: base
#: help:res.partner,employee:0
msgid "Check this box if this contact is an Employee."
msgstr ""
msgstr "Marque si el contacto es un empleado"
#. module: base
#: help:ir.model.fields,domain:0
@@ -171,7 +189,7 @@ msgstr "Ventana destino"
#. module: base
#: field:ir.actions.report.xml,report_rml:0
msgid "Main Report File Path"
msgstr ""
msgstr "Ruta de archivo del informe principal"
#. module: base
#: model:ir.module.module,shortdesc:base.module_sale_analytic_plans
@@ -192,6 +210,15 @@ msgid ""
"revenue\n"
"reports."
msgstr ""
"\n"
"Genere facturas desde los gastos y partes de horas.\n"
"========================================================\n"
"\n"
"Módulo para generar facturas basadas en los costes (recursos humanos, "
"gastos, ...).\n"
"\n"
"Puede definir tarifas en la contabilidad analítica, realizando algún informe "
"teórico de beneficios."
#. module: base
#: model:ir.module.module,description:base.module_crm
@@ -269,7 +296,7 @@ msgstr "creado."
#. module: base
#: field:ir.actions.report.xml,report_xsl:0
msgid "XSL Path"
msgstr ""
msgstr "Ruta XSL"
#. module: base
#: model:ir.module.module,shortdesc:base.module_l10n_tr
@@ -295,7 +322,7 @@ msgstr "Inuktitut / ᐃᓄᒃᑎᑐᑦ"
#. module: base
#: model:res.groups,name:base.group_multi_currency
msgid "Multi Currencies"
msgstr ""
msgstr "Multimoneda"
#. module: base
#: model:ir.module.module,description:base.module_l10n_cl
@@ -307,6 +334,12 @@ msgid ""
"\n"
" "
msgstr ""
"\n"
"Plan de cuentas y localización de impuestos chilenos.\n"
"==============================================\n"
"Plan contable chileno e impuestos de acuerdo a disposiciones vigentes\n"
"\n"
" "
#. module: base
#: model:ir.module.module,shortdesc:base.module_sale
@@ -319,6 +352,7 @@ msgid ""
"The internal user that is in charge of communicating with this contact if "
"any."
msgstr ""
"El usuario interno encargado de comunicarse con este contacto si lo hubiese."
#. module: base
#: view:res.partner:0
@@ -353,6 +387,15 @@ msgid ""
" * Commitment Date\n"
" * Effective Date\n"
msgstr ""
"\n"
"Añade información adicional al pedido de venta.\n"
"===================================================\n"
"\n"
"Puede añadir las siguientes fechas adicionales a un pedido de venta:\n"
"-----------------------------------------------------------\n"
" * Fecha solicitada\n"
" * Fecha de confirmación\n"
" * Fecha efectiva\n"
#. module: base
#: help:ir.actions.act_window,res_id:0
@@ -360,6 +403,8 @@ msgid ""
"Database ID of record to open in form view, when ``view_mode`` is set to "
"'form' only"
msgstr ""
"Id. de la base datos del registro para abrir en el formulario de vista, "
"cuando se establece el modo de visto únicamente a 'formulario'"
#. module: base
#: field:res.partner.address,name:0
@@ -435,6 +480,8 @@ msgid ""
"There is already a shared filter set as default for %(model)s, delete or "
"change it before setting a new default"
msgstr ""
"Ya existe un filtro compartido por defecto para %(model)s, elimínelo o "
"cámbielo antes de configurar uno nuevo"
#. module: base
#: code:addons/orm.py:2648
@@ -529,12 +576,12 @@ msgstr ""
#. module: base
#: field:ir.model.relation,name:0
msgid "Relation Name"
msgstr ""
msgstr "Nombre de relación"
#. module: base
#: view:ir.rule:0
msgid "Create Access Right"
msgstr ""
msgstr "Crear permisos de acceso"
#. module: base
#: model:res.country,name:base.tv
@@ -574,7 +621,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
@@ -697,7 +744,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
@@ -726,7 +773,7 @@ msgstr "Untranslated"
#. 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
@@ -820,6 +867,14 @@ msgid ""
" </p>\n"
" "
msgstr ""
"<p class=\"oe_view_nocontent_create\">\n"
"Pulse para añadir un contacto en la libreta de direcciones.\n"
"</p><p>\n"
"OpenERP le ayuda a gestionar todas las actividades relacionadas con los "
"clientes: discusiones, historial de oportunidades de negocio, documentos, "
"etc.\n"
"</p>\n"
" "
#. module: base
#: model:ir.module.module,description:base.module_web_linkedin
@@ -876,7 +931,7 @@ msgstr "Rumanía - Contabilidad"
#. module: base
#: model:ir.model,name:base.model_res_config_settings
msgid "res.config.settings"
msgstr ""
msgstr "Parámetros de configuración"
#. module: base
#: help:res.partner,image_small:0
@@ -885,6 +940,9 @@ msgid ""
"image, with aspect ratio preserved. Use this field anywhere a small image is "
"required."
msgstr ""
"Imagen de tamaño pequeño de este contacto. Se redimensiona automáticamente a "
"64x64 px, con el ratio de aspecto preservado. Use este campo donde se "
"requiera una imagen pequeña."
#. module: base
#: help:ir.actions.server,mobile:0
@@ -906,7 +964,7 @@ msgstr "Seguridad y autenticación"
#. module: base
#: model:ir.module.module,shortdesc:base.module_web_calendar
msgid "Web Calendar"
msgstr ""
msgstr "Calendario web"
#. module: base
#: selection:base.language.install,lang:0
@@ -917,7 +975,7 @@ msgstr "Swedish / svenska"
#: field:base.language.export,name:0
#: field:ir.attachment,datas_fname:0
msgid "File Name"
msgstr ""
msgstr "Nombre de Archivo"
#. module: base
#: model:res.country,name:base.rs
@@ -1013,7 +1071,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
@@ -1030,6 +1088,7 @@ msgstr "Zimbabwe"
msgid ""
"Type of the constraint: `f` for a foreign key, `u` for other constraints."
msgstr ""
"Tipo de restricción: 'f' para un clave foránea, 'u' para otras restricciones."
#. module: base
#: view:ir.actions.report.xml:0
@@ -1111,7 +1170,7 @@ msgstr "Otra licencia OSI Aprobada"
#. module: base
#: model:ir.module.module,shortdesc:base.module_web_gantt
msgid "Web Gantt"
msgstr ""
msgstr "Diagrama Gantt web"
#. module: base
#: model:ir.actions.act_window,name:base.act_menu_create
@@ -1138,7 +1197,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
@@ -1164,7 +1223,7 @@ msgstr "Principado de Andorra"
#. module: base
#: field:ir.rule,perm_read:0
msgid "Apply for Read"
msgstr ""
msgstr "Aplicar para lectura"
#. module: base
#: model:res.country,name:base.mn
@@ -1194,7 +1253,7 @@ msgstr ""
#: code:addons/base/ir/ir_model.py:727
#, python-format
msgid "Document model"
msgstr ""
msgstr "Modelo de documento"
#. module: base
#: view:res.lang:0
@@ -1244,12 +1303,12 @@ msgstr "El nombre del país debe ser único"
#. module: base
#: field:ir.module.module,installed_version:0
msgid "Latest Version"
msgstr ""
msgstr "Última versión"
#. module: base
#: view:ir.rule:0
msgid "Delete Access Right"
msgstr ""
msgstr "Eliminar permiso de acceso"
#. module: base
#: code:addons/base/ir/ir_mail_server.py:213
@@ -1276,7 +1335,7 @@ msgstr "Islas Caimán"
#. module: base
#: view:ir.rule:0
msgid "Record Rule"
msgstr ""
msgstr "Regla de registro"
#. module: base
#: model:res.country,name:base.kr
@@ -1304,7 +1363,7 @@ msgstr "Contribuyentes"
#. module: base
#: field:ir.rule,perm_unlink:0
msgid "Apply for Delete"
msgstr ""
msgstr "Aplicar para eliminación"
#. module: base
#: selection:ir.property,type:0
@@ -1319,7 +1378,7 @@ msgstr "Visible"
#. module: base
#: model:ir.actions.client,name:base.action_client_base_menu
msgid "Open Settings Menu"
msgstr ""
msgstr "Abrir menú 'Configuración'"
#. module: base
#: selection:base.language.install,lang:0
@@ -1405,7 +1464,7 @@ msgstr "Tests"
#. module: base
#: field:ir.actions.report.xml,attachment:0
msgid "Save as Attachment Prefix"
msgstr ""
msgstr "Prefijo del adjunto al guardar como"
#. module: base
#: field:ir.ui.view_sc,res_id:0
@@ -1442,7 +1501,7 @@ msgstr "Haití"
#. module: base
#: model:ir.module.module,shortdesc:base.module_l10n_fr_hr_payroll
msgid "French Payroll"
msgstr ""
msgstr "Nomina de Francia"
#. module: base
#: view:ir.ui.view:0
@@ -1562,6 +1621,12 @@ msgid ""
"=============\n"
" "
msgstr ""
"\n"
"Este módulo añade el menú y características de reclamaciones al portal si "
"los módulos 'claim' y 'portal' están instalados.\n"
"============================================================================="
"====================\n"
" "
#. module: base
#: model:ir.actions.act_window,help:base.action_res_partner_bank_account_form
@@ -1593,7 +1658,7 @@ msgstr ""
#. module: base
#: model:res.country,name:base.mf
msgid "Saint Martin (French part)"
msgstr ""
msgstr "San Martín (zona francesa)"
#. module: base
#: model:ir.model,name:base.model_ir_exports
@@ -1610,7 +1675,7 @@ msgstr "Ninguna lengua con el código \"%s\" existe"
#: model:ir.module.category,name:base.module_category_social_network
#: model:ir.module.module,shortdesc:base.module_mail
msgid "Social Network"
msgstr ""
msgstr "Red Social"
#. module: base
#: view:res.lang:0
@@ -1620,12 +1685,12 @@ msgstr "%Y - Año con siglo."
#. module: base
#: view:res.company:0
msgid "Report Footer Configuration"
msgstr ""
msgstr "Configuración de pie de informe"
#. module: base
#: field:ir.translation,comments:0
msgid "Translation comments"
msgstr ""
msgstr "Comentarios de traducción"
#. module: base
#: model:ir.module.module,description:base.module_lunch
@@ -1690,7 +1755,7 @@ msgstr ""
#. module: base
#: help:res.partner,website:0
msgid "Website of Partner or Company"
msgstr ""
msgstr "Sitio web de la empresa o compañía"
#. module: base
#: help:base.language.install,overwrite:0
@@ -1737,7 +1802,7 @@ msgstr ""
#. module: base
#: model:ir.module.module,summary:base.module_sale
msgid "Quotations, Sale Orders, Invoicing"
msgstr ""
msgstr "Presupuestos, órdenes de venta, facturación"
#. module: base
#: field:res.users,login:0
+44 -42
View File
@@ -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-18 22:00+0000\n"
"PO-Revision-Date: 2012-12-20 18:03+0000\n"
"Last-Translator: Ahti Hinnov <sipelgas@gmail.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-12-19 05:13+0000\n"
"X-Launchpad-Export-Date: 2012-12-21 04:38+0000\n"
"X-Generator: Launchpad (build 16378)\n"
#. module: base
@@ -531,7 +531,7 @@ msgstr "Kuupäeva vorming"
#. module: base
#: model:ir.module.module,shortdesc:base.module_base_report_designer
msgid "OpenOffice Report Designer"
msgstr ""
msgstr "OpenOffice aruande kujundaja"
#. module: base
#: model:res.country,name:base.an
@@ -980,7 +980,7 @@ msgstr ""
#. module: base
#: view:res.users:0
msgid "Email Preferences"
msgstr ""
msgstr "E-posti seadistused"
#. module: base
#: code:addons/base/ir/ir_fields.py:196
@@ -1355,7 +1355,7 @@ msgstr ""
#: code:addons/base/module/wizard/base_language_install.py:53
#, python-format
msgid "Language Pack"
msgstr ""
msgstr "Keelepakett"
#. module: base
#: model:ir.module.module,shortdesc:base.module_web_tests
@@ -1571,7 +1571,7 @@ msgstr ""
#. module: base
#: view:res.company:0
msgid "Report Footer Configuration"
msgstr ""
msgstr "Aruande jaluse seadistamine"
#. module: base
#: field:ir.translation,comments:0
@@ -1951,7 +1951,7 @@ msgstr "Lisatud mudel"
#. module: base
#: field:res.partner.bank,footer:0
msgid "Display on Reports"
msgstr ""
msgstr "Kuva aruannetes"
#. module: base
#: model:ir.module.module,description:base.module_project_timesheet
@@ -2005,7 +2005,7 @@ msgstr "Valem"
#: code:addons/base/res/res_users.py:311
#, python-format
msgid "Can not remove root user!"
msgstr "Ei saa eemaldada root kasutajat!"
msgstr "Ei saa eemaldada juurkasutajat!"
#. module: base
#: model:res.country,name:base.mw
@@ -2144,7 +2144,7 @@ msgstr "Hispaania / Español"
#. module: base
#: selection:base.language.install,lang:0
msgid "Korean (KP) / 한국어 (KP)"
msgstr ""
msgstr "Korea (KP) / 한국어 (KP)"
#. module: base
#: model:res.country,name:base.ax
@@ -2487,7 +2487,7 @@ msgstr ""
#. module: base
#: field:res.company,rml_header1:0
msgid "Company Slogan"
msgstr ""
msgstr "Ettevõtte loosung"
#. module: base
#: model:res.country,name:base.bb
@@ -2537,7 +2537,7 @@ msgstr "Kreeka / Ελληνικά"
#. module: base
#: field:res.company,custom_footer:0
msgid "Custom Footer"
msgstr ""
msgstr "Kohandatud jalus"
#. module: base
#: model:ir.module.module,shortdesc:base.module_sale_crm
@@ -2593,7 +2593,7 @@ msgstr "Arveldus"
#. module: base
#: field:ir.ui.view_sc,name:0
msgid "Shortcut Name"
msgstr "Kiirkorralduse nimi"
msgstr "Otsetee nimi"
#. module: base
#: field:res.partner,contact_address:0
@@ -2750,7 +2750,7 @@ msgstr "Ekspordi tõlked"
#: model:res.groups,name:base.group_sale_manager
#: model:res.groups,name:base.group_tool_manager
msgid "Manager"
msgstr ""
msgstr "Juht"
#. module: base
#: code:addons/base/ir/ir_model.py:718
@@ -3009,7 +3009,7 @@ msgstr "Norfolki saar"
#. module: base
#: selection:base.language.install,lang:0
msgid "Korean (KR) / 한국어 (KR)"
msgstr ""
msgstr "Korea (KR) / 한국어 (KR)"
#. module: base
#: help:ir.model.fields,model:0
@@ -3055,7 +3055,7 @@ msgstr "Viga! Sa ei saa luua rekursiivseid ettevõtteid."
#: view:res.users:0
#, python-format
msgid "Application"
msgstr ""
msgstr "Rakendamine"
#. module: base
#: model:res.groups,comment:base.group_hr_manager
@@ -3636,7 +3636,7 @@ msgstr ""
#: field:res.company,rml_footer:0
#: field:res.company,rml_footer_readonly:0
msgid "Report Footer"
msgstr ""
msgstr "Aruande jalus"
#. module: base
#: selection:res.lang,direction:0
@@ -3868,7 +3868,7 @@ msgstr "Peata kõik"
#. module: base
#: field:res.company,paper_format:0
msgid "Paper Format"
msgstr ""
msgstr "Paberi formaat"
#. module: base
#: model:ir.module.module,description:base.module_note_pad
@@ -4467,6 +4467,8 @@ msgid ""
"The selected language has been successfully installed. You must change the "
"preferences of the user and open a new menu to view the changes."
msgstr ""
"Valitud keel on edukalt paigaldatud. Muudatuste nägemiseks pead sa muutma "
"kasutaja eelistusi ja avama uue menüü."
#. module: base
#: field:ir.actions.act_window.view,view_id:0
@@ -4714,7 +4716,7 @@ msgstr ""
#. module: base
#: selection:base.language.install,lang:0
msgid "Portuguese (BR) / Português (BR)"
msgstr ""
msgstr "Portugali (BR) / Português (BR)"
#. module: base
#: model:ir.model,name:base.model_ir_needaction_mixin
@@ -5154,7 +5156,7 @@ msgstr ""
#. module: base
#: view:res.company:0
msgid "Preview Header/Footer"
msgstr ""
msgstr "Päise/jaluse eelvaade"
#. module: base
#: field:ir.ui.menu,parent_id:0
@@ -5694,7 +5696,7 @@ msgstr "Omadus kustutamisel many2one väljadel"
#. module: base
#: model:ir.module.category,name:base.module_category_accounting_and_finance
msgid "Accounting & Finance"
msgstr ""
msgstr "Raamatupidamine ja rahandus"
#. module: base
#: field:ir.actions.server,write_id:0
@@ -5717,7 +5719,7 @@ msgstr ""
#: model:ir.module.category,name:base.module_category_usability
#: view:res.users:0
msgid "Usability"
msgstr ""
msgstr "Kasutatavus"
#. module: base
#: field:ir.actions.act_window,domain:0
@@ -5836,12 +5838,12 @@ msgstr "Grupi nimi ei tohi alata \"-\" märgiga"
#. module: base
#: view:ir.module.module:0
msgid "Apps"
msgstr ""
msgstr "Rakendused"
#. module: base
#: view:ir.ui.view_sc:0
msgid "Shortcut"
msgstr "Lühend"
msgstr "Otsetee"
#. module: base
#: field:ir.model.data,date_init:0
@@ -6508,7 +6510,7 @@ msgstr ""
#. module: base
#: model:res.groups,name:base.group_partner_manager
msgid "Contact Creation"
msgstr ""
msgstr "Kontaktide loomine"
#. module: base
#: view:ir.module.module:0
@@ -6718,7 +6720,7 @@ msgstr ""
#. module: base
#: selection:res.currency,position:0
msgid "After Amount"
msgstr ""
msgstr "Peale summat"
#. module: base
#: selection:base.language.install,lang:0
@@ -7044,7 +7046,7 @@ msgstr "Mitmel dokumendil"
#: view:res.users:0
#: view:wizard.ir.model.menu.create:0
msgid "or"
msgstr ""
msgstr "või"
#. module: base
#: model:ir.module.module,shortdesc:base.module_account_accountant
@@ -7608,7 +7610,7 @@ msgstr "Kohandatud väli"
#. module: base
#: model:ir.module.module,summary:base.module_account_accountant
msgid "Financial and Analytic Accounting"
msgstr ""
msgstr "Finants- ja analüütiline raamatupidamine"
#. module: base
#: model:ir.module.module,shortdesc:base.module_portal_project
@@ -7708,7 +7710,7 @@ msgstr ""
#. module: base
#: model:res.partner.bank.type,name:base.bank_normal
msgid "Normal Bank Account"
msgstr ""
msgstr "Tavaline pangakonto"
#. module: base
#: view:ir.actions.wizard:0
@@ -7980,7 +7982,7 @@ msgstr "Tühista eemaldus"
#. module: base
#: view:res.bank:0
msgid "Communication"
msgstr "Suhtlemine"
msgstr "Kontakt"
#. module: base
#: model:ir.module.module,shortdesc:base.module_analytic
@@ -9180,7 +9182,7 @@ msgstr "Gambia"
#: view:res.partner:0
#: field:res.users,company_ids:0
msgid "Companies"
msgstr "Firmad"
msgstr "Ettevõtted"
#. module: base
#: help:res.currency,symbol:0
@@ -9249,7 +9251,7 @@ msgstr ""
#: view:res.users:0
#: view:wizard.ir.model.menu.create:0
msgid "Cancel"
msgstr "Tühista"
msgstr "Loobu"
#. module: base
#: code:addons/orm.py:1509
@@ -9571,7 +9573,7 @@ msgstr "Paigaldamise ootel"
#: model:ir.module.module,shortdesc:base.module_base
#: field:res.currency,base:0
msgid "Base"
msgstr "Baas"
msgstr "Alus"
#. module: base
#: field:ir.model.data,model:0
@@ -9660,12 +9662,12 @@ msgstr "Minutid"
#. module: base
#: view:res.currency:0
msgid "Display"
msgstr ""
msgstr "Kuvamine"
#. module: base
#: model:res.groups,name:base.group_multi_company
msgid "Multi Companies"
msgstr ""
msgstr "Mitu ettevõtet"
#. module: base
#: help:res.users,menu_id:0
@@ -10034,7 +10036,7 @@ msgstr "Ettevõttega seotud pangakontod"
#: model:ir.ui.menu,name:base.next_id_64
#: view:res.users:0
msgid "Sales"
msgstr "Müügid"
msgstr "Müük"
#. module: base
#: field:ir.actions.server,child_ids:0
@@ -10321,7 +10323,7 @@ msgstr ""
#. module: base
#: model:ir.module.module,shortdesc:base.module_sale_stock
msgid "Sales and Warehouse Management"
msgstr ""
msgstr "Müük ja laohaldus"
#. module: base
#: field:ir.model.fields,model:0
@@ -12095,7 +12097,7 @@ msgstr "ir.values"
#. module: base
#: model:res.groups,name:base.group_no_one
msgid "Technical Features"
msgstr ""
msgstr "Tehnilised funktsioonid"
#. module: base
#: selection:base.language.install,lang:0
@@ -12471,7 +12473,7 @@ msgstr "Küpros"
#. module: base
#: field:res.users,new_password:0
msgid "Set Password"
msgstr ""
msgstr "Määra salasõna"
#. module: base
#: field:ir.actions.server,subject:0
@@ -12503,7 +12505,7 @@ msgstr ""
#. module: base
#: selection:res.currency,position:0
msgid "Before Amount"
msgstr ""
msgstr "Enne summat"
#. module: base
#: field:res.request,act_from:0
@@ -12572,7 +12574,7 @@ msgstr ""
#. module: base
#: field:res.company,company_registry:0
msgid "Company Registry"
msgstr "Ettevõtte register"
msgstr "Ettevõtte registrikood"
#. module: base
#: view:ir.actions.report.xml:0
@@ -12917,7 +12919,7 @@ msgstr "Zaire"
#. module: base
#: model:ir.module.module,summary:base.module_project
msgid "Projects, Tasks"
msgstr ""
msgstr "Projektid, ülesanded"
#. module: base
#: field:workflow.instance,res_id:0
@@ -14247,7 +14249,7 @@ msgstr "Faks"
#: view:res.users:0
#: field:res.users,company_id:0
msgid "Company"
msgstr "Firma"
msgstr "Ettevõte"
#. module: base
#: model:ir.module.category,name:base.module_category_report_designer
+5 -5
View File
@@ -7,14 +7,14 @@ msgstr ""
"Project-Id-Version: OpenERP Server 5.0.4\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2012-12-03 16:01+0000\n"
"PO-Revision-Date: 2012-12-17 20:19+0000\n"
"PO-Revision-Date: 2012-12-19 20:48+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-18 04:58+0000\n"
"X-Generator: Launchpad (build 16372)\n"
"X-Launchpad-Export-Date: 2012-12-20 04:42+0000\n"
"X-Generator: Launchpad (build 16378)\n"
"Language: hr\n"
#. module: base
@@ -714,7 +714,7 @@ msgstr "Palau"
#. module: base
#: view:res.partner:0
msgid "Sales & Purchases"
msgstr "Prodaja i naručivanje"
msgstr "Prodaja i nabava"
#. module: base
#: view:ir.translation:0
@@ -1511,7 +1511,7 @@ msgstr ""
#: model:ir.module.category,name:base.module_category_purchase_management
#: model:ir.ui.menu,name:base.menu_purchase_root
msgid "Purchases"
msgstr "Nabave"
msgstr "Nabava"
#. module: base
#: model:res.country,name:base.md
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -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-18 23:39+0000\n"
"PO-Revision-Date: 2012-12-20 23:39+0000\n"
"Last-Translator: Sergio Corato <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-19 05:14+0000\n"
"X-Launchpad-Export-Date: 2012-12-21 04:38+0000\n"
"X-Generator: Launchpad (build 16378)\n"
#. module: base
@@ -158,7 +158,7 @@ msgstr ""
#. module: base
#: help:res.partner,employee:0
msgid "Check this box if this contact is an Employee."
msgstr "Seleziona questo box se il contatto è un Impiegato."
msgstr "Seleziona questo box se il contatto è un Dipendente."
#. module: base
#: help:ir.model.fields,domain:0
+168 -22
View File
@@ -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-18 18:59+0000\n"
"PO-Revision-Date: 2012-12-20 20:38+0000\n"
"Last-Translator: Fekete Mihai <mihai@erpsystems.ro>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-12-19 05:14+0000\n"
"X-Launchpad-Export-Date: 2012-12-21 04:38+0000\n"
"X-Generator: Launchpad (build 16378)\n"
#. module: base
@@ -327,7 +327,7 @@ msgstr "creat(a)."
#. module: base
#: field:ir.actions.report.xml,report_xsl:0
msgid "XSL Path"
msgstr ""
msgstr "Ruta XSL"
#. module: base
#: model:ir.module.module,shortdesc:base.module_l10n_tr
@@ -353,7 +353,7 @@ msgstr "Inuktitut / ᐃᓄᒃᑎᑐᑦ"
#. module: base
#: model:res.groups,name:base.group_multi_currency
msgid "Multi Currencies"
msgstr ""
msgstr "Mzi multe valute"
#. module: base
#: model:ir.module.module,description:base.module_l10n_cl
@@ -365,11 +365,17 @@ msgid ""
"\n"
" "
msgstr ""
"\n"
"Localizarea planului contabil si fiscal chilian\n"
"==============================================\n"
"Plan contabil si fiscal chilian in conformitate cu dispozitiile in vigoare\n"
"\n"
" "
#. module: base
#: model:ir.module.module,shortdesc:base.module_sale
msgid "Sales Management"
msgstr "Gestiune vanzari"
msgstr "Gestionarea vanzarilor"
#. module: base
#: help:res.partner,user_id:0
@@ -377,6 +383,8 @@ msgid ""
"The internal user that is in charge of communicating with this contact if "
"any."
msgstr ""
"Utilizatorul intern care este responsabil cu comunicarea cu acest contact, "
"daca exista."
#. module: base
#: view:res.partner:0
@@ -386,7 +394,7 @@ msgstr "Cauta Partener"
#. module: base
#: field:ir.module.category,module_nr:0
msgid "Number of Modules"
msgstr "Numar de module"
msgstr "Numar de Module"
#. module: base
#: help:multi_company.default,company_dest_id:0
@@ -411,6 +419,15 @@ msgid ""
" * Commitment Date\n"
" * Effective Date\n"
msgstr ""
"\n"
"Adauga informatii suplimentare despre date la comanda de vanzare.\n"
"===================================================\n"
"\n"
"Puteti adauga urmatoarele date suplimentare la o comanda de vanzare:\n"
"-----------------------------------------------------------\n"
" * Data Solicitata\n"
" * Data Angajament\n"
" * Data Efectiva\n"
#. module: base
#: help:ir.actions.act_window,res_id:0
@@ -418,6 +435,8 @@ msgid ""
"Database ID of record to open in form view, when ``view_mode`` is set to "
"'form' only"
msgstr ""
"ID baza de date a inregistrarii de deschis in vizualizare formular, atunci "
"cand ``modul_vizualizare`` este setat doar pe 'formular'"
#. module: base
#: field:res.partner.address,name:0
@@ -468,11 +487,18 @@ msgid ""
"document and Wiki based Hidden.\n"
" "
msgstr ""
"\n"
"Programul de instalare Ascuns bazat pe cunostinte.\n"
"=====================================\n"
"\n"
"Face Configuratia Aplicatiei Cunostinte disponibila, de unde puteti instala\n"
"documente si Ascuns bazat pe Wiki.\n"
" "
#. module: base
#: model:ir.module.category,name:base.module_category_customer_relationship_management
msgid "Customer Relationship Management"
msgstr "Gestionarea relatiilor cu clientii"
msgstr "Gestionarea Relatiilor cu Clientii"
#. module: base
#: model:ir.module.module,description:base.module_delivery
@@ -486,6 +512,14 @@ msgid ""
"invoices from picking, OpenERP is able to add and compute the shipping "
"line.\n"
msgstr ""
"\n"
"Va permite sa adaugati metode de livrare la comenzile de vanzare si "
"ridicare.\n"
"==============================================================\n"
"\n"
"Va puteti defini propriile grile de preturi pentru transport si livrare. "
"Atunci cand creati \n"
"facturi la ridicare, OpenERP poate adauga si calcula linia de transport.\n"
#. module: base
#: code:addons/base/ir/ir_filters.py:83
@@ -494,6 +528,8 @@ msgid ""
"There is already a shared filter set as default for %(model)s, delete or "
"change it before setting a new default"
msgstr ""
"Exista deja un filtru comun setat ca default pentru %(model)s, stergeti-l "
"sau schimbati-l inainte de a seta unul nou."
#. module: base
#: code:addons/orm.py:2648
@@ -536,7 +572,7 @@ msgstr "Obiect Sursa"
#. module: base
#: model:res.partner.bank.type,format_layout:base.bank_normal
msgid "%(bank_name)s: %(acc_number)s"
msgstr "%(bank_name)s: %(acc_number)s"
msgstr "%(nume_banca)s: %(numar_cont)s"
#. module: base
#: view:ir.actions.todo:0
@@ -591,12 +627,12 @@ msgstr ""
#. module: base
#: field:ir.model.relation,name:0
msgid "Relation Name"
msgstr ""
msgstr "Nume Relatie"
#. module: base
#: view:ir.rule:0
msgid "Create Access Right"
msgstr ""
msgstr "Creeaza Drept de acces"
#. module: base
#: model:res.country,name:base.tv
@@ -611,7 +647,7 @@ msgstr "Wizardul urmator"
#. module: base
#: field:res.lang,date_format:0
msgid "Date Format"
msgstr "Format data"
msgstr "Formatul Datei"
#. module: base
#: model:ir.module.module,shortdesc:base.module_base_report_designer
@@ -636,7 +672,7 @@ msgstr ""
#. module: base
#: view:workflow.transition:0
msgid "Workflow Transition"
msgstr ""
msgstr "Tranzitie Flux de lucru"
#. module: base
#: model:res.country,name:base.gf
@@ -646,7 +682,7 @@ msgstr "Guyana Franceza"
#. module: base
#: model:ir.module.module,summary:base.module_hr
msgid "Jobs, Departments, Employees Details"
msgstr ""
msgstr "Posturi, Departamente, Detalii angajati"
#. module: base
#: model:ir.module.module,description:base.module_analytic
@@ -662,6 +698,16 @@ msgid ""
"that have no counterpart in the general financial accounts.\n"
" "
msgstr ""
"\n"
"Modul pentru definirea obiectului contabilitatii analitice.\n"
"===============================================\n"
"\n"
"In OpenERP, conturile analitice sunt legate de conturile generale, dar sunt "
"tratate\n"
"in mod total independent. Astfel, puteti introduce diverse operatiuni "
"analitice\n"
"care nu au niciun corespondent in conturile financiare generale.\n"
" "
#. module: base
#: field:ir.ui.view.custom,ref_id:0
@@ -685,6 +731,19 @@ msgid ""
"* Use emails to automatically confirm and send acknowledgements for any "
"event registration\n"
msgstr ""
"\n"
"Organizarea si gestionarea Evenimentelor.\n"
"======================================\n"
"\n"
"Modulul eveniment va permite sa organizati eficient evenimente si toate "
"sarcinile asociate lor: planificare, urmarirea inregistrarii,\n"
"prezente, etc.\n"
"\n"
"Caracteristici cheie:\n"
"------------\n"
"* Gestioneaza Evenimentele si Inregistrarile dumneavoastra\n"
"* Foloseste email-urile pentru a confirma si trimite automat confirmarile "
"pentru inregistrarea oricarui eveniment\n"
#. module: base
#: selection:base.language.install,lang:0
@@ -710,6 +769,21 @@ msgid ""
"requested it.\n"
" "
msgstr ""
"\n"
"Traduceri Automate prin Gengo API\n"
"----------------------------------------\n"
"\n"
"Acest modul va instala programatorul pasiv pentru traduceri automate \n"
"folosind Gengo API. Pentru a-l activa, trebuie sa\n"
"1) Va configurati parametrii de autentificare Gengo din `Setari > Companii > "
"Parametrii Gengo`\n"
"2) Lansati wizard-ul din `Setari > Termenii Aplicatiei > Gengo: Solicitare "
"Manuala a Traducerii` si sa urmeze wizard-ul.\n"
"\n"
"Acest wizard va activa CRON si Programatorul si va incepe traducerea "
"automata prin intermediul Serviciilor Gengo pentru toti termenii solicitati "
"de dumneavoastra.\n"
" "
#. module: base
#: help:ir.actions.report.xml,attachment_use:0
@@ -759,7 +833,7 @@ msgstr "Columbia"
#. module: base
#: model:res.partner.title,name:base.res_partner_title_mister
msgid "Mister"
msgstr ""
msgstr "Domnul"
#. module: base
#: help:res.country,code:0
@@ -788,7 +862,7 @@ msgstr "Netradus(e)"
#. module: base
#: view:ir.mail_server:0
msgid "Outgoing Mail Server"
msgstr ""
msgstr "Server Iesire E-mail-uri"
#. module: base
#: help:ir.actions.act_window,context:0
@@ -822,7 +896,7 @@ msgstr "Mexic - Contabilitate"
#: help:ir.actions.server,action_id:0
msgid "Select the Action Window, Report, Wizard to be executed."
msgstr ""
"Selectati Fereastra de Actiune, Raport sau Asistent care urmeaza a fi "
"Selectati Fereastra de Actiune, Raportul sau Asistentul care urmeaza a fi "
"executat(a)."
#. module: base
@@ -866,6 +940,33 @@ msgid ""
"module named account_voucher.\n"
" "
msgstr ""
"\n"
"Contabilitate si Management Financiar.\n"
"====================================\n"
"\n"
"Modulul Finante si contabilitate acopera:\n"
"--------------------------------------------\n"
" * Contabilitatea Generala\n"
" * Costuri/Contabilitatea analitica\n"
" * Contabilitatea partii terte\n"
" * Gestonarea taxelor si impozitelor\n"
" * Bugete\n"
" * Facturile Clientului si ale Furnizorului\n"
" * Extrase bancare\n"
" * Procesul de reconciliere dupa partener\n"
"\n"
"Creeaza un panou pentru contabili care include:\n"
"--------------------------------------------------\n"
" Lista cu Facturile Clientilor care trebuie aprobate\n"
" Analiza Companiei\n"
" Grafic Trezorerie\n"
"\n"
"Procesele precum pastrarea unui registru contabil general sunt efectuate "
"prin Jurnalele financiare definite (linia inregistrarii sau gruparea se face "
"prin jurnal) \n"
"pentru un anumit an financiar, iar pentru pregatirea voucher-elor exista un "
"modul numit account_voucher (cont_voucher).\n"
" "
#. module: base
#: view:ir.model:0
@@ -885,6 +986,16 @@ msgid ""
" </p>\n"
" "
msgstr ""
"<p clasa=\"oe_vizualizare_niciuncontinut_creeaza\">\n"
" Faceti click pentru a adauga un contact in agenda "
"dumneavoastra.\n"
" </p><p>\n"
" OpenERP va ajuta sa tineti evidenta cu usurinta a tuturor "
"activitatilor legate de\n"
" un client: discutii, istoricul oportunitatilor de afaceri,\n"
" documente, etc.\n"
" </p>\n"
" "
#. module: base
#: model:ir.module.module,description:base.module_web_linkedin
@@ -895,6 +1006,11 @@ msgid ""
"This module provides the Integration of the LinkedIn with OpenERP.\n"
" "
msgstr ""
"\n"
"Modulul OpenERP Web LinkedIn.\n"
"============================\n"
"Acest modul asigura Integrarea lui LinkedIn in OpenERP.\n"
" "
#. module: base
#: help:ir.actions.act_window,src_model:0
@@ -917,7 +1033,7 @@ msgstr "Iordania"
#. module: base
#: help:ir.cron,nextcall:0
msgid "Next planned execution date for this job."
msgstr "Data urmatoarei executii planificate pentru acest proiect"
msgstr "Data urmatoarei executii planificate pentru aceasta sarcina."
#. module: base
#: model:res.country,name:base.er
@@ -932,7 +1048,7 @@ msgstr "Numele companiei trebuie sa fie unic !"
#. module: base
#: model:ir.ui.menu,name:base.menu_base_action_rule_admin
msgid "Automated Actions"
msgstr "Actiuni automate"
msgstr "Actiuni Automate"
#. module: base
#: model:ir.module.module,shortdesc:base.module_l10n_ro
@@ -942,7 +1058,7 @@ msgstr "Romania - Contabilitate"
#. module: base
#: model:ir.model,name:base.model_res_config_settings
msgid "res.config.settings"
msgstr ""
msgstr "res.configurare.setari"
#. module: base
#: help:res.partner,image_small:0
@@ -951,6 +1067,9 @@ msgid ""
"image, with aspect ratio preserved. Use this field anywhere a small image is "
"required."
msgstr ""
"Imagine de dimensiune mica a acestui contact. Este redimensionata automat ca "
"o imagine de 64x64px, cu raportul aspect pastrat. Folositi acest camp "
"oriunde este necesara o imagine mica."
#. module: base
#: help:ir.actions.server,mobile:0
@@ -972,7 +1091,7 @@ msgstr "Securitate si Autentificare"
#. module: base
#: model:ir.module.module,shortdesc:base.module_web_calendar
msgid "Web Calendar"
msgstr ""
msgstr "Calendar Web"
#. module: base
#: selection:base.language.install,lang:0
@@ -983,7 +1102,7 @@ msgstr "Suedeza / svenska"
#: field:base.language.export,name:0
#: field:ir.attachment,datas_fname:0
msgid "File Name"
msgstr ""
msgstr "Nume Fisier"
#. module: base
#: model:res.country,name:base.rs
@@ -1034,6 +1153,33 @@ msgid ""
"also possible in order to automatically create a meeting when a holiday "
"request is accepted by setting up a type of meeting in Leave Type.\n"
msgstr ""
"\n"
"Gestioneaza concediile si cererile de alocare\n"
"=====================================\n"
"\n"
"Aceasta aplicatie controleaza programul de concedii al companiei "
"dumneavoastra. Le permite angajatilor sa solicite concedii. Astfel, "
"managerii pot verifica cererile pentru concedii si le pot aproba sau "
"respinge. In acest mod puteti controla planificarea in ansamblu a "
"concediilor pentru companie sau departament.\n"
"\n"
"Puteti configura diverse tipuri de concedii (de boala, de odihna, zile "
"platite, ...) si aloca repede concedii unui angajat sau departament folosind "
"cererile de alocare. Un angajat poate de asemenea sa faca o cerere pentru "
"mai multe zile libere facand o noua ALocare. Acest lucru va mari totalul "
"zilelor disponibile pentru acel tip de concediu (daca cererea este "
"acceptata).\n"
"\n"
"Puteti tine evidenta concediilor in diverse moduri urmarind rapoartele: \n"
"\n"
"* Rezumat Concedii \n"
"* Concedii pe Departamente\n"
"* Analiza Concediilor\n"
"\n"
"O sincronizare cu o agenda interna (Intalnirile modulului MRC) este de "
"asemenea posibila pentru a crea in mod automat o intalnire atunci cand o "
"cerere de concediu este acceptata prin setarea unui tip de intalnire in "
"Tipul Concediului.\n"
#. module: base
#: selection:base.language.install,lang:0
@@ -1081,7 +1227,7 @@ msgstr "Preferinte 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' nu pare a fi o data valabila pentru campul '%%(field)s'"
#. module: base
#: view:res.partner:0
+25 -7
View File
@@ -7,14 +7,14 @@ msgstr ""
"Project-Id-Version: OpenERP Server 5.0.4\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2012-12-03 16:01+0000\n"
"PO-Revision-Date: 2012-12-06 18:17+0000\n"
"Last-Translator: ccdos <ccdos@163.com>\n"
"PO-Revision-Date: 2012-12-19 07:05+0000\n"
"Last-Translator: Wei \"oldrev\" Li <oldrev@gmail.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-12-07 04:34+0000\n"
"X-Generator: Launchpad (build 16341)\n"
"X-Launchpad-Export-Date: 2012-12-20 04:43+0000\n"
"X-Generator: Launchpad (build 16378)\n"
#. module: base
#: model:ir.module.module,description:base.module_account_check_writing
@@ -1517,7 +1517,7 @@ msgstr "海地"
#. module: base
#: model:ir.module.module,shortdesc:base.module_l10n_fr_hr_payroll
msgid "French Payroll"
msgstr ""
msgstr "法国薪酬管理"
#. module: base
#: view:ir.ui.view:0
@@ -2871,6 +2871,9 @@ msgid ""
"Module to attach a google document to any model.\n"
"================================================\n"
msgstr ""
"\n"
"用于附加 Google 文档到任意模型的模块。\n"
"================================================\n"
#. module: base
#: code:addons/base/ir/ir_fields.py:334
@@ -3118,7 +3121,7 @@ msgstr ""
#. module: base
#: model:ir.module.module,shortdesc:base.module_web_linkedin
msgid "LinkedIn Integration"
msgstr ""
msgstr "LinkedIn 集成"
#. module: base
#: code:addons/orm.py:2021
@@ -3301,6 +3304,10 @@ msgid ""
"==========================================\n"
" "
msgstr ""
"\n"
"比利时薪酬规则的会计数据。\n"
"==========================================\n"
" "
#. module: base
#: model:res.country,name:base.am
@@ -4002,6 +4009,17 @@ msgid ""
"If you need to manage your meetings, you should install the CRM module.\n"
" "
msgstr ""
"\n"
"这是功能完整的日程表系统\n"
"========================================\n"
"\n"
"支持:\n"
"------------\n"
" - 约会排程\n"
" - 重复性事件\n"
"\n"
"如果您需要管理您的会议,您应该安装 CRM 模块。\n"
" "
#. module: base
#: model:res.country,name:base.je
@@ -4039,7 +4057,7 @@ msgstr "%x - 使用日期表示"
#. module: base
#: view:res.partner:0
msgid "Tag"
msgstr ""
msgstr "标签"
#. module: base
#: view:res.lang:0
+163 -56
View File
@@ -18,11 +18,164 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import itertools
import hashlib
import itertools
import os
import re
from openerp import tools
from openerp.osv import fields,osv
class ir_attachment(osv.osv):
"""Attachments are used to link binary files or url to any openerp document.
External attachment storage
---------------------------
The 'data' function field (_data_get,data_set) is implemented using
_file_read, _file_write and _file_delete which can be overridden to
implement other storage engines, shuch methods should check for other
location pseudo uri (example: hdfs://hadoppserver)
The default implementation is the file:dirname location that stores files
on the local filesystem using name based on their sha1 hash
"""
def _name_get_resname(self, cr, uid, ids, object, method, context):
data = {}
for attachment in self.browse(cr, uid, ids, context=context):
model_object = attachment.res_model
res_id = attachment.res_id
if model_object and res_id:
model_pool = self.pool.get(model_object)
res = model_pool.name_get(cr,uid,[res_id],context)
res_name = res and res[0][1] or False
if res_name:
field = self._columns.get('res_name',False)
if field and len(res_name) > field.size:
res_name = res_name[:field.size-3] + '...'
data[attachment.id] = res_name
else:
data[attachment.id] = False
return data
# 'data' field implementation
def _full_path(self, cr, uid, location, path):
# location = 'file:filestore'
assert location.startswith('file:'), "Unhandled filestore location %s" % location
location = location[5:]
# sanitize location name and path
location = re.sub('[.]','',location)
location = location.strip('/\\')
path = re.sub('[.]','',path)
path = path.strip('/\\')
return os.path.join(tools.config['root_path'], location, cr.dbname, path)
def _file_read(self, cr, uid, location, fname, bin_size=False):
full_path = self._full_path(cr, uid, location, fname)
r = ''
try:
if bin_size:
r = os.path.getsize(full_path)
else:
r = open(full_path).read().encode('base64')
except IOError:
_logger.error("_read_file reading %s",full_path)
return r
def _file_write(self, cr, uid, location, value):
bin_value = value.decode('base64')
fname = hashlib.sha1(bin_value).hexdigest()
# scatter files across 1024 dirs
# we use '/' in the db (even on windows)
fname = fname[:3] + '/' + fname
full_path = self._full_path(cr, uid, location, fname)
try:
dirname = os.path.dirname(full_path)
if not os.path.isdir(dirname):
os.makedirs(dirname)
open(full_path,'wb').write(bin_value)
except IOError:
_logger.error("_file_write writing %s",full_path)
return fname
def _file_delete(self, cr, uid, location, fname):
count = self.search(cr, 1, [('store_fname','=',fname)], count=True)
if count <= 1:
full_path = self._full_path(cr, uid, location, fname)
try:
os.unlink(full_path)
except IOError:
# Harmless and needed for race conditions
_logger.error("_file_delete could not unlink %s",full_path)
def _data_get(self, cr, uid, ids, name, arg, context=None):
if context is None:
context = {}
result = {}
location = self.pool.get('ir.config_parameter').get_param(cr, uid, 'ir_attachment.location')
bin_size = context.get('bin_size')
for attach in self.browse(cr, uid, ids, context=context):
if location and attach.store_fname:
result[attach.id] = self._file_read(cr, uid, location, attach.store_fname, bin_size)
else:
result[attach.id] = attach.db_datas
return result
def _data_set(self, cr, uid, id, name, value, arg, context=None):
# We dont handle setting data to null
if not value:
return True
if context is None:
context = {}
location = self.pool.get('ir.config_parameter').get_param(cr, uid, 'ir_attachment.location')
file_size = len(value.decode('base64'))
if location:
attach = self.browse(cr, uid, id, context=context)
if attach.store_fname:
self._file_delete(cr, uid, location, attach.store_fname)
fname = self._file_write(cr, uid, location, value)
super(ir_attachment, self).write(cr, uid, [id], {'store_fname': fname, 'file_size': file_size}, context=context)
else:
super(ir_attachment, self).write(cr, uid, [id], {'db_datas': value, 'file_size': file_size}, context=context)
return True
_name = 'ir.attachment'
_columns = {
'name': fields.char('Attachment Name',size=256, required=True),
'datas_fname': fields.char('File Name',size=256),
'description': fields.text('Description'),
'res_name': fields.function(_name_get_resname, type='char', size=128, string='Resource Name', store=True),
'res_model': fields.char('Resource Model',size=64, readonly=True, help="The database object this attachment will be attached to"),
'res_id': fields.integer('Resource ID', readonly=True, help="The record id this is attached to"),
'create_date': fields.datetime('Date Created', readonly=True),
'create_uid': fields.many2one('res.users', 'Owner', readonly=True),
'company_id': fields.many2one('res.company', 'Company', change_default=True),
'type': fields.selection( [ ('url','URL'), ('binary','Binary'), ],
'Type', help="Binary File or URL", required=True, change_default=True),
'url': fields.char('Url', size=1024),
# al: We keep shitty field names for backward compatibility with document
'datas': fields.function(_data_get, fnct_inv=_data_set, string='File Content', type="binary", nodrop=True),
'store_fname': fields.char('Stored Filename', size=256),
'db_datas': fields.binary('Database Data'),
'file_size': fields.integer('File Size'),
}
_defaults = {
'type': 'binary',
'file_size': 0,
'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'ir.attachment', context=c),
}
def _auto_init(self, cr, context=None):
super(ir_attachment, self)._auto_init(cr, context)
cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('ir_attachment_res_idx',))
if not cr.fetchone():
cr.execute('CREATE INDEX ir_attachment_res_idx ON ir_attachment (res_model, res_id)')
cr.commit()
def check(self, cr, uid, ids, mode, context=None, values=None):
"""Restricts the access to an ir.attachment, according to referred model
In the 'document' module, it is overriden to relax this hard rule, since
@@ -108,6 +261,8 @@ class ir_attachment(osv.osv):
def write(self, cr, uid, ids, vals, context=None):
self.check(cr, uid, ids, 'write', context=context, values=vals)
if 'file_size' in vals:
del vals['file_size']
return super(ir_attachment, self).write(cr, uid, ids, vals, context)
def copy(self, cr, uid, id, default=None, context=None):
@@ -116,70 +271,22 @@ class ir_attachment(osv.osv):
def unlink(self, cr, uid, ids, context=None):
self.check(cr, uid, ids, 'unlink', context=context)
location = self.pool.get('ir.config_parameter').get_param(cr, uid, 'ir_attachment.location')
if location:
for attach in self.browse(cr, uid, ids, context=context):
if attach.store_fname:
self._file_delete(cr, uid, location, attach.store_fname)
return super(ir_attachment, self).unlink(cr, uid, ids, context)
def create(self, cr, uid, values, context=None):
self.check(cr, uid, [], mode='create', context=context, values=values)
if 'file_size' in values:
del values['file_size']
return super(ir_attachment, self).create(cr, uid, values, context)
def action_get(self, cr, uid, context=None):
return self.pool.get('ir.actions.act_window').for_xml_id(
cr, uid, 'base', 'action_attachment', context=context)
def _name_get_resname(self, cr, uid, ids, object, method, context):
data = {}
for attachment in self.browse(cr, uid, ids, context=context):
model_object = attachment.res_model
res_id = attachment.res_id
if model_object and res_id:
model_pool = self.pool.get(model_object)
res = model_pool.name_get(cr,uid,[res_id],context)
res_name = res and res[0][1] or False
if res_name:
field = self._columns.get('res_name',False)
if field and len(res_name) > field.size:
res_name = res_name[:field.size-3] + '...'
data[attachment.id] = res_name
else:
data[attachment.id] = False
return data
_name = 'ir.attachment'
_columns = {
'name': fields.char('Attachment Name',size=256, required=True),
'datas': fields.binary('Data'),
'datas_fname': fields.char('File Name',size=256),
'description': fields.text('Description'),
'res_name': fields.function(_name_get_resname, type='char', size=128,
string='Resource Name', store=True),
'res_model': fields.char('Resource Object',size=64, readonly=True,
help="The database object this attachment will be attached to"),
'res_id': fields.integer('Resource ID', readonly=True,
help="The record id this is attached to"),
'url': fields.char('Url', size=512, oldname="link"),
'type': fields.selection(
[ ('url','URL'), ('binary','Binary'), ],
'Type', help="Binary File or external URL", required=True, change_default=True),
'create_date': fields.datetime('Date Created', readonly=True),
'create_uid': fields.many2one('res.users', 'Owner', readonly=True),
'company_id': fields.many2one('res.company', 'Company', change_default=True),
}
_defaults = {
'type': 'binary',
'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'ir.attachment', context=c),
}
def _auto_init(self, cr, context=None):
super(ir_attachment, self)._auto_init(cr, context)
cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('ir_attachment_res_idx',))
if not cr.fetchone():
cr.execute('CREATE INDEX ir_attachment_res_idx ON ir_attachment (res_model, res_id)')
cr.commit()
ir_attachment()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+176 -85
View File
@@ -24,12 +24,22 @@ from docutils.transforms import Transform, writer_aux
from docutils.writers.html4css1 import Writer
import imp
import logging
import os
import re
import shutil
import tempfile
import urllib
import urllib2
import zipfile
import zipimport
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO # NOQA
import openerp
from openerp import modules, pooler, release, tools, addons
from openerp import modules, pooler, tools, addons
from openerp.modules.db import create_categories
from openerp.tools.parse_version import parse_version
from openerp.tools.translate import _
@@ -43,14 +53,29 @@ ACTION_DICT = {
'res_model': 'base.module.upgrade',
'target': 'new',
'type': 'ir.actions.act_window',
'nodestroy':True,
'nodestroy': True,
}
def backup(path, raise_exception=True):
path = os.path.normpath(path)
if not os.path.exists(path):
if not raise_exception:
return None
raise OSError('path does not exists')
cnt = 1
while True:
bck = '%s~%d' % (path, cnt)
if not os.path.exists(bck):
shutil.move(path, bck)
return bck
cnt += 1
class module_category(osv.osv):
_name = "ir.module.category"
_description = "Application"
def _module_nbr(self,cr,uid, ids, prop, unknow_none, context):
def _module_nbr(self, cr, uid, ids, prop, unknow_none, context):
cr.execute('SELECT category_id, COUNT(*) \
FROM ir_module_module \
WHERE category_id IN %(ids)s \
@@ -58,7 +83,7 @@ class module_category(osv.osv):
FROM ir_module_category \
WHERE parent_id IN %(ids)s) \
GROUP BY category_id', {'ids': tuple(ids)}
)
)
result = dict(cr.fetchall())
for id in ids:
cr.execute('select id from ir_module_category where parent_id=%s', (id,))
@@ -71,16 +96,16 @@ class module_category(osv.osv):
'parent_id': fields.many2one('ir.module.category', 'Parent Application', select=True),
'child_ids': fields.one2many('ir.module.category', 'parent_id', 'Child Applications'),
'module_nr': fields.function(_module_nbr, string='Number of Modules', type='integer'),
'module_ids' : fields.one2many('ir.module.module', 'category_id', 'Modules'),
'description' : fields.text("Description", translate=True),
'sequence' : fields.integer('Sequence'),
'visible' : fields.boolean('Visible'),
'module_ids': fields.one2many('ir.module.module', 'category_id', 'Modules'),
'description': fields.text("Description", translate=True),
'sequence': fields.integer('Sequence'),
'visible': fields.boolean('Visible'),
'xml_id': fields.function(osv.osv.get_external_id, type='char', size=128, string="External ID"),
}
_order = 'name'
_defaults = {
'visible' : 1,
'visible': 1,
}
class MyFilterMessages(Transform):
@@ -119,7 +144,6 @@ class module(osv.osv):
info = {}
try:
info = modules.load_information_from_description_file(name)
info['version'] = release.major_version + '.' + info['version']
except Exception:
_logger.debug('Error when trying to fetch informations for '
'module %s', name, exc_info=True)
@@ -134,9 +158,10 @@ class module(osv.osv):
return res
def _get_latest_version(self, cr, uid, ids, field_name=None, arg=None, context=None):
res = dict.fromkeys(ids, '')
default_version = modules.adapt_version('1.0')
res = dict.fromkeys(ids, default_version)
for m in self.browse(cr, uid, ids):
res[m.id] = self.get_module_info(m.name).get('version', '')
res[m.id] = self.get_module_info(m.name).get('version', default_version)
return res
def _get_views(self, cr, uid, ids, field_name=None, arg=None, context=None):
@@ -167,9 +192,11 @@ class module(osv.osv):
continue
# then, search and group ir.model.data records
imd_models = dict( [(m,[]) for m in dmodels])
imd_ids = model_data_obj.search(cr,uid,[('module','=', module_rec.name),
('model','in',tuple(dmodels))])
imd_models = dict([(m, []) for m in dmodels])
imd_ids = model_data_obj.search(cr, uid, [
('module', '=', module_rec.name),
('model', 'in', tuple(dmodels))
])
for imd_res in model_data_obj.read(cr, uid, imd_ids, ['model', 'res_id'], context=context):
imd_models[imd_res['model']].append(imd_res['res_id'])
@@ -181,7 +208,7 @@ class module(osv.osv):
view_ids = imd_models.get('ir.ui.view', [])
for v in view_obj.browse(cr, uid, view_ids, context=context):
aa = v.inherit_id and '* INHERIT ' or ''
res_mod_dic['views_by_module'].append(aa + v.name + '('+v.type+')')
res_mod_dic['views_by_module'].append('%s%s (%s)' % (aa, v.name, v.type))
report_ids = imd_models.get('ir.actions.report.xml', [])
for rx in report_obj.browse(cr, uid, report_ids, context=context):
@@ -191,15 +218,13 @@ class module(osv.osv):
for um in menu_obj.browse(cr, uid, menu_ids, context=context):
res_mod_dic['menus_by_module'].append(um.complete_name)
except KeyError, e:
_logger.warning(
'Data not found for items of %s', module_rec.name)
_logger.warning('Data not found for items of %s', module_rec.name)
except AttributeError, e:
_logger.warning(
'Data not found for items of %s %s', module_rec.name, str(e))
_logger.warning('Data not found for items of %s %s', module_rec.name, str(e))
except Exception, e:
_logger.warning('Unknown error while fetching data of %s',
module_rec.name, exc_info=True)
for key, _ in res.iteritems():
_logger.warning('Unknown error while fetching data of %s', module_rec.name, exc_info=True)
for key in res.iterkeys():
for k, v in res[key].iteritems():
res[key][k] = "\n".join(sorted(v))
return res
@@ -232,36 +257,34 @@ class module(osv.osv):
# installed_version refer the latest version (the one on disk)
# latest_version refer the installed version (the one in database)
# published_version refer the version available on the repository
'installed_version': fields.function(_get_latest_version,
string='Latest Version', type='char'),
'installed_version': fields.function(_get_latest_version, string='Latest Version', type='char'),
'latest_version': fields.char('Installed Version', size=64, readonly=True),
'published_version': fields.char('Published Version', size=64, readonly=True),
'url': fields.char('URL', size=128, readonly=True),
'sequence': fields.integer('Sequence'),
'dependencies_id': fields.one2many('ir.module.module.dependency',
'module_id', 'Dependencies', readonly=True),
'dependencies_id': fields.one2many('ir.module.module.dependency', 'module_id', 'Dependencies', readonly=True),
'auto_install': fields.boolean('Automatic Installation',
help='An auto-installable module is automatically installed by the '
'system when all its dependencies are satisfied. '
'If the module has no dependency, it is always installed.'),
help='An auto-installable module is automatically installed by the '
'system when all its dependencies are satisfied. '
'If the module has no dependency, it is always installed.'),
'state': fields.selection([
('uninstallable','Not Installable'),
('uninstalled','Not Installed'),
('installed','Installed'),
('to upgrade','To be upgraded'),
('to remove','To be removed'),
('to install','To be installed')
('uninstallable', 'Not Installable'),
('uninstalled', 'Not Installed'),
('installed', 'Installed'),
('to upgrade', 'To be upgraded'),
('to remove', 'To be removed'),
('to install', 'To be installed')
], string='Status', readonly=True, select=True),
'demo': fields.boolean('Demo Data', readonly=True),
'license': fields.selection([
('GPL-2', 'GPL Version 2'),
('GPL-2 or any later version', 'GPL-2 or later version'),
('GPL-3', 'GPL Version 3'),
('GPL-3 or any later version', 'GPL-3 or later version'),
('AGPL-3', 'Affero GPL-3'),
('Other OSI approved licence', 'Other OSI Approved Licence'),
('Other proprietary', 'Other Proprietary')
('GPL-2', 'GPL Version 2'),
('GPL-2 or any later version', 'GPL-2 or later version'),
('GPL-3', 'GPL Version 3'),
('GPL-3 or any later version', 'GPL-3 or later version'),
('AGPL-3', 'Affero GPL-3'),
('Other OSI approved licence', 'Other OSI Approved Licence'),
('Other proprietary', 'Other Proprietary')
], string='License', readonly=True),
'menus_by_module': fields.function(_get_views, string='Menus', type='text', multi="meta", store=True),
'reports_by_module': fields.function(_get_views, string='Reports', type='text', multi="meta", store=True),
@@ -283,7 +306,7 @@ class module(osv.osv):
return _('The name of the module must be unique !')
_sql_constraints = [
('name_uniq', 'UNIQUE (name)',_name_uniq_msg ),
('name_uniq', 'UNIQUE (name)', _name_uniq_msg),
]
def unlink(self, cr, uid, ids, context=None):
@@ -292,10 +315,9 @@ class module(osv.osv):
if isinstance(ids, (int, long)):
ids = [ids]
mod_names = []
for mod in self.read(cr, uid, ids, ['state','name'], context):
for mod in self.read(cr, uid, ids, ['state', 'name'], context):
if mod['state'] in ('installed', 'to upgrade', 'to remove', 'to install'):
raise orm.except_orm(_('Error'),
_('You try to remove a module that is installed or will be installed'))
raise orm.except_orm(_('Error'), _('You try to remove a module that is installed or will be installed'))
mod_names.append(mod['name'])
#Removing the entry from ir_model_data
#ids_meta = self.pool.get('ir.model.data').search(cr, uid, [('name', '=', 'module_meta_information'), ('module', 'in', mod_names)])
@@ -340,7 +362,7 @@ class module(osv.osv):
raise orm.except_orm(_('Error'), msg % (module_name, e.args[0]))
def state_update(self, cr, uid, ids, newstate, states_to_update, context=None, level=100):
if level<1:
if level < 1:
raise orm.except_orm(_('Error'), _('Recursion error in modules dependencies !'))
demo = False
for module in self.browse(cr, uid, ids, context=context):
@@ -348,9 +370,9 @@ class module(osv.osv):
for dep in module.dependencies_id:
if dep.state == 'unknown':
raise orm.except_orm(_('Error'), _("You try to install module '%s' that depends on module '%s'.\nBut the latter module is not available in your system.") % (module.name, dep.name,))
ids2 = self.search(cr, uid, [('name','=',dep.name)])
ids2 = self.search(cr, uid, [('name', '=', dep.name)])
if dep.state != newstate:
mdemo = self.state_update(cr, uid, ids2, newstate, states_to_update, context, level-1,) or mdemo
mdemo = self.state_update(cr, uid, ids2, newstate, states_to_update, context, level - 1) or mdemo
else:
od = self.browse(cr, uid, ids2)[0]
mdemo = od.demo or mdemo
@@ -359,7 +381,7 @@ class module(osv.osv):
if not module.dependencies_id:
mdemo = module.demo
if module.state in states_to_update:
self.write(cr, uid, [module.id], {'state': newstate, 'demo':mdemo})
self.write(cr, uid, [module.id], {'state': newstate, 'demo': mdemo})
demo = demo or mdemo
return demo
@@ -403,7 +425,7 @@ class module(osv.osv):
return self._button_immediate_function(cr, uid, ids, self.button_install, context=context)
def button_install_cancel(self, cr, uid, ids, context=None):
self.write(cr, uid, ids, {'state': 'uninstalled', 'demo':False})
self.write(cr, uid, ids, {'state': 'uninstalled', 'demo': False})
return True
def module_uninstall(self, cr, uid, ids, context=None):
@@ -421,12 +443,13 @@ class module(osv.osv):
return True
def downstream_dependencies(self, cr, uid, ids, known_dep_ids=None,
exclude_states=['uninstalled','uninstallable','to remove'],
exclude_states=['uninstalled', 'uninstallable', 'to remove'],
context=None):
"""Return the ids of all modules that directly or indirectly depend
on the given module `ids`, and that satisfy the `exclude_states`
filter"""
if not ids: return []
if not ids:
return []
known_dep_ids = set(known_dep_ids or [])
cr.execute('''SELECT DISTINCT m.id
FROM
@@ -437,13 +460,13 @@ class module(osv.osv):
d.name IN (SELECT name from ir_module_module where id in %s) AND
m.state NOT IN %s AND
m.id NOT IN %s ''',
(tuple(ids),tuple(exclude_states), tuple(known_dep_ids or ids)))
(tuple(ids), tuple(exclude_states), tuple(known_dep_ids or ids)))
new_dep_ids = set([m[0] for m in cr.fetchall()])
missing_mod_ids = new_dep_ids - known_dep_ids
known_dep_ids |= new_dep_ids
if missing_mod_ids:
known_dep_ids |= set(self.downstream_dependencies(cr, uid, list(missing_mod_ids),
known_dep_ids, exclude_states,context))
known_dep_ids, exclude_states, context))
return list(known_dep_ids)
def _button_immediate_function(self, cr, uid, ids, function, context=None):
@@ -460,9 +483,9 @@ class module(osv.osv):
menu_obj = self.pool.get('ir.ui.menu')
menu_ids = menu_obj.search(cr, uid, [('parent_id', '=', False)], context=context)
return {
'type' : 'ir.actions.client',
'tag' : 'reload',
'params' : {'menu_id' : menu_ids and menu_ids[0] or False}
'type': 'ir.actions.client',
'tag': 'reload',
'params': {'menu_id': menu_ids and menu_ids[0] or False}
}
def button_immediate_uninstall(self, cr, uid, ids, context=None):
@@ -496,20 +519,19 @@ class module(osv.osv):
self.update_list(cr, uid)
i = 0
while i<len(todo):
while i < len(todo):
mod = todo[i]
i += 1
if mod.state not in ('installed','to upgrade'):
raise orm.except_orm(_('Error'),
_("Can not upgrade module '%s'. It is not installed.") % (mod.name,))
if mod.state not in ('installed', 'to upgrade'):
raise orm.except_orm(_('Error'), _("Can not upgrade module '%s'. It is not installed.") % (mod.name,))
self.check_external_dependencies(mod.name, 'to upgrade')
iids = depobj.search(cr, uid, [('name', '=', mod.name)], context=context)
for dep in depobj.browse(cr, uid, iids, context=context):
if dep.module_id.state=='installed' and dep.module_id not in todo:
if dep.module_id.state == 'installed' and dep.module_id not in todo:
todo.append(dep.module_id)
ids = map(lambda x: x.id, todo)
self.write(cr, uid, ids, {'state':'to upgrade'}, context=context)
self.write(cr, uid, ids, {'state': 'to upgrade'}, context=context)
to_install = []
for mod in todo:
@@ -517,7 +539,7 @@ class module(osv.osv):
if dep.state == 'unknown':
raise orm.except_orm(_('Error'), _('You try to upgrade a module that depends on the module: %s.\nBut this module is not available in your system.') % (dep.name,))
if dep.state == 'uninstalled':
ids2 = self.search(cr, uid, [('name','=',dep.name)])
ids2 = self.search(cr, uid, [('name', '=', dep.name)])
to_install.extend(ids2)
self.button_install(cr, uid, to_install, context=context)
@@ -550,8 +572,9 @@ class module(osv.osv):
# update the list of available packages
def update_list(self, cr, uid, context=None):
res = [0, 0] # [update, add]
res = [0, 0] # [update, add]
default_version = modules.adapt_version('1.0')
known_mods = self.browse(cr, uid, self.search(cr, uid, []))
known_mods_names = dict([(m.name, m) for m in known_mods])
@@ -570,7 +593,7 @@ class module(osv.osv):
updated_values[key] = values[key]
if terp.get('installable', True) and mod.state == 'uninstallable':
updated_values['state'] = 'uninstalled'
if parse_version(terp.get('version', '')) > parse_version(mod.latest_version or ''):
if parse_version(terp.get('version', default_version)) > parse_version(mod.latest_version or default_version):
res[0] += 1
if updated_values:
self.write(cr, uid, mod.id, updated_values)
@@ -587,24 +610,32 @@ class module(osv.osv):
self._update_dependencies(cr, uid, mod, terp.get('depends', []))
self._update_category(cr, uid, mod, terp.get('category', 'Uncategorized'))
# Trigger load_addons if new module have been discovered it exists on
# wsgi handlers, so they can react accordingly
if tuple(res) != (0, 0):
for handler in openerp.service.wsgi_server.module_handlers:
if hasattr(handler,'load_addons'):
handler.load_addons()
return res
def download(self, cr, uid, ids, download=True, context=None):
res = []
default_version = modules.adapt_version('1.0')
for mod in self.browse(cr, uid, ids, context=context):
if not mod.url:
continue
match = re.search('-([a-zA-Z0-9\._-]+)(\.zip)', mod.url, re.I)
version = '0'
version = default_version
if match:
version = match.group(1)
if parse_version(mod.installed_version or '0') >= parse_version(version):
if parse_version(mod.installed_version) >= parse_version(version):
continue
res.append(mod.url)
if not download:
continue
zip_content = urllib.urlopen(mod.url).read()
fname = modules.get_module_path(str(mod.name)+'.zip', downloaded=True)
fname = modules.get_module_path(str(mod.name) + '.zip', downloaded=True)
try:
with open(fname, 'wb') as fp:
fp.write(zip_content)
@@ -614,17 +645,77 @@ class module(osv.osv):
raise orm.except_orm(_('Error'), _('Can not create the module file:\n %s') % (fname,))
terp = self.get_module_info(mod.name)
self.write(cr, uid, mod.id, self.get_values_from_terp(terp))
cr.execute('DELETE FROM ir_module_module_dependency ' \
'WHERE module_id = %s', (mod.id,))
self._update_dependencies(cr, uid, mod, terp.get('depends',
[]))
self._update_category(cr, uid, mod, terp.get('category',
'Uncategorized'))
cr.execute('DELETE FROM ir_module_module_dependency WHERE module_id = %s', (mod.id,))
self._update_dependencies(cr, uid, mod, terp.get('depends', []))
self._update_category(cr, uid, mod, terp.get('category', 'Uncategorized'))
# Import module
zimp = zipimport.zipimporter(fname)
zimp.load_module(mod.name)
return res
def install_from_urls(self, cr, uid, urls, context=None):
OPENERP = 'openerp'
tmp = tempfile.mkdtemp()
try:
for module_name, url in urls.items():
if not url:
continue # nothing to download, local version is already the last one
try:
content = urllib2.urlopen(url).read()
except Exception, e:
_logger.exception('ggr')
raise osv.except_osv('grrr', e)
else:
zipfile.ZipFile(StringIO(content)).extractall(tmp)
assert os.path.isdir(os.path.join(tmp, module_name))
for module_name in urls:
if module_name == OPENERP:
continue # special case. handled below
module_path = modules.get_module_path(module_name, downloaded=True, display_warning=False)
bck = backup(module_path, False)
shutil.move(os.path.join(tmp, module_name), module_path)
if bck:
shutil.rmtree(bck)
if urls.get(OPENERP, None):
# special case. it containt the server and the base module.
# extract path is not the same
base_path = os.path.dirname(modules.get_module_path('base'))
# copy all modules in the SERVER/openerp/addons directory to the new "openerp" module (except base itself)
for d in os.listdir(base_path):
if d != 'base' and os.path.isdir(os.path.join(base_path, d)):
destdir = os.path.join(tmp, OPENERP, 'addons', d) # XXX 'openerp' subdirectory ?
shutil.copytree(os.path.join(base_path, d), destdir)
# then replace the server by the new "base" module
server_dir = openerp.tools.config['root_path'] # XXX or dirname()
bck = backup(server_dir)
shutil.move(os.path.join(tmp, OPENERP), server_dir)
#if bck:
# shutil.rmtree(bck)
self.update_list(cr, uid, context=context)
ids = self.search(cr, uid, [('name', 'in', urls.keys())], context=context)
if self.search_count(cr, uid, [('id', 'in', ids), ('state', '=', 'installed')], context=context):
# if any to update
cr.commit()
openerp.service.restart_server()
return {
'type': 'ir.actions.client',
'tag': 'home',
'params': {'wait': True},
}
return self.button_immediate_install(cr, uid, ids, context=context)
finally:
shutil.rmtree(tmp)
def install_by_names(self, cr, uid, names, context=None):
raise NotImplementedError('# TODO')
def _update_dependencies(self, cr, uid, mod_browse, depends=None):
if depends is None:
depends = []
@@ -679,20 +770,20 @@ class module_dependency(osv.osv):
_columns = {
# The dependency name
'name': fields.char('Name', size=128, select=True),
'name': fields.char('Name', size=128, select=True),
# The module that depends on it
'module_id': fields.many2one('ir.module.module', 'Module', select=True, ondelete='cascade'),
'state': fields.function(_state, type='selection', selection=[
('uninstallable','Uninstallable'),
('uninstalled','Not Installed'),
('installed','Installed'),
('to upgrade','To be upgraded'),
('to remove','To be removed'),
('to install','To be installed'),
('uninstallable', 'Uninstallable'),
('uninstalled', 'Not Installed'),
('installed', 'Installed'),
('to upgrade', 'To be upgraded'),
('to remove', 'To be removed'),
('to install', 'To be installed'),
('unknown', 'Unknown'),
], string='Status', readonly=True, select=True),
], string='Status', readonly=True, select=True),
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+59 -40
View File
@@ -1,9 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- Modules Categories -->
<record id="view_module_category_form" model="ir.ui.view">
<field name="name">ir.module.category.form</field>
<field name="model">ir.module.category</field>
@@ -32,7 +30,6 @@
</record>
<!-- Click on a category -->
<record id="view_module_filter" model="ir.ui.view">
<field name="name">ir.module.module.list.select</field>
<field name="model">ir.module.module</field>
@@ -43,7 +40,7 @@
<filter name="app" icon="terp-check" string="Apps" domain="[('application', '=', 1)]"/>
<filter name="extra" icon="terp-check" string="Extra" domain="[('application', '=', 0)]"/>
<separator/>
<filter icon="terp-check" string="Installed" domain="[('state', 'in', ['installed', 'to upgrade', 'to remove'])]"/>
<filter name="installed" icon="terp-check" string="Installed" domain="[('state', 'in', ['installed', 'to upgrade', 'to remove'])]"/>
<filter icon="terp-dialog-close" string="Not Installed" domain="[('state', 'in', ['uninstalled', 'uninstallable', 'to install'])]"/>
<field name="category_id"/>
<group expand="0" string="Group By...">
@@ -53,37 +50,6 @@
</search>
</field>
</record>
<record model="ir.ui.view" id="module_view_kanban">
<field name="name">Modules Kanban</field>
<field name="model">ir.module.module</field>
<field name="arch" type="xml">
<kanban create="false">
<field name="icon"/>
<field name="name"/>
<field name="state"/>
<field name="summary"/>
<templates>
<t t-name="kanban-box">
<div class="oe_module_vignette">
<t t-set="installed" t-value="record.state.raw_value == 'installed'"/>
<img t-attf-src="#{record.icon.value}" class="oe_module_icon"/>
<div class="oe_module_desc">
<h4><a type="open"><field name="shortdesc"/></a></h4>
<p class="oe_module_name">
<t t-if="record.summary.raw_value"><field name="summary"/><br/></t>
<i><field name="name" groups="base.group_no_one"/></i>
</p>
<button type="object" name="button_immediate_install" states="uninstalled">Install</button>
<button t-if="installed" disabled="disabled">Installed</button>
</div>
</div>
</t>
</templates>
</kanban>
</field>
</record>
<record id="action_module_open_categ" model="ir.actions.act_window">
<field name="name">Modules</field>
<field name="res_model">ir.module.module</field>
@@ -100,7 +66,6 @@
<!-- Modules -->
<record id="module_form" model="ir.ui.view">
<field name="name">ir.module.module.form</field>
<field name="model">ir.module.module</field>
@@ -176,20 +141,74 @@
</tree>
</field>
</record>
<record model="ir.ui.view" id="module_view_kanban">
<field name="name">Modules Kanban</field>
<field name="model">ir.module.module</field>
<field name="arch" type="xml">
<kanban create="false">
<field name="icon"/>
<field name="name"/>
<field name="state"/>
<field name="summary"/>
<templates>
<t t-name="kanban-box">
<div class="oe_module_vignette">
<t t-set="installed" t-value="record.state.raw_value == 'installed'"/>
<img t-attf-src="#{record.icon.value}" class="oe_module_icon"/>
<div class="oe_module_desc">
<h4><a type="open"><field name="shortdesc"/></a></h4>
<p class="oe_module_name">
<t t-if="record.summary.raw_value"><field name="summary"/><br/></t>
<i><field name="name" groups="base.group_no_one"/></i>
</p>
<button type="object" name="button_immediate_install" states="uninstalled">Install</button>
<button t-if="installed" disabled="disabled">Installed</button>
</div>
</div>
</t>
</templates>
</kanban>
</field>
</record>
<record id="open_module_tree" model="ir.actions.act_window">
<field name="name">Install a Module</field>
<!-- uncomment on released
<field name="name">Installed Modules</field>
-->
<field name="name">Modules</field>
<field name="res_model">ir.module.module</field>
<field name="view_type">form</field>
<field name="view_mode">kanban,tree,form</field>
<field name="context">{'search_default_app':1}</field>
<!-- uncomment on released
<field name="context">{'search_default_installed':1}</field>
-->
<field name="search_view_id" ref="view_module_filter"/>
<field name="help" type="html">
<p><b>No module found!</b></p>
<p>You should try others search criteria.</p>
</field>
</record>
<menuitem id="menu_module_tree" parent="base.menu_management" name="Modules"
sequence="1" action="open_module_tree"/>
<menuitem id="menu_module_tree" parent="menu_management" name="Installed Modules" sequence="10" action="open_module_tree"/>
<!-- Apps modules -->
<record model="ir.actions.client" id="modules_act_cl">
<field name="name">Apps</field>
<field name="tag">apps</field>
</record>
<!-- uncomment on released
<menuitem id="module_mi" parent="base.menu_management" sequence="3" action="modules_act_cl"/>
-->
<menuitem id="module_mi" parent="base.menu_management" sequence="20" action="modules_act_cl"/>
<record model="ir.actions.client" id="modules_updates_act_cl">
<field name="name">Updates</field>
<field name="tag">apps.updates</field>
<field name="params">{}</field>
</record>
<!-- uncomment on released
<menuitem id="menu_module_updates" parent="base.menu_management" sequence="7" action="modules_updates_act_cl"/>
-->
<menuitem id="menu_module_updates" parent="base.menu_management" sequence="30" action="modules_updates_act_cl"/>
</data>
</openerp>
@@ -11,7 +11,7 @@
<group states="choose" string="Export Settings">
<field name="lang"/>
<field name="format"/>
<field name="modules"/>
<field name="modules" widget="many2many_tags"/>
</group>
<div states="get">
<h2>Export Complete</h2>
@@ -43,7 +43,7 @@
id="menu_view_base_module_import"
parent="menu_management"
groups="base.group_no_one"
sequence="1"/>
sequence="6"/>
-->
</data>
@@ -18,17 +18,17 @@
</group>
<footer>
<div states="init">
<button name="update_module" string="Update" type="object" class="oe_highlight"/> or
<button name="update_module" string="Update" type="object" class="oe_highlight"/> or
<button special="cancel" string="Cancel" class="oe_link"/>
</div>
<div states="done">
<button name="action_module_open" string="Open Modules" type="object" class="oe_highlight"/> or
<button name="action_module_open" string="Open Modules" type="object" class="oe_highlight"/> or
<button special="cancel" string="Close" class="oe_link"/>
</div>
</footer>
</form>
</field>
</record>
</record>
<record id="action_view_base_module_update" model="ir.actions.act_window">
<field name="name">Module Update</field>
@@ -45,7 +45,7 @@
id="menu_view_base_module_update"
groups="base.group_no_one"
parent="menu_management"
sequence="2"
sequence="40"
icon="STOCK_CONVERT"/>
</data>
@@ -35,7 +35,7 @@
groups="base.group_no_one"
id="menu_view_base_module_upgrade"
parent="menu_management"
sequence="3"/>
sequence="50"/>
<record id="view_base_module_upgrade_install" model="ir.ui.view">
<field name="name">Module Upgrade Install</field>
+3 -2
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
##############################################################################
#
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
#
@@ -15,7 +15,7 @@
# 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/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
@@ -32,6 +32,7 @@ import res_lang
import ir_property
import report
import wizard
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+2 -2
View File
@@ -118,7 +118,7 @@ class res_company(osv.osv):
'child_ids': fields.one2many('res.company', 'parent_id', 'Child Companies'),
'partner_id': fields.many2one('res.partner', 'Partner', required=True),
'rml_header': fields.text('RML Header', required=True),
'rml_header1': fields.char('Company Slogan', size=200, help="Appears by default on the top right corner of your printed documents (report header)."),
'rml_header1': fields.char('Company Tagline', size=200, help="Appears by default on the top right corner of your printed documents (report header)."),
'rml_header2': fields.text('RML Internal Header', required=True),
'rml_header3': fields.text('RML Internal Header for Landscape Reports', required=True),
'rml_footer': fields.text('Report Footer', help="Footer text displayed at the bottom of all reports."),
@@ -330,7 +330,7 @@ class res_company(osv.osv):
<!-- left margin -->
<rotate degrees="90"/>
<fill color="grey"/>
<drawString x="2.65cm" y="-0.4cm">produced by OpenERP.com</drawString>
<drawString x="2.65cm" y="-0.4cm">generated by OpenERP.com</drawString>
<fill color="black"/>
<rotate degrees="-90"/>
@@ -32,6 +32,11 @@
<field name="name" class="oe_inline"/>
</h1>
</div>
<div attrs="{'invisible' : [('logo','!=',False)]}" class="oe_view_nocontent oe_clear">
<p class="oe_view_nocontent_create">
Click to set your company logo.
</p>
</div>
<group col="4">
<field name="parent_id" groups="base.group_multi_company"/>
</group>
+18 -10
View File
@@ -466,7 +466,8 @@ class res_config_settings(osv.osv_memory):
groups.append((name, ref(field_group), ref(field.implied_group)))
elif name.startswith('module_') and isinstance(field, fields.boolean):
mod_ids = ir_module.search(cr, uid, [('name', '=', name[7:])])
modules.append((name, ir_module.browse(cr, uid, mod_ids[0], context)))
record = ir_module.browse(cr, uid, mod_ids[0], context) if mod_ids else None
modules.append((name, record))
else:
others.append(name)
@@ -490,7 +491,7 @@ class res_config_settings(osv.osv_memory):
# modules: which modules are installed/to install
for name, module in classified['module']:
res[name] = module.state in ('installed', 'to install', 'to upgrade')
res[name] = module and module.state in ('installed', 'to install', 'to upgrade')
# other fields: call all methods that start with 'get_default_'
for method in dir(self):
@@ -501,9 +502,7 @@ class res_config_settings(osv.osv_memory):
def execute(self, cr, uid, ids, context=None):
ir_values = self.pool.get('ir.values')
ir_model_data = self.pool.get('ir.model.data')
ir_module = self.pool.get('ir.module.module')
res_groups = self.pool.get('res.groups')
classified = self._get_classified_fields(cr, uid, context)
config = self.browse(cr, uid, ids[0], context)
@@ -526,17 +525,26 @@ class res_config_settings(osv.osv_memory):
getattr(self, method)(cr, uid, ids, context)
# module fields: install/uninstall the selected modules
to_install_ids = []
to_install_names = []
to_uninstall_ids = []
lm = len('module_')
for name, module in classified['module']:
if config[name]:
if module.state == 'uninstalled': to_install_ids.append(module.id)
if not module or module.state == 'uninstalled':
to_install_names.append(name[lm:])
else:
if module.state in ('installed','upgrade'): to_uninstall_ids.append(module.id)
if module and module.state in ('installed', 'to upgrade'):
to_uninstall_ids.append(module.id)
if to_install_ids or to_uninstall_ids:
ir_module.button_uninstall(cr, uid, to_uninstall_ids, context=context)
ir_module.button_immediate_install(cr, uid, to_install_ids, context=context)
if to_uninstall_ids:
ir_module.button_immediate_uninstall(cr, uid, to_uninstall_ids, context=context)
if to_install_names:
return {
'type': 'ir.actions.client',
'tag': 'apps',
'params': {'modules': to_install_names},
}
config = self.pool.get('res.config').next(cr, uid, [], context=context) or {}
if config.get('type') not in ('ir.actions.act_window_close',):
+23 -10
View File
@@ -187,6 +187,12 @@ class res_partner(osv.osv, format_address):
def _set_image(self, cr, uid, id, name, value, args, context=None):
return self.write(cr, uid, [id], {'image': tools.image_resize_image_big(value)}, context=context)
def _has_image(self, cr, uid, ids, name, args, context=None):
result = {}
for obj in self.browse(cr, uid, ids, context=context):
result[obj.id] = obj.image != False
return result
_order = "name"
_columns = {
'name': fields.char('Name', size=128, required=True, select=True),
@@ -253,6 +259,7 @@ class res_partner(osv.osv, format_address):
help="Small-sized image of this contact. It is automatically "\
"resized as a 64x64px image, with aspect ratio preserved. "\
"Use this field anywhere a small image is required."),
'has_image': fields.function(_has_image, type="boolean"),
'company_id': fields.many2one('res.company', 'Company', select=1),
'color': fields.integer('Color Index'),
'user_ids': fields.one2many('res.users', 'partner_id', 'Users'),
@@ -297,7 +304,7 @@ class res_partner(osv.osv, format_address):
'is_company': False,
'type': 'default',
'use_parent_address': True,
'image': lambda self, cr, uid, ctx: self._get_default_image(cr, uid, ctx.get('default_is_company', False), ctx),
'image': False,
}
def copy(self, cr, uid, id, default=None, context=None):
@@ -308,8 +315,7 @@ class res_partner(osv.osv, format_address):
return super(res_partner, self).copy(cr, uid, id, default, context)
def onchange_type(self, cr, uid, ids, is_company, context=None):
# get value as for an onchange on the image
value = tools.image_get_resized_images(self._get_default_image(cr, uid, is_company, context), return_big=True, return_medium=False, return_small=False)
value = {}
value['title'] = False
if is_company:
value['parent_id'] = False
@@ -402,6 +408,8 @@ class res_partner(osv.osv, format_address):
name = name + "\n" + self._display_address(cr, uid, record, without_company=True, context=context)
name = name.replace('\n\n','\n')
name = name.replace('\n\n','\n')
if context.get('show_email') and record.email:
name = "%s <%s>" % (name, record.email)
res.append((record.id, name))
return res
@@ -437,18 +445,23 @@ class res_partner(osv.osv, format_address):
def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=100):
if not args:
args = []
if name and operator in ('=', 'ilike', '=ilike', 'like'):
if name and operator in ('=', 'ilike', '=ilike', 'like', '=like'):
# search on the name of the contacts and of its company
name2 = operator == '=' and name or '%' + name + '%'
search_name = name
if operator in ('ilike', 'like'):
search_name = '%%%s%%' % name
if operator in ('=ilike', '=like'):
operator = operator[1:]
query_args = {'name': search_name}
limit_str = ''
query_args = [name2]
if limit:
limit_str = ' limit %s'
query_args += [limit]
limit_str = ' limit %(limit)s'
query_args['limit'] = limit
cr.execute('''SELECT partner.id FROM res_partner partner
LEFT JOIN res_partner company ON partner.parent_id = company.id
WHERE partner.name || ' (' || COALESCE(company.name,'') || ')'
''' + operator + ''' %s ''' + limit_str, query_args)
WHERE partner.email ''' + operator +''' %(name)s
OR partner.name || ' (' || COALESCE(company.name,'') || ')'
''' + operator + ' %(name)s ' + limit_str, query_args)
ids = map(lambda x: x[0], cr.fetchall())
if args:
ids = self.search(cr, uid, [('id', 'in', ids)] + args, limit=limit, context=context)
+29 -5
View File
@@ -196,12 +196,13 @@
<field name="phone"/>
<field name="street"/>
<field name="street2"/>
<field name="image"/>
<field name="zip"/>
<field name="city"/>
<field name="country_id"/>
<field name="mobile"/>
<field name="fax"/>
<field name="state_id"/>
<field name="has_image"/>
<templates>
<t t-name="kanban-box">
<t t-set="color" t-value="kanban_color(record.color.raw_value)"/>
@@ -209,7 +210,17 @@
<a t-if="! read_only_mode" type="delete" style="position: absolute; right: 0; padding: 4px; diplay: inline-block">X</a>
<div class="oe_module_vignette">
<a type="open">
<img t-att-src="kanban_image('res.partner', 'image', record.id.value, {'preview_image': 'image_small'})" class="oe_avatar oe_kanban_avatar_smallbox"/>
<t t-if="record.has_image.raw_value === true">
<img t-att-src="kanban_image('res.partner', 'image', record.id.value, {'preview_image': 'image_small'})" class="oe_avatar oe_kanban_avatar_smallbox"/>
</t>
<t t-if="record.has_image.raw_value === false">
<t t-if="record.is_company.raw_value === true">
<img t-att-src='_s + "/base/static/src/img/company_image.png"' class="oe_kanban_image"/>
</t>
<t t-if="record.is_company.raw_value === false">
<img t-att-src='_s + "/base/static/src/img/avatar.png"' class="oe_kanban_image"/>
</t>
</t>
</a>
<div class="oe_module_desc">
<div class="oe_kanban_box_content oe_kanban_color_bglight oe_kanban_color_border">
@@ -222,6 +233,9 @@
<div><a t-if="record.email.raw_value" title="Mail" t-att-href="'mailto:'+record.email.value">
<field name="email"/>
</a></div>
<div t-if="record.phone.raw_value">Phone: <field name="phone"/></div>
<div t-if="record.mobile.raw_value">Mobile: <field name="mobile"/></div>
<div t-if="record.fax.raw_value">Fax: <field name="fax"/></div>
</td>
</tr>
</table>
@@ -285,8 +299,7 @@
<field name="arch" type="xml">
<search string="Search Partner">
<field name="name"
filter_domain="['|','|',('name','ilike',self),('parent_id','ilike',self),('ref','=',self)]"
string="Partner"/>
filter_domain="['|','|',('name','ilike',self),('parent_id','ilike',self),('ref','=',self)]"/>
<filter help="My Partners" icon="terp-personal+" domain="[('user_id','=',uid)]"/>
<separator/>
<filter string="Persons" name="type_person" icon="terp-personal" domain="[('is_company','=',0)]"/>
@@ -328,11 +341,22 @@
<field name="mobile"/>
<field name="state_id"/>
<field name="category_id"/>
<field name="has_image"/>
<templates>
<t t-name="kanban-box">
<div class="oe_kanban_vignette oe_semantic_html_override">
<a type="open">
<img t-att-src="kanban_image('res.partner', 'image_small', record.id.value)" class="oe_kanban_image"/>
<t t-if="record.has_image.raw_value === true">
<img t-att-src="kanban_image('res.partner', 'image_small', record.id.value)" class="oe_kanban_image"/>
</t>
<t t-if="record.has_image.raw_value === false">
<t t-if="record.is_company.raw_value === true">
<img t-att-src='_s + "/base/static/src/img/company_image.png"' class="oe_kanban_image"/>
</t>
<t t-if="record.is_company.raw_value === false">
<img t-att-src='_s + "/base/static/src/img/avatar.png"' class="oe_kanban_image"/>
</t>
</t>
</a>
<div class="oe_kanban_details">
<h4 class="oe_partner_heading"><a type="open"><field name="name"/></a></h4>
+4 -1
View File
@@ -136,6 +136,10 @@
<form string="Users" version="7.0">
<field name="id" invisible="1"/>
<sheet>
<div class="oe_right oe_button_box">
<button string="Change Password" type="action" name="%(change_password_wizard_action)d"
help="Change the user password."/>
</div>
<field name="image" widget='image' class="oe_avatar oe_left" options='{"preview_image": "image_medium"}'/>
<div class="oe_title">
<label for="name" class="oe_edit_only"/>
@@ -145,7 +149,6 @@
</div>
<group>
<group>
<field name="new_password" password="True" attrs="{'required': [('id','=',False)]}"/>
<field name="company_id" context="{'user_preference': 0}"/>
</group>
<group>
@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2011 OpenERP S.A (<http://www.openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import change_password_wizard
@@ -0,0 +1,81 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2011 OpenERP S.A (<http://www.openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import fields, osv
class change_password_wizard(osv.TransientModel):
"""
A wizard to manage the change of users' passwords
"""
_name = "change.password.wizard"
_description = "Change Password Wizard"
_columns = {
'user_ids': fields.one2many('change.password.user', 'wizard_id', string='Users'),
}
def default_get(self, cr, uid, fields, context=None):
if context == None:
context = {}
user_ids = context.get('active_ids', [])
wiz_id = context.get('active_id', None)
res = []
users = self.pool.get('res.users').browse(cr, uid, user_ids, context=context)
for user in users:
res.append((0, 0, {
'wizard_id': wiz_id,
'user_id': user.id,
'user_login': user.login,
}))
return {'user_ids': res}
def change_password_button(self, cr, uid, id, context=None):
wizard = self.browse(cr, uid, id, context=context)[0]
user_ids = []
for user in wizard.user_ids:
user_ids.append(user.id)
self.pool.get('change.password.user').change_password_button(cr, uid, user_ids, context=context)
return {
'type': 'ir.actions.act_window_close',
}
class change_password_user(osv.TransientModel):
"""
A model to configure users in the change password wizard
"""
_name = 'change.password.user'
_description = 'Change Password Wizard User'
_columns = {
'wizard_id': fields.many2one('change.password.wizard', string='Wizard', required=True),
'user_id': fields.many2one('res.users', string='User', required=True),
'user_login': fields.char('User Login', readonly=True),
'new_passwd': fields.char('New Password'),
}
_defaults = {
'new_passwd': '',
}
def change_password_button(self, cr, uid, ids, context=None):
for user in self.browse(cr, uid, ids, context=context):
self.pool.get('res.users').write(cr, uid, user.user_id.id, {'password': user.new_passwd})
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- wizard action on res.users -->
<act_window id="change_password_wizard_action"
name="Change Password"
src_model="res.users"
res_model="change.password.wizard"
view_type="form" view_mode="form"
key2="client_action_multi" target="new"
groups="base.group_erp_manager"/>
<!-- wizard view -->
<record id="change_password_wizard_view" model="ir.ui.view">
<field name="name">Change Password</field>
<field name="model">change.password.wizard</field>
<field name="arch" type="xml">
<form string="Change Password" version="7.0">
<field name="user_ids"/>
<footer>
<button string="Change Password" name="change_password_button" type="object" class="oe_highlight"/>
or
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
<!-- wizard user list view -->
<record id="change_password_wizard_user_tree_view" model="ir.ui.view">
<field name="name">Change Password Users</field>
<field name="model">change.password.user</field>
<field name="arch" type="xml">
<!-- the user list is editable, but one cannot add or delete rows -->
<tree string="Users" editable="bottom" create="false" delete="false">
<field name="user_login"/>
<field name="new_passwd" required="True"/>
</tree>
</field>
</record>
</data>
</openerp>
+117
View File
@@ -0,0 +1,117 @@
openerp.base = function(instance) {
instance.base.apps_remote = null;
instance.base.apps_client = null;
instance.base.Apps = instance.web.Widget.extend({
template: 'EmptyComponent',
remote_action_id: 'loempia.action_embed',
failback_action_id: 'base.open_module_tree',
init: function(parent, action) {
this._super(parent, action);
var options = action.params || {};
if (options.apps_user) {
sessionStorage.setItem('apps.login', options.apps_user);
}
if (options.apps_access_token) {
sessionStorage.setItem('apps.access_token', options.apps_access_token);
}
this.params = options; // NOTE read by embedded client action
},
get_client: function() {
// return the client via a deferred, resolved or rejected depending if the remote host is available or not.
var check_client_available = function(client) {
var d = $.Deferred();
var i = new Image();
i.onerror = function() {
d.reject(client);
};
i.onload = function() {
client.session.session_bind(client.origin).then(function() {
// check if client can authenticate
client.authenticate().then(
function() { /* done */
d.resolve(client);
}, function() { /* fail */
if (client.login === 'anonymous') {
d.reject(client);
} else {
sessionStorage.removeItem('apps.login');
sessionStorage.removeItem('apps.access_token');
client.bind_crendentials(client.dbname, 'anonymous', 'anonymous');
client.authenticate().then(
function() { /* done */
d.resolve(client);
}, function() { /* fail */
d.reject(client);
});
}
});
});
};
i.src = _.str.sprintf('%s/web/static/src/img/sep-a.gif', client.origin);
return d.promise();
};
if (instance.base.apps_client) {
return check_client_available(instance.base.apps_client);
} else {
var ICP = new instance.web.Model('ir.config_parameter');
return ICP.call('get_param', ['apps.server', 'https://apps.openerp.com/apps']).then(function(u) {
var link = $(_.str.sprintf('<a href="%s"></a>', u))[0];
var host = _.str.sprintf('%s//%s', link.protocol, link.host);
var dbname = link.pathname;
if (dbname[0] === '/') {
dbname = dbname.substr(1);
}
var login = (sessionStorage ? sessionStorage.getItem('apps.login') : null) || 'anonymous';
var passwd = (sessionStorage ? sessionStorage.getItem('apps.access_token') : null) || 'anonymous';
if (_.isNull(instance.base.apps_remote)) {
instance.base.apps_remote = new openerp.init();
}
var client = new instance.base.apps_remote.web.EmbeddedClient(null, host, dbname, login, passwd);
instance.base.apps_client = client;
return check_client_available(client);
});
}
},
destroy: function() {
if (instance.base.apps_client) {
instance.base.apps_client.destroy();
}
return this._super();
},
start: function() {
var self = this;
return self.get_client().
done(function(client) {
client.replace(self.$el).
done(function() {
client.$el.removeClass('openerp');
client.do_action(self.remote_action_id);
});
}).
fail(function(client) {
self.do_warn('Apps Server not reachable.', 'Showing local modules.', true);
self.rpc('/web/action/load', {action_id: self.failback_action_id}).done(function(action) {
self.do_action(action);
instance.webclient.menu.open_action(action.id);
});
});
},
});
instance.base.AppsUpdates = instance.base.Apps.extend({
remote_action_id: 'loempia.action_embed_updates'
});
instance.web.client_actions.add("apps", "instance.base.Apps");
instance.web.client_actions.add("apps.updates", "instance.base.AppsUpdates");
};
-14
View File
@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!--
Assert:
-->
<assert model="res.request" id="res_partner_asustek_request" string="Reference field request returns a browsable object">
<test expr="ref_doc1.name">ASUSTeK</test>
</assert>
</data>
</openerp>
-116
View File
@@ -1,116 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2011-TODAY OpenERP S.A. <http://www.openerp.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import time
from datetime import datetime
from dateutil.relativedelta import relativedelta
import openerp
JOB = {
'function': u'_0_seconds',
'interval_type': u'minutes',
'user_id': 1,
'name': u'test',
'args': False,
'numbercall': 1,
'nextcall': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
'priority': 5,
'doall': True,
'active': True,
'interval_number': 1,
'model': u'ir.cron'
}
class test_ir_cron(openerp.osv.osv.osv):
""" Add a few handy methods to test cron jobs scheduling. """
_inherit = "ir.cron"
def _0_seconds(a, b, c):
print ">>> _0_seconds"
def _20_seconds(self, cr, uid):
print ">>> in _20_seconds"
time.sleep(20)
print ">>> out _20_seconds"
def _80_seconds(self, cr, uid):
print ">>> in _80_seconds"
time.sleep(80)
print ">>> out _80_seconds"
def test_0(self, cr, uid):
now = datetime.now()
t1 = (now + relativedelta(minutes=1)).strftime('%Y-%m-%d %H:%M:%S')
t2 = (now + relativedelta(minutes=1, seconds=5)).strftime('%Y-%m-%d %H:%M:%S')
t3 = (now + relativedelta(minutes=1, seconds=10)).strftime('%Y-%m-%d %H:%M:%S')
self.create(cr, uid, dict(JOB, name='test_0 _20_seconds A', function='_20_seconds', nextcall=t1))
self.create(cr, uid, dict(JOB, name='test_0 _20_seconds B', function='_20_seconds', nextcall=t2))
self.create(cr, uid, dict(JOB, name='test_0 _20_seconds C', function='_20_seconds', nextcall=t3))
def test_1(self, cr, uid):
now = datetime.now()
t1 = (now + relativedelta(minutes=1)).strftime('%Y-%m-%d %H:%M:%S')
self.create(cr, uid, dict(JOB, name='test_1 _20_seconds * 3', function='_20_seconds', nextcall=t1, numbercall=3))
def test_2(self, cr, uid):
now = datetime.now()
t1 = (now + relativedelta(minutes=1)).strftime('%Y-%m-%d %H:%M:%S')
self.create(cr, uid, dict(JOB, name='test_2 _80_seconds * 2', function='_80_seconds', nextcall=t1, numbercall=2))
def test_3(self, cr, uid):
now = datetime.now()
t1 = (now + relativedelta(minutes=1)).strftime('%Y-%m-%d %H:%M:%S')
t2 = (now + relativedelta(minutes=1, seconds=5)).strftime('%Y-%m-%d %H:%M:%S')
t3 = (now + relativedelta(minutes=1, seconds=10)).strftime('%Y-%m-%d %H:%M:%S')
self.create(cr, uid, dict(JOB, name='test_3 _80_seconds A', function='_80_seconds', nextcall=t1))
self.create(cr, uid, dict(JOB, name='test_3 _20_seconds B', function='_20_seconds', nextcall=t2))
self.create(cr, uid, dict(JOB, name='test_3 _20_seconds C', function='_20_seconds', nextcall=t3))
# This test assumes 4 cron threads.
def test_00(self, cr, uid):
self.test_00_set = set()
now = datetime.now()
t1 = (now + relativedelta(minutes=1)).strftime('%Y-%m-%d %H:%M:%S')
t2 = (now + relativedelta(minutes=1, seconds=5)).strftime('%Y-%m-%d %H:%M:%S')
t3 = (now + relativedelta(minutes=1, seconds=10)).strftime('%Y-%m-%d %H:%M:%S')
self.create(cr, uid, dict(JOB, name='test_00 _20_seconds_A', function='_20_seconds_A', nextcall=t1))
self.create(cr, uid, dict(JOB, name='test_00 _20_seconds_B', function='_20_seconds_B', nextcall=t2))
self.create(cr, uid, dict(JOB, name='test_00 _20_seconds_C', function='_20_seconds_C', nextcall=t3))
def _expect(self, cr, uid, to_add, to_sleep, to_expect_in, to_expect_out):
assert self.test_00_set == to_expect_in
self.test_00_set.add(to_add)
time.sleep(to_sleep)
self.test_00_set.discard(to_add)
assert self.test_00_set == to_expect_out
def _20_seconds_A(self, cr, uid):
self._expect(cr, uid, 'A', 20, set(), set(['B', 'C']))
def _20_seconds_B(self, cr, uid):
self._expect(cr, uid, 'B', 20, set('A'), set('C'))
def _20_seconds_C(self, cr, uid):
self._expect(cr, uid, 'C', 20, set(['A', 'B']), set())
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
-61
View File
@@ -1,61 +0,0 @@
-
Test the cron jobs scheduling.
-
Disable the existing cron jobs if any during the tests.
-
!python {model: ir.cron }: |
# For this test to work, as it involves multiple database cursors,
# we have to commit changes. But YAML tests must be rollbacked, so
# the final database state is left untouched. So we have to be a bit
# ugly here: use our own cursor, commit, and clean after ourselves.
# We also pass around some ids using setattr/delattr, and we have to
# rollback the previous tests otherwise we won't be able to touch the
# db.
# Well, this should probably be a standalone, or regular unit test,
# instead of using the YAML infrastructure.
cr.rollback()
our_cr = self.pool.db.cursor()
try:
ids = self.search(our_cr, uid, [], {})
setattr(self, 'saved_ids', ids)
self.write(our_cr, uid, ids, {'active': False}, {})
our_cr.commit()
finally:
our_cr.close()
-
Three concurrent jobs started with a slight time gap. Assume 4 cron threads.
This will take about 2 minutes.
-
!python {model: ir.cron }: |
# Pretend initialization is already done. We the use a try/finally
# to reset _init correctly.
self.pool._init = False
our_cr = self.pool.db.cursor()
try:
self.test_00(our_cr, uid) # this will commit using the passed cursor
import openerp.cron
openerp.cron._thread_slots = 4
# Wake up this db as soon as the master cron thread starts.
openerp.cron.schedule_wakeup(1, self.pool.db.dbname)
# Pretend to be the master thread, for 4 iterations.
openerp.cron.runner_body()
openerp.cron.runner_body()
openerp.cron.runner_body()
openerp.cron.runner_body()
finally:
self.pool._init = True
our_cr.close()
-
Clean after ourselves.
-
!python {model: ir.cron }: |
our_cr = self.pool.db.cursor()
try:
ids = [x for x in self.search(our_cr, uid, ['|', ('active', '=', True), ('active', '=', False)], {}) if x not in self.saved_ids]
self.unlink(our_cr, uid, ids, {})
ids = self.saved_ids
delattr(self, 'saved_ids')
self.write(our_cr, uid, ids, {'active': True}, {})
our_cr.commit()
finally:
our_cr.close()
+8 -2
View File
@@ -1,9 +1,15 @@
import test_base, test_expression, test_search, test_ir_values, test_menu
import test_base
import test_expression
import test_ir_attachment
import test_ir_values
import test_menu
import test_search
checks = [
test_base,
test_expression,
test_search,
test_ir_attachment,
test_ir_values,
test_menu,
test_search,
]
@@ -0,0 +1,89 @@
import hashlib
import os
import unittest2
import openerp
import openerp.tests.common
class test_ir_attachment(openerp.tests.common.TransactionCase):
def test_00_attachment_flow(self):
registry, cr, uid = self.registry, self.cr, self.uid
root_path = openerp.tools.config['root_path']
ira = registry('ir.attachment')
# Blob1
blob1 = 'blob1'
blob1_b64 = blob1.encode('base64')
blob1_hash = hashlib.sha1(blob1).hexdigest()
blob1_fname = blob1_hash[:3] + '/' + blob1_hash
# Blob2
blob2 = 'blob2'
blob2_b64 = blob2.encode('base64')
blob2_hash = hashlib.sha1(blob2).hexdigest()
blob2_fname = blob2_hash[:3] + '/' + blob2_hash
# 'ir_attachment.location' is undefined test database storage
a1 = ira.create(cr, uid, {'name': 'a1', 'datas': blob1_b64})
a1_read = ira.read(cr, uid, [a1], ['datas'])
self.assertEqual(a1_read[0]['datas'], blob1_b64)
cr.execute("select id,db_datas from ir_attachment where id = %s", (a1,) )
a1_db_datas = str(cr.fetchall()[0][1])
self.assertEqual(a1_db_datas, blob1_b64)
# define a location for filestore
registry('ir.config_parameter').set_param(cr, uid, 'ir_attachment.location', 'file:///filestore')
# Test file storage
a2 = ira.create(cr, uid, {'name': 'a2', 'datas': blob1_b64})
a2_read = ira.read(cr, uid, [a2], ['datas'])
self.assertEqual(a2_read[0]['datas'], blob1_b64)
cr.execute("select id,store_fname from ir_attachment where id = %s", (a2,) )
a2_store_fname = cr.fetchall()[0][1]
self.assertEqual(a2_store_fname, blob1_fname)
a2_fn = os.path.join(root_path, 'filestore', cr.dbname, blob1_hash[:3], blob1_hash)
fc = file(a2_fn).read()
self.assertEqual(fc, blob1)
# create a3 with same blob
a3 = ira.create(cr, uid, {'name': 'a3', 'datas': blob1_b64})
a3_read = ira.read(cr, uid, [a3], ['datas'])
self.assertEqual(a3_read[0]['datas'], blob1_b64)
cr.execute("select id,store_fname from ir_attachment where id = %s", (a3,) )
a3_store_fname = cr.fetchall()[0][1]
self.assertEqual(a3_store_fname, a2_store_fname)
# create a4 blob2
a4 = ira.create(cr, uid, {'name': 'a4', 'datas': blob2_b64})
a4_read = ira.read(cr, uid, [a4], ['datas'])
self.assertEqual(a4_read[0]['datas'], blob2_b64)
a4_fn = os.path.join(root_path, 'filestore', cr.dbname, blob2_hash[:3], blob2_hash)
self.assertTrue(os.path.isfile(a4_fn))
# delete a3 but file stays
ira.unlink(cr, uid, [a3])
self.assertTrue(os.path.isfile(a2_fn))
# delete a2 it is unlinked
ira.unlink(cr, uid, [a2])
self.assertFalse(os.path.isfile(a2_fn))
# update a4 blob2 by blob1
ira.write(cr, uid, [a4], {'datas': blob1_b64})
a4_read = ira.read(cr, uid, [a4], ['datas'])
self.assertEqual(a4_read[0]['datas'], blob1_b64)
# file of a4 disapear and a2 reappear
self.assertFalse(os.path.isfile(a4_fn))
self.assertTrue(os.path.isfile(a2_fn))
# everybody applause
+9 -4
View File
@@ -95,7 +95,7 @@ def preload_registry(dbname):
""" Preload a registry, and start the cron."""
try:
update_module = True if openerp.tools.config['init'] or openerp.tools.config['update'] else False
db, registry = openerp.pooler.get_db_and_pool(dbname, update_module=update_module, pooljobs=False)
db, registry = openerp.pooler.get_db_and_pool(dbname,update_module=update_module)
except Exception:
_logger.exception('Failed to initialize database `%s`.', dbname)
@@ -103,7 +103,7 @@ def run_test_file(dbname, test_file):
""" Preload a registry, possibly run a test file, and start the cron."""
try:
config = openerp.tools.config
db, registry = openerp.pooler.get_db_and_pool(dbname, update_module=config['init'] or config['update'], pooljobs=False)
db, registry = openerp.pooler.get_db_and_pool(dbname, update_module=config['init'] or config['update'])
cr = db.cursor()
_logger.info('loading test file %s', test_file)
openerp.tools.convert_yaml_import(cr, 'base', file(test_file), 'test', {}, 'test', True)
@@ -207,10 +207,15 @@ def quit_on_signals():
pass
config = openerp.tools.config
openerp.service.stop_services()
if getattr(openerp, 'phoenix', False):
# like the phoenix, reborn from ashes...
openerp.service._reexec()
return
if config['pidfile']:
os.unlink(config['pidfile'])
openerp.service.stop_services()
sys.exit(0)
def configure_babel_localedata_path():
+3 -2
View File
@@ -3,7 +3,7 @@
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
# Copyright (C) 2010-2011 OpenERP s.a. (<http://openerp.com>).
# Copyright (C) 2010-2012 OpenERP s.a. (<http://openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@@ -32,7 +32,8 @@ from openerp.modules.module import \
load_information_from_description_file, \
get_module_resource, zip_directory, \
get_module_path, initialize_sys_path, \
load_openerp_module, init_module_models
load_openerp_module, init_module_models, \
adapt_version
from openerp.modules.loading import load_modules
+13 -5
View File
@@ -3,7 +3,7 @@
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
# Copyright (C) 2010-2011 OpenERP s.a. (<http://openerp.com>).
# Copyright (C) 2010-2012 OpenERP s.a. (<http://openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@@ -329,7 +329,7 @@ def load_information_from_description_file(module):
'license': 'AGPL-3',
'name': False,
'post_load': None,
'version': '0.0.0',
'version': '1.0',
'web': False,
'website': '',
'sequence': 100,
@@ -349,6 +349,7 @@ def load_information_from_description_file(module):
# 'active' has been renamed 'auto_install'
info['auto_install'] = info['active']
info['version'] = adapt_version(info['version'])
return info
#TODO: refactor the logger in this file to follow the logging guidelines
@@ -447,15 +448,22 @@ def get_modules():
def get_modules_with_version():
modules = get_modules()
res = {}
res = dict.fromkeys(modules, adapt_version('1.0'))
for module in modules:
try:
info = load_information_from_description_file(module)
res[module] = "%s.%s" % (release.major_version, info['version'])
except Exception, e:
res[module] = info['version']
except Exception:
continue
return res
def adapt_version(version):
serie = release.major_version
if version == serie or not version.startswith(serie + '.'):
version = '%s.%s' % (serie, version)
return version
def get_test_modules(module, submodule, explode):
"""
Return a list of submodules containing tests.
+10 -22
View File
@@ -58,8 +58,8 @@ 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
# Indicates that the registry is
self.ready = False
# Inter-process signaling (used only when openerp.multi_process is True):
# The `base_registry_signaling` sequence indicates the whole registry
@@ -121,15 +121,6 @@ class Registry(object):
models_to_load.append(model._name)
return [self.models[m] for m in models_to_load]
def schedule_cron_jobs(self):
""" Make the cron thread care about this registry/database jobs.
This will initiate the cron thread to check for any pending jobs for
this registry/database as soon as possible. Then it will continuously
monitor the ir.cron model for future jobs. See openerp.cron for
details.
"""
self.cron = True
def clear_caches(self):
""" Clear the caches
This clears the caches associated to methods decorated with
@@ -192,18 +183,17 @@ class RegistryManager(object):
registries_lock = threading.RLock()
@classmethod
def get(cls, db_name, force_demo=False, status=None, update_module=False,
pooljobs=True):
def get(cls, db_name, force_demo=False, status=None, update_module=False):
""" Return a registry for a given database name."""
try:
return cls.registries[db_name]
except KeyError:
return cls.new(db_name, force_demo, status,
update_module, pooljobs)
update_module)
@classmethod
def new(cls, db_name, force_demo=False, status=None,
update_module=False, pooljobs=True):
update_module=False):
""" Create and return a new registry for a given database name.
The (possibly) previous registry for that database name is discarded.
@@ -235,8 +225,7 @@ class RegistryManager(object):
finally:
cr.close()
if pooljobs:
registry.schedule_cron_jobs()
registry.ready = True
return registry
@@ -273,7 +262,7 @@ class RegistryManager(object):
@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)
registry = cls.get(db_name)
cr = registry.db.cursor()
try:
cr.execute("""
@@ -285,8 +274,7 @@ class RegistryManager(object):
# 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 = cls.new(db_name)
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
@@ -311,7 +299,7 @@ class RegistryManager(object):
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)
registry = cls.get(db_name)
if registry.any_cache_cleared():
_logger.info("At least one model cache has been cleared, signaling through the database.")
cr = registry.db.cursor()
@@ -327,7 +315,7 @@ class RegistryManager(object):
@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)
registry = cls.get(db_name)
cr = registry.db.cursor()
r = 1
try:
+9 -1
View File
@@ -4857,7 +4857,15 @@ class BaseModel(object):
return res[0][0]
cr.execute('SELECT "%s".id FROM ' % self._table + from_clause + where_str + order_by + limit_str + offset_str, where_clause_params)
res = cr.fetchall()
return [x[0] for x in res]
# TDE note: with auto_join, we could have several lines about the same result
# i.e. a lead with several unread messages; we uniquify the result using
# a fast way to do it while preserving order (http://www.peterbe.com/plog/uniqifiers-benchmark)
def _uniquify_list(seq):
seen = set()
return [x for x in seq if x not in seen and not seen.add(x)]
return _uniquify_list([x[0] for x in res])
# returns the different values ever entered for one field
# this is used, for example, in the client when the user hits enter on
+3 -4
View File
@@ -28,18 +28,17 @@
from openerp.modules.registry import RegistryManager
def get_db_and_pool(db_name, force_demo=False, status=None, update_module=False, pooljobs=True):
def get_db_and_pool(db_name, force_demo=False, status=None, update_module=False):
"""Create and return a database connection and a newly initialized registry."""
registry = RegistryManager.get(db_name, force_demo, status, update_module, pooljobs)
registry = RegistryManager.get(db_name, force_demo, status, update_module)
return registry.db, registry
def restart_pool(db_name, force_demo=False, status=None, update_module=False):
"""Delete an existing registry and return a database connection and a newly initialized registry."""
registry = RegistryManager.new(db_name, force_demo, status, update_module, True)
registry = RegistryManager.new(db_name, force_demo, status, update_module)
return registry.db, registry
def get_db(db_name):
"""Return a database connection. The corresponding registry is initialized."""
return get_db_and_pool(db_name)[0]
+2
View File
@@ -50,4 +50,6 @@ author = 'OpenERP S.A.'
author_email = 'info@openerp.com'
license = 'AGPL-3'
nt_service_name = "openerp-server-" + serie
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+1 -1
View File
@@ -493,7 +493,7 @@ class _rml_canvas(object):
img = ImageReader(s)
(sx,sy) = img.getSize()
_logger.debug("Image is %dx%d", sx, sy)
args = { 'x': 0.0, 'y': 0.0 }
args = { 'x': 0.0, 'y': 0.0, 'mask': 'auto'}
for tag in ('width','height','x','y'):
if node.get(tag):
args[tag] = utils.unit_get(node.get(tag))
+38 -2
View File
@@ -21,6 +21,10 @@
##############################################################################
import logging
import os
import signal
import subprocess
import sys
import threading
import time
@@ -33,6 +37,7 @@ import wsgi_server
import openerp.modules
import openerp.netsvc
import openerp.osv
from openerp.release import nt_service_name
import openerp.tools
#.apidoc title: RPC Services
@@ -97,27 +102,58 @@ def stop_services():
_logger.info("Initiating shutdown")
_logger.info("Hit CTRL-C again or send a second signal to force the shutdown.")
logging.shutdown()
# Manually join() all threads before calling sys.exit() to allow a second signal
# to trigger _force_quit() in case some non-daemon threads won't exit cleanly.
# threading.Thread.join() should not mask signals (at least in python 2.5).
me = threading.currentThread()
_logger.debug('current thread: %r', me)
for thread in threading.enumerate():
_logger.debug('process %r (%r)', thread, thread.isDaemon())
if thread != me and not thread.isDaemon() and thread.ident != main_thread_id:
while thread.isAlive():
_logger.debug('join and sleep')
# Need a busyloop here as thread.join() masks signals
# and would prevent the forced shutdown.
thread.join(0.05)
time.sleep(0.05)
_logger.debug('--')
openerp.modules.registry.RegistryManager.delete_all()
logging.shutdown()
def start_services_workers():
import openerp.service.workers
openerp.multi_process = True
openerp.service.workers.Multicorn(openerp.service.wsgi_server.application).run()
def _reexec():
"""reexecute openerp-server process with (nearly) the same arguments"""
if openerp.tools.osutil.is_running_as_nt_service():
subprocess.call('sc stop {0} && sc start {0}'.format(nt_service_name), shell=True)
exe = os.path.basename(sys.executable)
strip_args = ['-d', '-u']
a = sys.argv[:]
args = [x for i, x in enumerate(a) if x not in strip_args and a[max(i - 1, 0)] not in strip_args]
if not args or args[0] != exe:
args.insert(0, exe)
os.execv(sys.executable, args)
def restart_server():
if openerp.multi_process:
raise NotImplementedError("Multicorn is not supported (but gunicorn was)")
pid = openerp.wsgi.core.arbiter_pid
os.kill(pid, signal.SIGHUP)
else:
if os.name == 'nt':
def reborn():
stop_services()
_reexec()
# run in a thread to let the current thread return response to the caller.
threading.Thread(target=reborn).start()
else:
openerp.phoenix = True
os.kill(os.getpid(), signal.SIGINT)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+1 -1
View File
@@ -43,7 +43,7 @@ def cron_runner(number):
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:
while True and registry.ready:
# acquired = openerp.addons.base.ir.ir_cron.ir_cron._acquire_job(db_name)
# TODO why isnt openerp.addons.base defined ?
import sys
+12 -1
View File
@@ -54,7 +54,12 @@ from openerp import SUPERUSER_ID
_logger = logging.getLogger(__name__)
RPC_VERSION_1 = {'server_version': '6.1', 'protocol_version': 1}
RPC_VERSION_1 = {
'server_version': release.version,
'server_version_info': release.version_info,
'server_serie': release.serie,
'protocol_version': 1,
}
# This should be moved to openerp.modules.db, along side initialize().
def _initialize_db(serv, id, db_name, demo, lang, user_password):
@@ -120,6 +125,12 @@ class db(netsvc.ExportService):
db = sql_db.db_connect('postgres')
cr = db.cursor()
chosen_template = tools.config['db_template']
cr.execute("""SELECT datname
FROM pg_database
WHERE datname = %s """,
(name,))
if cr.fetchall():
raise openerp.exceptions.Warning(" %s database already exists!" % name )
try:
cr.autocommit(True) # avoid transaction block
cr.execute("""CREATE DATABASE "%s" ENCODING 'unicode' TEMPLATE "%s" """ % (name, chosen_template))
+1
View File
@@ -442,5 +442,6 @@ def stop_service():
"""
if httpd:
httpd.shutdown()
openerp.netsvc.close_socket(httpd.socket)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+15 -7
View File
@@ -8,12 +8,20 @@ Tests can be explicitely added to the `fast_suite` or `checks` lists or not.
See the :ref:`test-framework` section in the :ref:`features` list.
"""
from . import test_acl
from . import test_expression, test_mail, test_ir_sequence, test_orm, \
test_fields, test_basecase, \
test_view_validation, test_uninstall, test_misc, test_db_cursor, \
test_osv, test_translate
from . import test_ir_filters
import test_acl
import test_basecase
import test_db_cursor
import test_expression
import test_fields
import test_ir_filters
import test_ir_sequence
import test_mail
import test_misc
import test_orm
import test_osv
import test_translate
import test_uninstall
import test_view_validation
fast_suite = [
test_ir_sequence,
@@ -33,5 +41,5 @@ checks = [
test_osv,
test_translate,
]
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+4
View File
@@ -177,6 +177,10 @@ class TestCleaner(unittest2.TestCase):
new_html = html_email_clean(False)
self.assertEqual(new_html, False, 'html_email_cleaner did change a False in an other value.')
# Test6: Message with xml and doctype tags don't crash
new_html = html_email_clean(u'<?xml version="1.0" encoding="iso-8859-1"?>\n<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"\n "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n <head>\n <title>404 - Not Found</title>\n </head>\n <body>\n <h1>404 - Not Found</h1>\n </body>\n</html>\n')
self.assertNotIn('encoding', new_html, 'html_email_cleaner did not remove correctly encoding attributes')
class TestHtmlTools(unittest2.TestCase):
""" Test some of our generic utility functions about html """
+1 -1
View File
@@ -484,7 +484,7 @@ class configmanager(object):
if opt.server_wide_modules:
openerp.conf.server_wide_modules = map(lambda m: m.strip(), opt.server_wide_modules.split(','))
else:
openerp.conf.server_wide_modules = ['web']
openerp.conf.server_wide_modules = ['web','web_kanban']
if complete:
openerp.modules.module.initialize_sys_path()
openerp.modules.loading.open_openerp_namespace()
+5 -1
View File
@@ -130,11 +130,15 @@ def html_email_clean(html):
dest += source[idx:]
return dest
if not html:
if not html or not isinstance(html, basestring):
return html
html = ustr(html)
# 0. remove encoding attribute inside tags
doctype = re.compile(r'(<[^>]*\s)(encoding=(["\'][^"\']*?["\']|[^\s\n\r>]+)(\s[^>]*|/)?>)', re.IGNORECASE | re.DOTALL)
html = doctype.sub(r"", html)
# 1. <br[ /]> -> \n, because otherwise the tree is obfuscated
br_tags = re.compile(r'([<]\s*[bB][rR]\s*\/?[>])')
html = _replace_matching_regex(br_tags, html, '__BR_TAG__')
+59
View File
@@ -26,6 +26,12 @@ Some functions related to the os and os.path module
import os
from os.path import join as opj
if os.name == 'nt':
import ctypes
import win32service as ws
import win32serviceutil as wsu
def listdir(dir, recursive=False):
"""Allow to recursively get the file listing"""
dir = os.path.normpath(dir)
@@ -56,6 +62,59 @@ def walksymlinks(top, topdown=True, onerror=None):
yield dirpath, dirnames, filenames
if os.name != 'nt':
getppid = os.getppid
is_running_as_nt_service = lambda: False
else:
# based on http://mail.python.org/pipermail/python-win32/2007-June/006174.html
_TH32CS_SNAPPROCESS = 0x00000002
class _PROCESSENTRY32(ctypes.Structure):
_fields_ = [("dwSize", ctypes.c_ulong),
("cntUsage", ctypes.c_ulong),
("th32ProcessID", ctypes.c_ulong),
("th32DefaultHeapID", ctypes.c_ulong),
("th32ModuleID", ctypes.c_ulong),
("cntThreads", ctypes.c_ulong),
("th32ParentProcessID", ctypes.c_ulong),
("pcPriClassBase", ctypes.c_ulong),
("dwFlags", ctypes.c_ulong),
("szExeFile", ctypes.c_char * 260)]
def getppid():
CreateToolhelp32Snapshot = ctypes.windll.kernel32.CreateToolhelp32Snapshot
Process32First = ctypes.windll.kernel32.Process32First
Process32Next = ctypes.windll.kernel32.Process32Next
CloseHandle = ctypes.windll.kernel32.CloseHandle
hProcessSnap = CreateToolhelp32Snapshot(_TH32CS_SNAPPROCESS, 0)
current_pid = os.getpid()
try:
pe32 = _PROCESSENTRY32()
pe32.dwSize = ctypes.sizeof(_PROCESSENTRY32)
if not Process32First(hProcessSnap, ctypes.byref(pe32)):
raise OSError('Failed getting first process.')
while True:
if pe32.th32ProcessID == current_pid:
return pe32.th32ParentProcessID
if not Process32Next(hProcessSnap, ctypes.byref(pe32)):
return None
finally:
CloseHandle(hProcessSnap)
from contextlib import contextmanager
from openerp.release import nt_service_name
def is_running_as_nt_service():
@contextmanager
def close_srv(srv):
try:
yield srv
finally:
ws.CloseServiceHandle(srv)
with close_srv(ws.OpenSCManager(None, None, ws.SC_MANAGER_ALL_ACCESS)) as hscm:
with close_srv(wsu.SmartOpenService(hscm, nt_service_name, ws.SERVICE_ALL_ACCESS)) as hs:
info = ws.QueryServiceStatusEx(hs)
return info['ProcessId'] == getppid()
if __name__ == '__main__':
from pprint import pprint as pp
+21 -36
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
##############################################################################
#
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
#
@@ -15,14 +15,12 @@
# 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/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
# Win32 python extensions modules
import win32serviceutil
import win32service
import win32event
import win32api
import win32process
import servicemanager
@@ -30,35 +28,32 @@ import servicemanager
import sys
import subprocess
import os
import thread
try:
import meta
except ImportError:
if hasattr(sys, 'frozen'):
raise
from setup import generate_files
generate_files()
import meta # noqa
class OpenERPServerService(win32serviceutil.ServiceFramework):
# required info
_svc_name_ = "openerp-server-6.1"
_svc_display_name_ = "OpenERP Server 6.1"
# optionnal info
_svc_description_ = "OpenERP Server 6.1 service"
_svc_name_ = meta.nt_service_name
_svc_display_name_ = "%s %s" % (meta.description, meta.serie)
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
# Create an event which we will use to wait on.
# The "service stop" request will set this event.
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
# a reference to the server's process
self.terpprocess = None
# info if the service terminates correctly or if the server crashed
self.stopping = False
def SvcStop(self):
# Before we do anything, tell the SCM we are starting the stop process.
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
# stop the running TERP Server: say it's a normal exit
# stop the running OpenERP Server: say it's a normal exit
win32api.TerminateProcess(int(self.terpprocess._handle), 0)
servicemanager.LogInfoMsg("OpenERP Server stopped correctly")
# And set my event.
win32event.SetEvent(self.hWaitStop)
def StartTERP(self):
# The server finds now its configuration automatically on Windows
@@ -69,29 +64,19 @@ class OpenERPServerService(win32serviceutil.ServiceFramework):
server_path = os.path.join(server_dir, 'server', 'openerp-server.exe')
self.terpprocess = subprocess.Popen([server_path], cwd=server_dir, creationflags=win32process.CREATE_NO_WINDOW)
def StartControl(self,ws):
# this listens to the Service Manager's events
win32event.WaitForSingleObject(ws, win32event.INFINITE)
self.stopping = True
def SvcDoRun(self):
# Start OpenERP Server itself
self.StartTERP()
# start the loop waiting for the Service Manager's stop signal
thread.start_new_thread(self.StartControl, (self.hWaitStop,))
# Log a info message that the server is running
servicemanager.LogInfoMsg("OpenERP Server up and running")
# verification if the server is really running, else quit with an error
self.terpprocess.wait()
if not self.stopping:
sys.exit("OpenERP Server check: server not running, check the logfile for more info")
# exit with same exit code as OpenERP process
sys.exit(self.terpprocess.wait())
def option_handler(opts):
# configure the service to auto restart on failures...
subprocess.call(['sc', 'failure', meta.nt_service_name, 'reset=', '0', 'actions=', 'restart/0/restart/0/restart/0'])
if __name__=='__main__':
if __name__ == '__main__':
# Do with the service whatever option is passed in the command line
win32serviceutil.HandleCommandLine(OpenERPServerService)
win32serviceutil.HandleCommandLine(OpenERPServerService, customOptionHandler=option_handler)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+41 -10
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
##############################################################################
#
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
@@ -15,7 +15,7 @@
# 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/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
@@ -30,15 +30,46 @@ def datas():
r.append(("Microsoft.VC90.CRT", glob.glob('C:\Microsoft.VC90.CRT\*.*')))
return r
setup(service=["OpenERPServerService"],
options={"py2exe":{"excludes":["Tkconstants","Tkinter","tcl",
"_imagingtk","PIL._imagingtk",
"ImageTk", "PIL.ImageTk",
"FixTk"],
"skip_archive": 1,
"optimize": 2,}},
meta = {}
execfile(os.path.join(os.path.dirname(__file__), '..', 'openerp', 'release.py'), meta)
def generate_files():
actions = {
'start': ['stop', 'start'],
'stop': ['stop'],
}
files = []
for action, steps in actions.items():
fname = action + '.bat'
files.append(fname)
with open(fname, 'w') as fp:
fp.write('@PATH=%WINDIR%\system32;%WINDIR%;%WINDIR%\System32\Wbem;.\n')
for step in steps:
fp.write('@net %s %s\n' % (step, meta['nt_service_name']))
files.append('meta.py')
with open('meta.py', 'w') as fp:
for m in 'description serie nt_service_name'.split():
fp.write("%s = %r\n" % (m, meta[m],))
return files
excludes = "Tkconstants Tkinter tcl _imagingtk PIL._imagingtk ImageTk PIL.ImageTk FixTk".split()
setup(service = ["OpenERPServerService"],
version = meta['version'],
license = meta['license'],
url = meta['url'],
author = meta['author'],
author_email = meta['author_email'],
data_files = generate_files(),
options = {"py2exe": {
"excludes": excludes,
"skip_archive": 1,
"optimize": 2,
}},
data_files=datas(),
)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
-7
View File
@@ -1,7 +0,0 @@
@PATH=%WINDIR%\system32;%WINDIR%;%WINDIR%\System32\Wbem;.
@net stop openerp-server-6.1
@net start openerp-server-6.1
cls
-5
View File
@@ -1,5 +0,0 @@
@PATH=%WINDIR%\system32;%WINDIR%;%WINDIR%\System32\Wbem;.
@net stop openerp-server-6.1
cls