diff --git a/doc/adding-command.rst b/doc/adding-command.rst new file mode 100644 index 00000000000..5a177b06817 --- /dev/null +++ b/doc/adding-command.rst @@ -0,0 +1,34 @@ +.. _adding-command: + +Adding a new command +==================== + +``oe`` uses the argparse_ library to implement commands. Each +command lives in its own ``openerpcommand/.py`` file. + +.. _argparse: http://docs.python.org/2.7/library/argparse.html + +To create a new command, probably the most simple way to get started is to +copy/paste an existing command, say ``openerpcommand/initialize.py`` to +``openerpcommand/foo.py``. In the newly created file, the important bits +are the ``run(args)`` and ``add_parser(subparsers)`` functions. + +``add_parser``'s responsability is to create a (sub-)parser for the command, +i.e. describe the different options and flags. The last thing it does is to set +``run`` as the function to call when the command is invoked. + +.. code-block:: python + + > def add_parser(subparsers): + > parser = subparsers.add_parser('', + > description='...') + > parser.add_argument(...) + > ... + > parser.set_defaults(run=run) + +``run(args)`` actually implements the command. It should be kept as simple as +possible and delegate most of its work to small functions (probably placed at +the top of the new file). In other words, its responsability is mainly to +deal with the presence/absence/pre-processing of ``argparse``'s arguments. + +Finally, the module must be added to ``openerpcommand/__init__.py``. diff --git a/doc/conf.py b/doc/conf.py new file mode 100644 index 00000000000..57edef310c9 --- /dev/null +++ b/doc/conf.py @@ -0,0 +1,256 @@ +# -*- coding: utf-8 -*- +# +# OpenERP Technical Documentation configuration file, created by +# sphinx-quickstart on Fri Feb 17 16:14:06 2012. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) +sys.path.append(os.path.abspath('_themes')) +sys.path.append(os.path.abspath('..')) +sys.path.append(os.path.abspath('../openerp')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.viewcode'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'OpenERP Server Developers Documentation' +copyright = u'2012, OpenERP s.a.' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '7.0' +# The full version, including alpha/beta/rc tags. +release = '7.0b' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'flask' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +html_theme_path = ['_themes'] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +html_sidebars = { + 'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'], + '**': ['sidebarlogo.html', 'localtoc.html', 'relations.html', + 'sourcelink.html', 'searchbox.html'] +} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'openerp-server-doc' + + +# -- Options for LaTeX output -------------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'openerp-server-doc.tex', u'OpenERP Server Developers Documentation', + u'OpenERP s.a.', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'openerp-server-doc', u'OpenERP Server Developers Documentation', + [u'OpenERP s.a.'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------------ + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'OpenERPServerDocumentation', u'OpenERP Server Developers Documentation', + u'OpenERP s.a.', 'OpenERPServerDocumentation', 'Developers documentation for the openobject-server project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = { + 'python': ('http://docs.python.org/', None), + 'openerpweb': ('http://doc.openerp.com/trunk/developers/web', None), +} diff --git a/doc/index.rst b/doc/index.rst index 4dd2eddf0eb..64fc04b552e 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -18,13 +18,21 @@ OpenERP Server 06_misc 09_deployment +OpenERP Command +''''''''''''''' + +.. toctree:: + :maxdepth: 1 + + openerp-command.rst + adding-command.rst + OpenERP Server API '''''''''''''''''' .. toctree:: :maxdepth: 1 - api_core.rst api_models.rst Concepts diff --git a/doc/openerp-command.rst b/doc/openerp-command.rst new file mode 100644 index 00000000000..2bfd845f8e3 --- /dev/null +++ b/doc/openerp-command.rst @@ -0,0 +1,60 @@ +.. _openerp-command: + +OpenERP Command +=============== + +The ``oe`` script provides a set of command-line tools around the OpenERP +framework. + +Using OpenERP Command +--------------------- + +In contrast to the previous ``openerp-server`` script, ``oe`` defines a few +sub-commands, each with its own set of flags and options. You can get some +information for any of them with + +:: + + > oe --help + +For instance:: + + > oe run-tests --help + +Some ``oe`` options can be provided via environment variables. For instance:: + + > export OPENERP_DATABASE=trunk + > export OPENERP_HOST=127.0.0.1 + > export OPENERP_PORT=8069 + +Depending on your needs, you can group all of the above in one single script; +for instance here is a, say, ``test-trunk-view-validation.sh`` file:: + + COMMAND_REPO=/home/thu/repos/command/trunk/ + SERVER_REPO=/home/thu/repos/server/trunk + + export PYTHONPATH=$SERVER_REPO:$COMMAND_REPO + export PATH=$SERVER_REPO:$COMMAND_REPO:$PATH + export OPENERP_DATABASE=trunk + export OPENERP_HOST=127.0.0.1 + export OPENERP_PORT=8069 + + # The -d ignored is actually needed by `oe` even though `test_view_validation` + # itself does not need it. + oe run-tests -d ignored -m openerp.test_view_validation + +Adding new commands +------------------- + +See the :doc:`adding-command` page. + +Bash completion +--------------- + +A preliminary ``oe-bash-completion`` file is provided. After sourcing it, + +:: + + > . oe-bash-completion + +completion (using the TAB character) in Bash should be working. diff --git a/oe b/oe new file mode 100755 index 00000000000..59827731409 --- /dev/null +++ b/oe @@ -0,0 +1,5 @@ +#! /usr/bin/env python2 + +if __name__ == '__main__': + import openerpcommand.main + openerpcommand.main.run() diff --git a/oe-bash-completion b/oe-bash-completion new file mode 100644 index 00000000000..57a4391d803 --- /dev/null +++ b/oe-bash-completion @@ -0,0 +1,89 @@ +_oe() +{ + local cur prev opts + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" + + cmd="${COMP_WORDS[0]}" + subcmd="" + if [[ ${COMP_CWORD} > 0 ]] ; then + subcmd="${COMP_WORDS[1]}" + fi + + # oe + + opts="initialize model read run-tests scaffold update \ + call open show consume-nothing consume-memory leak-memory \ + consume-cpu bench-read bench-fields-view-get bench-dummy bench-login \ + bench-sale-mrp --help" + + if [[ ${prev} == oe && ${cur} != -* ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + fi + + # oe call + + opts="--database --user --password --host --port --help" + + if [[ ${subcmd} == call ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + fi + + # oe initialize + + opts="--database --addons --all-modules --exclude --no-create --help" + + if [[ ${subcmd} == initialize ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + fi + + # oe model + + opts="--database --model --field --verbose --help" + + if [[ ${subcmd} == model ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + fi + + # oe read + + opts="--database --model --id --field --verbose --short --help" + + if [[ ${subcmd} == read ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + fi + + # oe run-tests + + opts="--database --addons --module --dry-run --help" + + if [[ ${subcmd} == run-tests ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + fi + + # oe scaffold + + opts="--help" + + if [[ ${subcmd} == scaffold ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + fi + + # fallback for unimplemented completion + + opts="--help" + + if [[ true ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + fi +} +complete -F _oe oe diff --git a/openerp/addons/base/i18n/ro.po b/openerp/addons/base/i18n/ro.po index 9e9e91fe31e..3a8c21af815 100644 --- a/openerp/addons/base/i18n/ro.po +++ b/openerp/addons/base/i18n/ro.po @@ -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-21 17:04+0000\n" -"PO-Revision-Date: 2012-12-31 21:53+0000\n" +"PO-Revision-Date: 2013-01-05 18:18+0000\n" "Last-Translator: Fekete Mihai \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: 2013-01-01 04:46+0000\n" -"X-Generator: Launchpad (build 16378)\n" +"X-Launchpad-Export-Date: 2013-01-06 04:45+0000\n" +"X-Generator: Launchpad (build 16393)\n" #. module: base #: model:ir.module.module,description:base.module_account_check_writing @@ -25,7 +25,7 @@ msgid "" " " msgstr "" "\n" -"Modul pentru Verifica Scrisul si Verifica Imprimarea.\n" +"Modul pentru Bifeaza Scriere si Bifeaza Imprimare.\n" "================================================\n" " " @@ -10395,6 +10395,8 @@ msgid "" "Translation features are unavailable until you install an extra OpenERP " "translation." msgstr "" +"Caracteristicile traducerii nu sunt disponibile pana cand nu instalati o " +"traducere OpenERP suplimentara." #. module: base #: model:ir.module.module,description:base.module_l10n_nl @@ -10438,6 +10440,45 @@ msgid "" "\n" " " msgstr "" +"\n" +"Acesta este modulul pentru gestionarea planului de conturi pentru Olanda in " +"OpenERP.\n" +"=============================================================================" +"\n" +"\n" +"Cititi schimbarea registrului in file __openerp__.py pentru informatii " +"despre versiune.\n" +"Dit is een basismodule om een uitgebreid grootboek- en BTW schema voor\n" +" Nederlandse bedrijven te installeren in OpenERP versie 7.0.\n" +" \n" +"De BTW rekeningen zijn waar nodig gekoppeld om de juiste rapportage te " +"genereren,\n" +"denk b.v. aan intracommunautaire verwervingen waarbij u 21% BTW moet " +"opvoeren,\n" +"maar tegelijkertijd ook 21% als voorheffing weer mag aftrekken.\n" +" \n" +"Na installatie van deze module word de configuratie wizard voor 'Accounting' " +"aangeroepen.\n" +" * U krijgt een lijst met grootboektemplates aangeboden waarin zich ook " +"het\n" +" Nederlandse grootboekschema bevind.\n" +"\n" +" * Als de configuratie wizard start, wordt u gevraagd om de naam van uw " +"bedrijf\n" +" in te voeren, welke grootboekschema te installeren, uit hoeveel " +"cijfers een\n" +" grootboekrekening mag bestaan, het rekeningnummer van uw bank en de " +"currency\n" +" om Journalen te creeren.\n" +"\n" +"Let op!! -> De template van het Nederlandse rekeningschema is opgebouwd uit " +"4\n" +"cijfers. Dit is het minimale aantal welk u moet invullen, u mag het aantal " +"verhogen.\n" +"De extra cijfers worden dan achter het rekeningnummer aangevult met " +"'nullen'.\n" +"\n" +" " #. module: base #: help:ir.rule,global:0 @@ -10483,7 +10524,7 @@ msgstr "Raport de introspectie asupra obiectelor" #. module: base #: model:ir.module.module,shortdesc:base.module_web_analytics msgid "Google Analytics" -msgstr "" +msgstr "Google Analytics" #. module: base #: model:ir.module.module,description:base.module_note @@ -10502,6 +10543,22 @@ msgid "" "\n" "Notes can be found in the 'Home' menu.\n" msgstr "" +"\n" +"Acest modul le permite utilizatorilor sa isi creeze propriile notite in " +"OpenERP.\n" +"=================================================================\n" +"\n" +"Foloseste notitele pentru a scrie procesele verbale ale intalnirilor, a " +"organiza idei, a organiza liste proprii\n" +"cu ceea ce este de efectuat, etc. Fiecare utilizator isi gestioneaza " +"propriile Notite. Notitele sunt disponibile\n" +"doar autorilor lor, dar acestia isi pot impartasi notitele si altor " +"utilizatori astfel ca mai multi\n" +"oameni pot lucra la aceeasi nota in timp real. Este foarte eficienta " +"impartasirea\n" +"proceselor verbale ale intalnirilor.\n" +"\n" +"Notitele pot fi gasite in meniul 'Acasa'.\n" #. module: base #: model:res.country,name:base.dm @@ -10511,7 +10568,7 @@ msgstr "Dominica" #. module: base #: field:ir.translation,name:0 msgid "Translated field" -msgstr "" +msgstr "Camp Tradus" #. module: base #: model:ir.module.module,shortdesc:base.module_stock_location @@ -10531,17 +10588,17 @@ msgstr "Nepal" #. module: base #: model:ir.module.module,shortdesc:base.module_document_page msgid "Document Page" -msgstr "" +msgstr "Pagina Documentului" #. module: base #: model:ir.module.module,shortdesc:base.module_l10n_ar msgid "Argentina Localization Chart Account" -msgstr "" +msgstr "Localizare Plan de Conturi Argentina" #. module: base #: field:ir.module.module,description_html:0 msgid "Description HTML" -msgstr "" +msgstr "Descriere HTML" #. module: base #: help:res.groups,implied_ids:0 @@ -10551,7 +10608,7 @@ msgstr "Utilizatorii din acest grup mostenesc automat acele grupuri" #. module: base #: model:ir.module.module,summary:base.module_note msgid "Sticky notes, Collaborative, Memos" -msgstr "" +msgstr "Notite pe foaie adeziva, Colaborative, Memorii" #. module: base #: model:ir.module.module,shortdesc:base.module_hr_attendance @@ -10590,6 +10647,9 @@ msgid "" "If the selected language is loaded in the system, all documents related to " "this contact will be printed in this language. If not, it will be English." msgstr "" +"Daca limba selectata este incarcata in sistem, toate documentele asociate " +"acestui contact vor fi tiparite in aceasta limba. Daca nu, limba va fi " +"engleza." #. module: base #: model:ir.module.module,description:base.module_hr_evaluation @@ -10623,6 +10683,36 @@ msgid "" "employees evaluation plans. Each user receives automatic emails and requests " "to perform a periodical evaluation of their colleagues.\n" msgstr "" +"\n" +"Evaluarea periodica a angajatilor si aprecieri.\n" +"==============================================\n" +"\n" +"Folosind aceasta aplicatie puteti mentine procesul motivational efectuand " +"evaluari periodice ale performantei angajatilor dumneavoastra. Evaluarea " +"regulata a resurselor umane poate fi benefica atat angajatilor cat si " +"organizatiei dumneavoastra. \n" +"\n" +"Un plan de evaluare poate fi atribuit fiecarui angajat. Aceste planuri " +"definesc frecventa si modul in care va gestionati evaluarile periodice " +"personale. Veti putea sa definiti pasii si sa atasati formulare de " +"interviuri fiecarui pas. \n" +"\n" +"Gestioneaza diverse tipuri de evaluare: de jos in sus, de sus in jos, " +"autoevaluari si evaluarea finala a managerului.\n" +"\n" +"Caracteristici cheie:\n" +"------------\n" +"* Capacitatea de a crea evaluari ale angajatilor.\n" +"* O evaluare poate fi creata de catre un angajat pentru subalterni, juniori, " +"precum si pentru managerul lui.\n" +"* Evaluarea este efectuata conform unui plan in care pot fi create diverse " +"sondaje. La fiecare sondaj se poate raspunde la un anumit nivel din ierarhia " +"angajatilor. Verificarea si evaluarea finala sunt facute de catre manager.\n" +"* Fiecare evaluare completata de catre angajati poate fi vizualizata intr-un " +"formular PDF.\n" +"* Cererile de Interviu sunt generate automat de OpenERP conform planurilor " +"de evaluare ale angajatilor. Fiecare utilizator primeste email-uri si cereri " +"automate pentru a efectua o evaluare periodica a colegilor lor.\n" #. module: base #: model:ir.ui.menu,name:base.menu_view_base_module_update @@ -10684,6 +10774,12 @@ msgid "" "\n" " " msgstr "" +"\n" +"Plan de Conturi bolivian si localizarea taxelor boliviene.\n" +"\n" +"Plan de conturi bolivian si impozite conform legislatiei in vigoare\n" +"\n" +" " #. module: base #: view:res.lang:0 @@ -10698,7 +10794,7 @@ msgstr "Slovena / slovenščina" #. module: base #: field:res.currency,position:0 msgid "Symbol Position" -msgstr "" +msgstr "Pozitie Simbol" #. module: base #: model:ir.module.module,description:base.module_l10n_de @@ -10739,11 +10835,15 @@ msgid "" "\n" "(Document type: %s)" msgstr "" +"Pentru acest tip de document, puteti accesa numai inregistrari pe care le-" +"ati creat dumneavoastra.\n" +"\n" +"(Tipul documentului: %s)" #. module: base #: view:base.language.export:0 msgid "documentation" -msgstr "" +msgstr "documentatie" #. module: base #: help:ir.model,osv_memory:0 @@ -10751,6 +10851,8 @@ msgid "" "This field specifies whether the model is transient or not (i.e. if records " "are automatically deleted from the database or not)" msgstr "" +"Acest camp specifica daca modulul este provizoriu sau nu (adica daca " +"inregistrarile sunt sterse automat din baza de date sau nu)" #. module: base #: code:addons/base/ir/ir_mail_server.py:440 @@ -10789,12 +10891,12 @@ msgstr "%b - Numele prescurtat al lunii." #: code:addons/base/ir/ir_model.py:722 #, python-format msgid "Sorry, you are not allowed to delete this document." -msgstr "" +msgstr "Ne pare rau, nu aveti permisiunea de a sterge acest document." #. module: base #: constraint:ir.rule:0 msgid "Rules can not be applied on the Record Rules model." -msgstr "" +msgstr "Regulile nu pot fi aplicate la modelul Regulile Inregistrarii." #. module: base #: field:res.partner,supplier:0 @@ -10811,7 +10913,7 @@ msgstr "Actiuni multiple" #. module: base #: model:ir.module.module,summary:base.module_mail msgid "Discussions, Mailing Lists, News" -msgstr "" +msgstr "Discutii, Liste, Stiri" #. module: base #: model:ir.module.module,description:base.module_fleet @@ -10833,6 +10935,25 @@ msgid "" "* Show all costs associated to a vehicle or to a type of service\n" "* Analysis graph for costs\n" msgstr "" +"\n" +"Vehicul, leasing, asigurari, costuri\n" +"==================================\n" +"Cu acest modul, OpenERP va ajuta sa va gestionati toate vehiculele, " +"contactele\n" +"asociate acelor vehicule, precum servicii, registrul inregistrarilor\n" +"combustibilului, costuri si multe alte caracteristici necesare pentru " +"gestionarea \n" +"parcului dumneavoastra auto.\n" +"\n" +"Caracteristici principale:\n" +"-------------\n" +"* Adauga vehicule parcului dumneavoastra auto\n" +"* Gestioneaza contracte pentru vehicule\n" +"* Memento atunci cand un contract ajunge la data de expirare\n" +"* Adauga servicii, inregistrarea in registrul de combustibil, valorile " +"contorului de kilometraj pentru toate vehiculele\n" +"* Afiseaza toate costurile asociate unui vehicul sau unui tip de servicii\n" +"* Grafic de analiza a costurilor\n" #. module: base #: field:multi_company.default,company_dest_id:0 @@ -10862,7 +10983,7 @@ msgstr "Samoa Americana" #. module: base #: view:ir.attachment:0 msgid "My Document(s)" -msgstr "" +msgstr "Documentul meu(s)" #. module: base #: help:ir.actions.act_window,res_model:0 @@ -10912,7 +11033,7 @@ msgstr "EroareUtilizator" #. module: base #: model:ir.module.module,summary:base.module_project_issue msgid "Support, Bug Tracker, Helpdesk" -msgstr "" +msgstr "Asistenta, Gasire Erori, Serviciul de Asistenta" #. module: base #: model:res.country,name:base.ae @@ -10926,6 +11047,10 @@ msgid "" "related to a model that uses the need_action mechanism, this field is set to " "true. Otherwise, it is false." msgstr "" +"Daca actiunea de inregistrare in meniu este o actiune de tipul " +"fereastra_activa, si daca aceasta actiune este asociata unui model care " +"foloseste mecanismul nevoia_de_actiune, acest camp este setat pe adevarat. " +"Altfel, este fals." #. module: base #: code:addons/orm.py:3960 @@ -10939,12 +11064,12 @@ msgstr "" #. module: base #: model:res.partner.category,name:base.res_partner_category_5 msgid "Silver" -msgstr "" +msgstr "Argintiu" #. module: base #: field:res.partner.title,shortcut:0 msgid "Abbreviation" -msgstr "" +msgstr "Abreviere" #. module: base #: model:ir.ui.menu,name:base.menu_crm_case_job_req_main @@ -10965,7 +11090,7 @@ msgstr "" "Acesta este modulul de baza pentru gestionarea planului de conturi pentru " "Grecia.\n" "==================================================================\n" -" \n" +"\n" "Plan de conturi grecesc si localizare.\n" " " @@ -11077,6 +11202,112 @@ msgid "" "authentication if installed at the same time.\n" " " msgstr "" +"\n" +"Adauga asistenta pentru autentificare de catre serverul LDAP.\n" +"===============================================\n" +"Acest modul le permite utilizatorilor sa se conecteze cu numele de " +"utilizator si parola LDAP, si\n" +"va crea automat utilizatori OpenERP pentru ei pe loc. \n" +"\n" +"**Nota:** Acest modul functioneaza numai pe serverele care au instalat " +"modulul Python ``ldap``.\n" +"\n" +"Configurare:\n" +"--------------\n" +"Dupa instalarea acestui modul, trebuie sa configurati parametrii LDAP in\n" +"tabul Configurare cu detaliile Companiei. Companii diferite pot avea " +"servere\n" +"LDAP diferite, atata timp cat au nume de utilizatori unice (numele de " +"utilizatori trebuie sa fie unice\n" +"in OpenERP, chiar si in cazul companiilor multiple).\n" +"\n" +"Conectarea LDAP anonima este de asemenea acceptata (pentru serverele LDAP " +"care permit acest lucru), pur\n" +"si simplu lasand campul utilizator si parola LDAP necompletate in " +"configurarea LDAP.\n" +"Aceasta nu permite autentificarea anonima a utilizatorilor, este doar pentru " +"contul LDAP\n" +"principal care este utilizat pentru a verifica daca un utilizator exista " +"inainte de a incerca\n" +"autentificarea lui.\n" +"\n" +"Securizarea legaturii cu STARTTLS este disponibila pentru serverele LDAP " +"care o accepta,\n" +"activand optiunea TLS din configurarea LDAP.\n" +"\n" +"Pentru mai multe optiuni legate de configurarea setarilor LDAP, accesati " +"pagina ldap.conf\n" +"manpage: manpage:`ldap.conf(5)`.\n" +"\n" +"Consideratii legate de Securitate:\n" +"------------------------\n" +"Parolele utilizatorilor LDAP nu sunt niciciodata stocate in baza de date " +"OpenERP, iar serverul LDAP\n" +"este intrebat ori de cate ori un utilizator trebuie autentificat. Nu are loc " +"nici o copiere a\n" +"parolei, iar parolele sunt gestionate doar intr-un singur loc.\n" +"\n" +"OpenERP nu gestioneaza schimbarile de parole in LDAP, astfel ca orice " +"modificare a parolei\n" +"trebuie efectuata prin alte mijloace direct in directorul LDAP (pentru " +"utilizatorii LDAP).\n" +"\n" +"De asemenea, este posibil sa aveti utilizatori locali OpenERP in baza de " +"date impreuna cu\n" +"utilizatorii LDAP autentificati (contul Administrator este un exemplu " +"evident).\n" +"\n" +"Iata modul cum functioneaza:\n" +"---------------------\n" +" * Sistemul incearca mai intai sa autentifice utilizatorii din baza " +"locala de date\n" +" OpenERP;\n" +" * daca aceasta autentificare esueaza (de exemplu pentru ca utilizatorul " +"nu are o parola\n" +" locala), atunci sistemul incearca sa autentifice in LDAP;\n" +"\n" +"Din moment ce utilizatorii LDAP au implicit parole necompletate in baza " +"locala de date OpenERP\n" +"(ceea ce inseamna ca nu au acces), primul pas esueaza intotdeauna, iar " +"serverul LDAP trebuie\n" +"sa faca autentificarea.\n" +"\n" +"Activarea STARTTLS se asigura ca cererea de autentificare in serverul este\n" +"criptata.\n" +"\n" +"Sablon Utilizator:\n" +"--------------\n" +"In configurarea LDAP din formularul Companiei, se poate selecta un *Sablon " +"de\n" +"Utilizator*. Daca este setat, acest utilizator va fi folosit ca sablon " +"pentru a crea utilizatorii locali\n" +"ori de cate ori cineva se autentifica pentru prima data prin intermediul " +"autentificarii LDAP. Aceasta\n" +"permite pre-setarea grupurilor si meniurilor implicite pentru utilizatorii " +"de prima data.\n" +"\n" +"**Avertizare:** daca configurati o parola pentru utilizatorul sablon, " +"aceasta parola va fi\n" +" atribuita ca parola locala pentru fiecare utilizator LDAP nou, " +"configurand eficient\n" +" o *parola principala* pentru acesti utilizatori (pana cand este " +"modificata manual). De\n" +" obicei, nu se doreste acest lucru. Un pas usor de a seta un " +"utilizator sablon este de a\n" +" va conecta odata ca un utilizator LDAP valid, sa lasati ca OpenERP " +"sa creeze un utilizator local\n" +" necompletat cu aceeleasi date de autentificare (si o parola " +"necompletata), apoi sa redenumiti acest\n" +" utilizator nou cu un utilizator care nu exista in LDAP, si sa ii " +"configurati grupurile\n" +" asa cum doriti.\n" +"\n" +"Interactiunea cu base_crypt:\n" +"----------------------------\n" +"Modulul base_crypt nu este compatibil cu acest modul, si va dezactiva " +"autentificarea\n" +"LDAP daca sunt instalate in acelasi timp.\n" +" " #. module: base #: model:res.country,name:base.re @@ -11139,16 +11370,20 @@ msgid "" "=============================\n" "\n" msgstr "" +"\n" +"Vizualizare diagrama Gantt OpenERP Web.\n" +"=============================\n" +"\n" #. module: base #: model:ir.module.module,shortdesc:base.module_base_status msgid "State/Stage Management" -msgstr "" +msgstr "Managementul Starii/Etapei" #. module: base #: model:ir.module.category,name:base.module_category_warehouse_management msgid "Warehouse" -msgstr "" +msgstr "Depozit" #. module: base #: field:ir.exports,resource:0 @@ -11172,6 +11407,17 @@ msgid "" "\n" " " msgstr "" +"\n" +"Acest modul arata procesele de baza implicate in modulele selectate si " +"ordinea in care au loc.\n" +"=============================================================================" +"=========================\n" +"\n" +"**Nota:** Acesta se aplica modulelor care contin modulename_process.xml.\n" +"\n" +"**exemplu:** produs/proces/produs_proces.xml.\n" +"\n" +" " #. module: base #: view:res.lang:0 @@ -11181,7 +11427,7 @@ msgstr "8. %I:%M:%S %p ==> 06:25:20 PM" #. module: base #: view:ir.filters:0 msgid "Filters shared with all users" -msgstr "" +msgstr "Filtre comune tuturor utilizatorilor" #. module: base #: view:ir.translation:0 @@ -11197,7 +11443,7 @@ msgstr "Raport" #. module: base #: model:res.partner.title,shortcut:base.res_partner_title_prof msgid "Prof." -msgstr "" +msgstr "Prof." #. module: base #: code:addons/base/ir/ir_mail_server.py:238 @@ -11233,7 +11479,7 @@ msgstr "Niciunul/Niciuna" #. module: base #: model:ir.module.module,shortdesc:base.module_hr_holidays msgid "Leave Management" -msgstr "" +msgstr "Managementul Concediilor" #. module: base #: model:res.company,overdue_msg:base.main_company @@ -11249,6 +11495,16 @@ msgid "" "Thank you in advance for your cooperation.\n" "Best Regards," msgstr "" +"Stimate Domn/Doamna,\n" +"\n" +"Inregistrarile noastre indica faptul ca unii parametrii din contul " +"dumneavoastra sunt inca scadente. Aveti toate detaliile mai jos.\n" +"Daca suma a fost deja platita, va rugam sa ignorati acest aviz. In cazul in " +"care nu a fost platita inca, va rugam sa efectuati plata restanta.\n" +"Daca aveti intrebari in legatura cu acest cont, va rugam sa ne contactati.\n" +"\n" +"Va multumim anticipat pentru cooperarea dumneavoastra.\n" +"Cu stima," #. module: base #: view:ir.module.category:0 @@ -11273,7 +11529,7 @@ msgstr "Mali" #. module: base #: model:ir.ui.menu,name:base.menu_project_config_project msgid "Stages" -msgstr "" +msgstr "Etape" #. module: base #: selection:base.language.install,lang:0 @@ -11305,6 +11561,13 @@ msgid "" "==========================================================\n" " " msgstr "" +"\n" +"Acest modul adauga o lista de angajati paginii de contact a portalului " +"dumneavoastra daca sunt instalate modulele hr (resurse umane) si portal_crm " +"(care creeaza pagina de contact).\n" +"=============================================================================" +"==========================================================\n" +" " #. module: base #: model:res.country,name:base.bn @@ -11327,7 +11590,7 @@ msgstr "Interfata Utilizator" #. module: base #: model:ir.module.module,shortdesc:base.module_mrp_byproduct msgid "MRP Byproducts" -msgstr "" +msgstr "Produse secundare MRP" #. module: base #: field:res.request,ref_partner_id:0 @@ -11337,7 +11600,7 @@ msgstr "Ref. partener" #. module: base #: model:ir.module.module,shortdesc:base.module_hr_expense msgid "Expense Management" -msgstr "" +msgstr "Managementul Cheltuielilor" #. module: base #: field:ir.attachment,create_date:0 @@ -11371,6 +11634,12 @@ msgid "" "Indian accounting chart and localization.\n" " " msgstr "" +"\n" +"Contabilitatea Indiana: Plan de Conturi.\n" +"====================================\n" +"\n" +"Plan de conturi si localizare indiene.\n" +" " #. module: base #: model:ir.module.module,shortdesc:base.module_l10n_uy @@ -11385,7 +11654,7 @@ msgstr "Comenzi rapide personalizate" #. module: base #: model:ir.module.module,shortdesc:base.module_l10n_si msgid "Slovenian - Accounting" -msgstr "" +msgstr "Slovena - Contabilitate" #. module: base #: model:ir.module.module,description:base.module_account_cancel @@ -11399,6 +11668,15 @@ msgid "" "If set to true it allows user to cancel entries & invoices.\n" " " msgstr "" +"\n" +"Permite anularea inregistrarilor contabile.\n" +"====================================\n" +"\n" +"Acest modul adauga campul 'Permite Anularea Inregistrarilor' in vizualizarea " +"formularului a jurnalului contului.\n" +"Daca este setat pe adevarat, ii permite utilizatorului sa anuleze " +"inregistrari si facturi.\n" +" " #. module: base #: model:ir.module.module,shortdesc:base.module_plugin @@ -11416,7 +11694,7 @@ msgstr "Modele" #: code:addons/base/module/module.py:500 #, python-format msgid "The `base` module cannot be uninstalled" -msgstr "" +msgstr "Modulul 'baza' nu poate fi dezinstalat" #. module: base #: code:addons/base/ir/ir_cron.py:247 @@ -11443,7 +11721,7 @@ msgstr "osv_memorie.autovid" #: code:addons/base/ir/ir_model.py:721 #, python-format msgid "Sorry, you are not allowed to create this kind of document." -msgstr "" +msgstr "Ne pare rau, nu aveti permisiunea de a crea acest tip de document." #. module: base #: field:base.language.export,lang:0 @@ -11485,7 +11763,7 @@ msgstr "%H - Ora (24 ore) [00,23]." #. module: base #: field:ir.model.fields,on_delete:0 msgid "On Delete" -msgstr "" +msgstr "Sterge" #. module: base #: code:addons/base/ir/ir_model.py:342 @@ -11520,6 +11798,13 @@ msgid "" "=======================================================\n" " " msgstr "" +"\n" +"Acest modul adauga o pagina de contact (cu un formular de contact care " +"creeaza o pista atunci cand este trimis) portalului dumneavoastra daca sunt " +"instalate modulele crm (managementul relatiei cu clientii) si portal.\n" +"=============================================================================" +"=======================================================\n" +" " #. module: base #: model:ir.module.module,shortdesc:base.module_l10n_us @@ -11546,7 +11831,7 @@ msgstr "Anulati" #: code:addons/orm.py:1506 #, python-format msgid "Unknown database identifier '%s'" -msgstr "" +msgstr "Identificator necunoscut '%s' al bazei de date" #. module: base #: selection:base.language.export,format:0 @@ -11561,6 +11846,10 @@ msgid "" "=========================\n" "\n" msgstr "" +"\n" +"Vizualizare Diagrama OpenERP Web.\n" +"=========================\n" +"\n" #. module: base #: model:ir.module.module,description:base.module_l10n_ch @@ -11598,6 +11887,39 @@ msgid "" "launchpad:\n" "https://launchpad.net/openerp-swiss-localization\n" msgstr "" +"\n" +"Localizare elvetiana :\n" +"====================\n" +"**Plan de conturi si taxe elvetiene STERCHI limbi multiple**\n" +" **Autor:** Camptocamp SA\n" +"\n" +" ** Sponsori:** Hasa Sàrl, Open Net Sàrl si Prisme Solutions Informatique " +"SA\n" +"\n" +" **Traducatori:** brain-tec AG, Agile Business Group\n" +"\n" +"**Aceasta versiune va introduce schimbari majore la l10n_ch.**\n" +"\n" +"Datorita unor nevoi importante de refactorizare si a adoptarii de catre " +"Elvetia a noului standard international de plata pentru 2013-2014. Am " +"reorganizat localizarea elvetiana in acest mod:\n" +"\n" +"- **l10n_ch**: Plan de conturi si taxe elvetiene STERCHI limbi multiple " +"(addon oficial)\n" +" - **l10n_ch_base_bank**: Modul tehnic care introduce o versiune noua si " +"simplificata a managementului de tip bancar\n" +" - **l10n_ch_bank**: Lista cu bancile elvetiene\n" +" - **l10n_ch_zip**: Lista cu coduri postale elvetiene\n" +" - **l10n_ch_dta**: Asistenta pentru protocolul platii (va fi valabil pana " +"la sfarsitul anului 2014)\n" +" - **l10n_ch_payment_slip**: Asistenta pentru raportul si reconcilierea " +"platii ESR/BVR. Raport refactorizat cu pozitionarea usoara a elementelor.\n" +" - **l10n_ch_sepa**: Implementarea alfa a asistentei PostFinance SEPA/PAIN " +"va fi completata in timpul lui 2013/2014\n" +"\n" +"Modulele vor fi dispobibile in curand in localizarea elvetiana OpenERP pe " +"launchpad:\n" +"https://launchpad.net/openerp-swiss-localization\n" #. module: base #: model:res.country,name:base.nt @@ -11632,12 +11954,36 @@ msgid "" "Accounting/Invoicing settings.\n" " " msgstr "" +"\n" +"Acest modul adauga un meniu Vanzari portalului dumneavoastra de indata ce " +"vanzari si portal sunt instalate.\n" +"=============================================================================" +"=========\n" +"\n" +"Dupa instalarea acestui modul, utilizatorii portalului vor putea sa isi " +"acceseze propriile documente\n" +"prin intermediul urmatoarelor meniuri:\n" +"\n" +" - Cotatii\n" +" - Comenzi de Vanzare\n" +" - Comenzi de Livrare\n" +" - Produse (cele publice)\n" +" - Facturi\n" +" - Plati/Rambursari\n" +"\n" +"Daca sunt configurati beneficiarii platii online, utilizatorii portalului " +"vor avea posibilitatea de a-si\n" +"plati online Comenzile de Vanzari si Facturile care nu au fost platite inca. " +"Paypal este inclus\n" +"implicit, trebuie doar sa configurati un cont Paypal in setarile " +"Contabilitate/Facturare.\n" +" " #. module: base #: code:addons/base/ir/ir_fields.py:316 #, python-format msgid "external id" -msgstr "" +msgstr "id extern" #. module: base #: view:ir.model:0 @@ -11657,7 +12003,7 @@ msgstr "Managementul achizitiilor" #. module: base #: field:ir.module.module,published_version:0 msgid "Published Version" -msgstr "Versiune publicata" +msgstr "Versiunea Publicata" #. module: base #: model:res.country,name:base.is @@ -11680,6 +12026,12 @@ msgid "" "=====================\n" " " msgstr "" +"\n" +"Acest modul adauga meniul probleme si caracteristici portalului " +"dumneavoastra daca sunt instalate project_issue si portal.\n" +"=============================================================================" +"=====================\n" +" " #. module: base #: view:res.lang:0 @@ -11689,7 +12041,7 @@ msgstr "%I - Ora (12 ore) [01,12]." #. module: base #: view:res.config:0 msgid "res_config_contents" -msgstr "res_config_continuturi" +msgstr "res_config_contents (res_config_continuturi)" #. module: base #: model:res.country,name:base.de @@ -11699,7 +12051,7 @@ msgstr "Germania" #. module: base #: model:ir.module.module,shortdesc:base.module_auth_oauth msgid "OAuth2 Authentication" -msgstr "" +msgstr "Autentificare OAuth2" #. module: base #: view:workflow:0 @@ -11710,6 +12062,12 @@ msgid "" "default value. If you don't do that, your customization will be overwrited " "at the next update or upgrade to a future version of OpenERP." msgstr "" +"Atunci cand personaizati un flux de lucru, asigurati-va ca nu modificati un " +"nod sau o sageata existenta, ci ca adaugati noduri sau sageti noi. Daca " +"trebuie neaparat sa modificati un nod sau o sageata, puteti modifica numai " +"campurile care sunt necompletate sau puteti seta valoarea implicita. Daca nu " +"faceti acest lucru, personalizarea dumneavoastra va fi suprascrisa la " +"urmatoarea actualizare sau upgrade a unei versiuni viitoare a lui OpenERP." #. module: base #: report:ir.module.reference:0 @@ -11729,7 +12087,7 @@ msgstr "" "\n" "Acest modul este pentru gestionarea unui mediu multicorporatist.\n" "=======================================================\n" -" \n" +"\n" "Acesta este modulul de baza pentru alte module multicorporatiste.\n" " " @@ -11742,7 +12100,7 @@ msgstr "Codul monedei trebuie sa fie unic per companie!" #: code:addons/base/module/wizard/base_export_language.py:39 #, python-format msgid "New Language (Empty translation template)" -msgstr "" +msgstr "Limba noua (sablon gol traducere)" #. module: base #: help:ir.actions.server,email:0 @@ -11751,9 +12109,9 @@ msgid "" "same values as for the condition field.\n" "Example: object.invoice_address_id.email, or 'me@example.com'" msgstr "" -"Expresie care intoarce edresa de e-mail la care se trimite e-mail. Poate fi " -"bazata pe aceleasi valori ca si pentru campul conditie.\n" -"Exemplu: obiect.factura_adresa_id.e-mail, sau 'me@exemplu.com'" +"Expresie care intoarce adresa de e-mail la trimite catre. Poate fi bazata pe " +"aceleasi valori ca si pentru campul conditie.\n" +"Exemplu: obiect.id_adresa_factura.e-mail, sau 'me@exemplu.com'" #. module: base #: model:ir.module.module,description:base.module_project_issue_sheet @@ -11773,7 +12131,7 @@ msgstr "" "Problemelor/Virusilor in Proiect.\n" "=============================================================================" "====\n" -" \n" +"\n" "Jurnalele de lucru pot fi pastrate pentru a indica numarul de ore petrecut " "de catre utilizatori pentru a se ocupa de o problema.\n" " " @@ -11812,7 +12170,7 @@ msgstr "Egipt" #. module: base #: view:ir.attachment:0 msgid "Creation" -msgstr "" +msgstr "Creare" #. module: base #: help:ir.actions.server,model_id:0 @@ -11843,11 +12201,11 @@ msgid "" "(default: 465)" msgstr "" "Alegeti planul conectarii codificate:\n" -"- Nici unul: Sesiunile SMTP sunt realizate in celartext.\n" +"- Nici unul: Sesiunile SMTP sunt realizate in cleartext.\n" "- TLS (STARTTLS): Codificarea TLS este solicitata la inceputul sesiunii SMTP " "(Recomandat)\n" "- SSL/TLS: Sesiunile SMTP sunt codificate cu SSL/TLS printr-un port dedicat " -")implicit: 465)" +"(implicit: 465)" #. module: base #: view:ir.model:0 @@ -11857,7 +12215,7 @@ msgstr "Descriere Campuri" #. module: base #: model:ir.module.module,shortdesc:base.module_analytic_contract_hr_expense msgid "Contracts Management: hr_expense link" -msgstr "" +msgstr "Managementul Contractelor: linkul hr_expense (ru_cheltuieli)" #. module: base #: view:ir.attachment:0 @@ -11871,12 +12229,12 @@ msgstr "" #: view:res.partner:0 #: view:workflow.activity:0 msgid "Group By..." -msgstr "Grupati dupa..." +msgstr "Grupeaza dupa..." #. module: base #: view:base.module.update:0 msgid "Module Update Result" -msgstr "" +msgstr "Rezultat Actualizare Modul" #. module: base #: model:ir.module.module,description:base.module_analytic_contract_hr_expense @@ -11887,21 +12245,26 @@ msgid "" "=============================================================================" "=========================\n" msgstr "" +"\n" +"Acs modul este pentru modificarea vizualizarii contului analitic pentru a " +"afisa niste date referitoare la modulul hr_expense (ru_cheltuieli).\n" +"=============================================================================" +"=========================\n" #. module: base #: field:ir.attachment,store_fname:0 msgid "Stored Filename" -msgstr "" +msgstr "Numele Fisierului Stocat" #. module: base #: field:res.partner,use_parent_address:0 msgid "Use Company Address" -msgstr "" +msgstr "Foloseste Adresa Companiei" #. module: base #: model:ir.module.module,summary:base.module_hr_holidays msgid "Holidays, Allocation and Leave Requests" -msgstr "" +msgstr "Cereri de Concediu, Alocare si Zile Libere" #. module: base #: model:ir.module.module,description:base.module_web_hello @@ -11911,6 +12274,10 @@ msgid "" "===========================\n" "\n" msgstr "" +"\n" +"Modul exemplu OpenERP Web.\n" +"===========================\n" +"\n" #. module: base #: selection:ir.module.module,state:0 @@ -11948,6 +12315,16 @@ msgid "" "

\n" " " msgstr "" +"\n" +" Faceti click pentru a adauga un contact in agenda " +"dumneavoastra.\n" +"

\n" +" OpenERP va ajuta sa tineti evidenta cu usurinta a tuturor " +"activitatilor asociate\n" +" unui furnizor: discutii, istoric al achizitiilor,\n" +" documente, etc.\n" +"

\n" +" " #. module: base #: model:res.country,name:base.lr @@ -11962,6 +12339,10 @@ msgid "" "=======================\n" "\n" msgstr "" +"\n" +"Test suita OpenERP Web.\n" +"=======================\n" +"\n" #. module: base #: view:ir.model:0 @@ -12030,7 +12411,7 @@ msgstr "" #. module: base #: model:ir.actions.report.xml,name:base.preview_report msgid "Preview Report" -msgstr "Previzualizare Raport" +msgstr "Previzualizeaza Raportul" #. module: base #: model:ir.module.module,shortdesc:base.module_purchase_analytic_plans @@ -12041,7 +12422,7 @@ msgstr "Planuri Analitice Achizitionare" #: model:ir.actions.act_window,name:base.ir_sequence_type #: model:ir.ui.menu,name:base.menu_ir_sequence_type msgid "Sequence Codes" -msgstr "Coduri secventa" +msgstr "Secventa Coduri" #. module: base #: selection:base.language.install,lang:0 @@ -12055,7 +12436,7 @@ msgid "" "individual wizards via the list of configuration wizards." msgstr "" "Toti asistentii de configurare aflati in asteptare au fost executati. Puteti " -"restarta asistentii individual prin lista de configurare asistenti." +"restarta asistentii individuali prin lista de configurare asistenti." #. module: base #: view:ir.sequence:0 @@ -12065,7 +12446,7 @@ msgstr "Anul curent cu Secol: %(an)s" #. module: base #: field:ir.exports,export_fields:0 msgid "Export ID" -msgstr "Export ID" +msgstr "ID Export" #. module: base #: model:res.country,name:base.fr @@ -12106,6 +12487,7 @@ msgstr "Campanie de Marketing - Demo" msgid "" "Can not create Many-To-One records indirectly, import the field separately" msgstr "" +"Nu pot fi create inregistrari Many-To-One indirect, importati campul separat" #. module: base #: field:ir.cron,interval_type:0 @@ -12115,7 +12497,7 @@ msgstr "Interval Unitate" #. module: base #: model:ir.module.module,shortdesc:base.module_portal_stock msgid "Portal Stock" -msgstr "" +msgstr "Portal Stoc" #. module: base #: field:workflow.activity,kind:0 @@ -12148,12 +12530,12 @@ msgstr "Data crearii" #. module: base #: model:ir.module.module,shortdesc:base.module_l10n_cn msgid "中国会计科目表 - Accounting" -msgstr "中国会计科目表 - Contabilitate" +msgstr "Plan de Conturi chinezesc - Contabilitate" #. module: base #: sql_constraint:ir.model.constraint:0 msgid "Constraints with the same name are unique per module." -msgstr "" +msgstr "Restrictiile cu acelari nume sunt unice per modul." #. module: base #: model:ir.module.module,description:base.module_report_intrastat @@ -12165,6 +12547,13 @@ msgid "" "This module gives the details of the goods traded between the countries of\n" "European Union." msgstr "" +"\n" +"Un modul care adauga rapoarte intrastat.\n" +"=====================================\n" +"\n" +"Acest modul ofera detalii cu privire la marfurile tranzactionate intre " +"tarile\n" +"Uniunii Europene." #. module: base #: help:ir.actions.server,loop_action:0 @@ -12198,12 +12587,12 @@ msgstr "res.cerere" #. module: base #: field:res.partner,image_medium:0 msgid "Medium-sized image" -msgstr "" +msgstr "Imagine de dimensiune mijlocie" #. module: base #: view:ir.model:0 msgid "In Memory" -msgstr "In memorie" +msgstr "In memoria" #. module: base #: view:ir.actions.todo:0 @@ -12213,7 +12602,7 @@ msgstr "De efectuat" #. module: base #: model:ir.module.module,shortdesc:base.module_product_visible_discount msgid "Prices Visible Discounts" -msgstr "Preturi Reduceri Vizibile" +msgstr "Reduceri Vizibile Preturi" #. module: base #: field:ir.attachment,datas:0 @@ -12225,7 +12614,7 @@ msgstr "Continut Fisier" #: view:ir.model.relation:0 #: model:ir.ui.menu,name:base.ir_model_relation_menu msgid "ManyToMany Relations" -msgstr "" +msgstr "Relatii ManyToMany" #. module: base #: model:res.country,name:base.pa @@ -12264,7 +12653,7 @@ msgstr "Insula Pitcairn" #. module: base #: field:res.partner,category_id:0 msgid "Tags" -msgstr "" +msgstr "Etichete" #. module: base #: view:base.module.upgrade:0 @@ -12284,7 +12673,7 @@ msgstr "Reguli Inregistrare" #. module: base #: view:multi_company.default:0 msgid "Multi Company" -msgstr "" +msgstr "Multi Companie" #. module: base #: model:ir.module.category,name:base.module_category_portal @@ -12295,13 +12684,13 @@ msgstr "Portal" #. module: base #: selection:ir.translation,state:0 msgid "To Translate" -msgstr "" +msgstr "De Tradus" #. module: base #: code:addons/base/ir/ir_fields.py:294 #, python-format msgid "See all possible values" -msgstr "" +msgstr "Vezi toate valorile posibile" #. module: base #: model:ir.module.module,description:base.module_claim_from_delivery @@ -12336,12 +12725,12 @@ msgstr "" #. module: base #: help:ir.model.constraint,name:0 msgid "PostgreSQL constraint or foreign key name." -msgstr "" +msgstr "Restrictie PostgreSQL sau nume cheie straina." #. module: base #: view:res.company:0 msgid "Click to set your company logo." -msgstr "" +msgstr "Faceti click pentru a configura sigla companiei." #. module: base #: view:res.lang:0 @@ -12363,12 +12752,12 @@ msgstr "Guineea Bissau" #. module: base #: field:ir.actions.report.xml,header:0 msgid "Add RML Header" -msgstr "" +msgstr "Adauga Antet RML" #. module: base #: help:res.company,rml_footer:0 msgid "Footer text displayed at the bottom of all reports." -msgstr "" +msgstr "Textul subsol afisat in partea de jos a tuturor rapoartelor." #. module: base #: field:ir.module.module,icon:0 @@ -12378,7 +12767,7 @@ msgstr "Pictograma URL" #. module: base #: model:ir.module.module,shortdesc:base.module_note_pad msgid "Memos pad" -msgstr "" +msgstr "Bloc notes" #. module: base #: model:ir.module.module,description:base.module_pad @@ -12392,11 +12781,19 @@ msgid "" "pads (by default, http://ietherpad.com/).\n" " " msgstr "" +"\n" +"Adauga suport marit pentru atasamentele (Ether)Pad in clientul web.\n" +"===================================================================\n" +"\n" +"Permite companiei sa personalizeze instalatia Pad care ar trebui folosita " +"pentru a se conecta\n" +"la pad-uri noi (implicit, http://ietherpad.com/).\n" +" " #. module: base #: sql_constraint:res.lang:0 msgid "The code of the language must be unique !" -msgstr "Codul limbii trebuie sa fie unic!" +msgstr "Codul limbii trebuie sa fie unic !" #. module: base #: model:ir.actions.act_window,name:base.action_attachment @@ -12430,7 +12827,7 @@ msgstr "Alte actiuni" #. module: base #: model:ir.module.module,shortdesc:base.module_l10n_be_coda msgid "Belgium - Import Bank CODA Statements" -msgstr "" +msgstr "Belgia - Importa Extrase de cont CODA" #. module: base #: selection:ir.actions.todo,state:0 @@ -12483,7 +12880,7 @@ msgstr "Italia" #. module: base #: model:res.groups,name:base.group_sale_salesman msgid "See Own Leads" -msgstr "" +msgstr "Vezi Propriile Piste" #. module: base #: view:ir.actions.todo:0 @@ -12494,7 +12891,7 @@ msgstr "De efectuat" #. module: base #: model:ir.module.module,shortdesc:base.module_portal_hr_employees msgid "Portal HR employees" -msgstr "" +msgstr "Portal RU angajati" #. module: base #: selection:base.language.install,lang:0 @@ -12523,6 +12920,8 @@ msgid "" "Insufficient fields to generate a Calendar View for %s, missing a date_stop " "or a date_delay" msgstr "" +"Campuri insuficiente pentru generarea unei Vizualizari Calendar pentru %s, " +"lipseste stop_data sau intarziere_data" #. module: base #: field:workflow.activity,action:0 @@ -12577,6 +12976,17 @@ msgid "" "associated to every resource. It also manages the leaves of every resource.\n" " " msgstr "" +"\n" +"Modul pentru mangementul resurselor.\n" +"===============================\n" +"\n" +"O resursa reprezinta ceva care poate fi programat (un dezvoltator pe o " +"sarcina sau un\n" +"centru de lucru pe comenzi de productie). Acest modul gestioneaza un " +"calendar de resurse\n" +"asociat fiecarei resurse. De asemenea, gestioneaza concediile fiecarei " +"resurse.\n" +" " #. module: base #: model:ir.module.module,description:base.module_account_followup @@ -12614,6 +13024,8 @@ msgstr "" msgid "" "Please contact your system administrator if you think this is an error." msgstr "" +"Va rugam sa va contactati administratorul de sistem daca credeti ca aceasta " +"este o eroare." #. module: base #: code:addons/base/module/module.py:546 @@ -12621,7 +13033,7 @@ msgstr "" #: model:ir.actions.act_window,name:base.action_view_base_module_upgrade #, python-format msgid "Apply Schedule Upgrade" -msgstr "" +msgstr "Aplica Programeaza Actualizarea" #. module: base #: view:workflow.activity:0 @@ -12661,6 +13073,8 @@ msgid "" "One of the documents you are trying to access has been deleted, please try " "again after refreshing." msgstr "" +"Unul dintre documentele pe care incercati sa le accesati a fost sters, va " +"rugam sa incercati din nou dupa ce dati refresh." #. module: base #: model:ir.model,name:base.model_ir_mail_server @@ -12680,11 +13094,11 @@ msgid "" "the bounds of global ones. The first group rules restrict further than " "global rules, but any additional group rule will add more permissions" msgstr "" -"Regulile globale (nespecifice unui grup) sunt restrictiile si nu pot fi " -"evitate. Regulile locale ale grupului ofera permisiuni suplimentare, dar " -"sunt constranse in limitele celor globale. Regulile primului grup limiteaza " -"mai miult decat regulile globale, dar orice regula suplimentara a grupului " -"va adauga mai multe permisiuni" +"Regulile globale (nespecifice unui grup) sunt restrictii si nu pot fi " +"evitate. Regulile grupului local ofera permisiuni suplimentare, dar sunt " +"constranse in limitele celor globale. Regulile primului grup limiteaza mai " +"mult decat regulile globale, dar orice regula suplimentara a grupului va " +"adauga mai multe permisiuni" #. module: base #: field:res.currency.rate,rate:0 @@ -12699,7 +13113,7 @@ msgstr "Congo" #. module: base #: view:res.lang:0 msgid "Examples" -msgstr "Exemplu" +msgstr "Exemple" #. module: base #: field:ir.default,value:0 @@ -12762,7 +13176,7 @@ msgstr "" #. module: base #: model:ir.module.module,shortdesc:base.module_sale_stock msgid "Sales and Warehouse Management" -msgstr "" +msgstr "Managementul Vanzarilor si al Depozitului" #. module: base #: model:ir.module.module,description:base.module_hr_recruitment @@ -12783,6 +13197,22 @@ msgid "" "You can define the different phases of interviews and easily rate the " "applicant from the kanban view.\n" msgstr "" +"\n" +"Gestioneaza locurile de munca si procesul de recrutare\n" +"================================================\n" +"\n" +"Aceasta aplicatie va permite sa tineti evidenta cu usurinta a locurilor de " +"munca, a posturilor libere, a aplicatiilor, a interviurilor...\n" +"\n" +"Este integrat cu mail gateway pentru a gasi automat email-urile trimise la " +" (locuri_de_munca@companiadumneavoastra.com) din lista " +"aplicatiilor. De asemenea, este integrat cu sistemul de management al " +"documentelor pentru a stoca si a cauta in baza de CV-uri si a gasi " +"candidatul pe care il cautati. De asemenea, este integrat cu modulul de " +"sondaje pentru a va permite sa definiti interviuri pentru diferite locuri de " +"munca.\n" +"Puteti defini diferitele etape ale interviurilor si puteti evalua candidatul " +"cu usurinta din vizualizarea kanban.\n" #. module: base #: field:ir.model.fields,model:0 @@ -12809,7 +13239,7 @@ msgstr "Nu este instalat(a)" #: view:workflow.activity:0 #: field:workflow.activity,out_transitions:0 msgid "Outgoing Transitions" -msgstr "Tranzitii iesire" +msgstr "Tranzitii de iesire" #. module: base #: field:ir.module.module,icon_image:0 @@ -12823,14 +13253,14 @@ msgid "" "Helps you manage your human resources by encoding your employees structure, " "generating work sheets, tracking attendance and more." msgstr "" -"Va ajuta sa va gestionati resurse umane prin inregistrarea structurii " +"Va ajuta sa va gestionati resursele umane prin inregistrarea structurii " "angajatilor d-voastra, generarea de pontaje, tinerea evidentei prezentei si " "multe altele." #. module: base #: help:res.partner,ean13:0 msgid "BarCode" -msgstr "" +msgstr "Cod de bare" #. module: base #: help:ir.model.fields,model_id:0 @@ -12852,6 +13282,7 @@ msgstr "Martinica (Franceza)" #: help:res.partner,is_company:0 msgid "Check if the contact is a company, otherwise it is a person" msgstr "" +"Bifati daca contactul este o companie, in caz contrar este o persoana" #. module: base #: view:ir.sequence.type:0 @@ -12861,13 +13292,13 @@ msgstr "Tip Secvente" #. module: base #: view:res.partner:0 msgid "Mobile:" -msgstr "" +msgstr "Mobil:" #. module: base #: code:addons/base/res/res_bank.py:195 #, python-format msgid "Formating Error" -msgstr "" +msgstr "Eroare de formatare" #. module: base #: model:res.country,name:base.ye @@ -12906,7 +13337,7 @@ msgstr "" "achizitiile, marjele si alti indicatori interesanti bazati pe facturi.\n" "=============================================================================" "================================================\n" -" \n" +"\n" "Wizardul pentru lansarea raportului are mai multe optiuni pentru a va ajuta " "sa obtineti datele de care aveti nevoie.\n" @@ -12936,7 +13367,7 @@ msgstr "" #: code:addons/base/ir/ir_model.py:1024 #, python-format msgid "Permission Denied" -msgstr "" +msgstr "Permisiune refuzata" #. module: base #: field:ir.ui.menu,child_id:0 @@ -13007,12 +13438,12 @@ msgstr "E-mail" #. module: base #: model:res.partner.category,name:base.res_partner_category_12 msgid "Office Supplies" -msgstr "" +msgstr "Echipament de birou" #. module: base #: field:ir.attachment,res_model:0 msgid "Resource Model" -msgstr "" +msgstr "Model de Resursa" #. module: base #: code:addons/custom.py:555 @@ -13069,7 +13500,7 @@ msgstr "" #. module: base #: model:res.country,name:base.bl msgid "Saint Barthélémy" -msgstr "" +msgstr "Saint Barthélémy" #. module: base #: selection:ir.module.module,license:0 @@ -13089,12 +13520,12 @@ msgstr "flux de lucru" #. module: base #: view:ir.rule:0 msgid "Read Access Right" -msgstr "" +msgstr "Citeste Dreptul de Acces" #. module: base #: model:ir.module.module,shortdesc:base.module_analytic_user_function msgid "Jobs on Contracts" -msgstr "Locuri de munca cu Contracte" +msgstr "" #. module: base #: code:addons/base/res/res_lang.py:187 @@ -13120,7 +13551,7 @@ msgstr "In serie" #. module: base #: model:ir.module.module,summary:base.module_crm msgid "Leads, Opportunities, Phone Calls" -msgstr "" +msgstr "Piste, Oportunitati, Apeluri telefonice" #. module: base #: model:ir.module.category,description:base.module_category_knowledge_management @@ -13139,13 +13570,13 @@ msgstr "Araba / الْعَرَبيّة" #. module: base #: selection:ir.translation,state:0 msgid "Translated" -msgstr "" +msgstr "Tradus" #. module: base #: model:ir.actions.act_window,name:base.action_inventory_form #: model:ir.ui.menu,name:base.menu_action_inventory_form msgid "Default Company per Object" -msgstr "" +msgstr "Compania Implicita per Obiect" #. module: base #: model:ir.module.module,shortdesc:base.module_web_hello @@ -13160,7 +13591,7 @@ msgstr "Urmatorul Pas de Configurare" #. module: base #: field:res.groups,comment:0 msgid "Comment" -msgstr "Observatie" +msgstr "Comentariu" #. module: base #: field:ir.filters,domain:0 @@ -13175,7 +13606,7 @@ msgstr "Domeniu" #: code:addons/base/ir/ir_fields.py:166 #, python-format msgid "Use '1' for yes and '0' for no" -msgstr "" +msgstr "Foloseste '1' pentru da si '0' pentru nu" #. module: base #: model:ir.module.module,shortdesc:base.module_marketing_campaign @@ -13190,18 +13621,18 @@ msgstr "Numele starii" #. module: base #: help:ir.attachment,type:0 msgid "Binary File or URL" -msgstr "" +msgstr "Fisier Binar sau URL" #. module: base #: code:addons/base/ir/ir_fields.py:313 #, python-format msgid "Invalid database id '%s' for the field '%%(field)s'" -msgstr "" +msgstr "Id '%s' nevalid al bazei de date pentru campul '%%(field)s'" #. module: base #: view:res.lang:0 msgid "Update Languague Terms" -msgstr "Actualizare Termeni Limbaj" +msgstr "Actualizeaza Termenii Lingvistici" #. module: base #: field:workflow.activity,join_mode:0 @@ -13236,7 +13667,7 @@ msgstr "" #. module: base #: view:ir.filters:0 msgid "Shared" -msgstr "" +msgstr "Partajat(e)" #. module: base #: code:addons/base/module/module.py:357 @@ -13245,12 +13676,12 @@ msgid "" "Unable to install module \"%s\" because an external dependency is not met: %s" msgstr "" "Modulul \"%s\" nu poate fi instalat deoarece nu este indeplinita o " -"dependenta externe: %s" +"dependenta externa: %s" #. module: base #: view:ir.module.module:0 msgid "Search modules" -msgstr "Cautati module" +msgstr "Cauta module" #. module: base #: model:res.country,name:base.by @@ -13313,6 +13744,9 @@ msgid "" "Allow users to sign up through OAuth2 Provider.\n" "===============================================\n" msgstr "" +"\n" +"Permite utilizatorilor sa se conecteze prin OAuth2 Provider.\n" +"===============================================\n" #. module: base #: field:change.password.user,user_id:0 @@ -13335,12 +13769,12 @@ msgstr "Puerto Rico" #. module: base #: model:ir.module.module,shortdesc:base.module_web_tests_demo msgid "Demonstration of web/javascript tests" -msgstr "" +msgstr "Demonstratie a testelor web/javascript" #. module: base #: field:workflow.transition,signal:0 msgid "Signal (Button Name)" -msgstr "" +msgstr "Semnal (Numele Butonului)" #. module: base #: view:ir.actions.act_window:0 @@ -13370,7 +13804,7 @@ msgstr "Grenada" #. module: base #: help:res.partner,customer:0 msgid "Check this box if this contact is a customer." -msgstr "" +msgstr "Bifati aceasta casuta daca acest contact este un client." #. module: base #: view:ir.actions.server:0 @@ -13380,7 +13814,7 @@ msgstr "Declansati Configurarea" #. module: base #: view:base.language.install:0 msgid "Load" -msgstr "Incarcare" +msgstr "Incarca" #. module: base #: model:ir.module.module,description:base.module_warning @@ -13398,7 +13832,7 @@ msgstr "" "Modul pentru declansarea avertizarilor in obiectele OpenERP.\n" "==============================================\n" " \n" -"Mesajele de avertizarevor fi afisate pentru obiecte precum comanda de " +"Mesajele de avertizare vor fi afisate pentru obiecte precum comanda de " "vanzare, comanda de achizitie,\n" "ridicare si facturare. Mesajul este declansat de formularul evenimentului.\n" " " @@ -13406,7 +13840,7 @@ msgstr "" #. module: base #: field:res.users,partner_id:0 msgid "Related Partner" -msgstr "" +msgstr "Partener Asociat" #. module: base #: code:addons/osv.py:153 @@ -13428,6 +13862,15 @@ msgid "" "\n" " " msgstr "" +"\n" +"Plan de conturi panamez si localizare taxe.\n" +"\n" +"Plan de conturi panamez si impozite conform dispozitiilor in vigoare\n" +"\n" +"Cu participarea \n" +"- AHMNET CORP http://www.ahmnet.com\n" +"\n" +" " #. module: base #: code:addons/base/ir/ir_model.py:293 @@ -13443,24 +13886,24 @@ msgstr "Operatiuni de productie" #. module: base #: view:base.language.export:0 msgid "Here is the exported translation file:" -msgstr "" +msgstr "Iata fisierul traducerii exportate:" #. module: base #: field:ir.actions.report.xml,report_rml_content:0 #: field:ir.actions.report.xml,report_rml_content_data:0 msgid "RML Content" -msgstr "" +msgstr "Continut RML" #. module: base #: view:res.lang:0 msgid "Update Terms" -msgstr "Actualizati termenii" +msgstr "Actualizeaza Termenii" #. module: base #: field:res.request,act_to:0 #: field:res.request.history,act_to:0 msgid "To" -msgstr "Catre" +msgstr "Destinatar" #. module: base #: model:ir.module.module,shortdesc:base.module_hr @@ -13579,6 +14022,102 @@ msgid "" "from Gate A\n" " " msgstr "" +"\n" +"Acest modul suplimenteaza aplicatia Depozit prin implementarea eficienta a " +"fluxurilor de inventar Push (Impingere) si Pull (Tragere).\n" +"=============================================================================" +"===============================\n" +"\n" +"De obicei, acest modul poate fi folosit pentru a:\n" +"--------------------------------\n" +" * Gestiona lanturile de productie a produselor\n" +" * Gestiona locatiile prestabilite pentru fiecare produs\n" +" * Defini rute in cadrul depozitului dumneavoastra in functie de nevoile " +"afacerii, cum ar fi:\n" +" - Controlul Calitatii\n" +" - Servicii Post Vanzare\n" +" - Retur Furnizor\n" +"\n" +" * Ajuta managementul inchirierilor, prin generarea miscarilor automate " +"de returnare a produselor inchiriate\n" +"\n" +"Odata instalat acest modul, un tab suplimentar apare in formularul " +"produsului,\n" +"unde puteti adauga specificatiile fluxului Push si Pull. Datele demo ale " +"produsului\n" +"CPU1 pentru acel flux push/pull :\n" +"\n" +"Fluxurile Push:\n" +"-----------\n" +"Fluxurile Push sunt utile atunci cand sosirea anumitor produse intr-o " +"anumita locatie\n" +"ar trebui sa fie urmata intotdeauna de o miscare corespunzatoare intr-o alta " +"locatie, optional\n" +"dupa un anumit interval de timp. Aplicatia originala Depozit accepta deja " +"astfel\n" +"de specificatii ale fluxului Push in Locatiile propriu zise, dar acestea nu " +"pot fi\n" +"definite pentru fiecare produs.\n" +"\n" +"O sepcificatie a fluxului push indica locatiile care sunt legate intre ele,\n" +"si cu ce parametri. De indata ce o anumita cantitate de produse este mutata " +"in\n" +"locatia sursa, o miscare inlantuita este prevazuta automat in conformitate " +"cu\n" +"parametrii stabiliti in caietul de sarcini (locatia destinatie, intarzierea, " +"tipul de miscare,\n" +"registrul). Noua miscare poate fi procesata automat, sau poate necesita o " +"confirmare\n" +"manuala, in functie de parametri.\n" +"\n" +"Fluxurile Pull:\n" +"-----------\n" +"Fluxurile Pull sunt putin diferite fata de fluxurile Push, in sensul ca " +"acestea nu sunt\n" +"legate de procesarea miscarilor produselor, ci mai degraba de procesarea\n" +"comenzilor de achizitii. Ceea ce este tras este o necesitate, nu direct " +"produse. Un\n" +"exemplu clasic de flux Pull (de tragere) este atunci cand aveti o piata de " +"desfacere, cu o companie principala\n" +"care este responsabila cu aprovizionarea pietei de desfacere.\n" +"\n" +" [ Client ] <- A - [ Piata de desfacere ] <- B - [ Companie ] <~ C ~ [ " +"Furnizor ]\n" +"\n" +"Atunci cand o comanda de achizitii (A, provenita din confirmarea unui Ordin " +"de Vanzare,\n" +"de exemplu) ajunge in Outlet, este transformata intr-o alta achizitie\n" +"(B, prin intermediul unui flux Pull de tipul 'mutare') solicitata de " +"Compania principala. Atunci cand comanda\n" +"de achizitie B este procesata de catre Compania principala, si daca produsul " +"nu se afla in stoc,\n" +"aceasta poate fi transformata intr-un Ordin de Achizitie (C) de la Furnizor " +"(flux Pull de tipul\n" +"Achizitie). Rezultatul este ca ordinul de achizitie, necesitatea, este " +"impins\n" +"pana la capat intre Client si Furnizor.\n" +"\n" +"Tehnic vorbind, procesele tehnologice Pull permit procesarea comenzilor de " +"achizitie in mod diferit, nu doar\n" +"in functie de produsul despre care este vorba, ci si in functie de\n" +"locatia care detine 'nevoia' pentru acel produs (adicalocatia destinatie a\n" +"acelei comenzi de achizitie).\n" +"\n" +"Utilizare-Caz:\n" +"---------\n" +"\n" +"Puteti folosi datele demonstrative dupa cum urmeaza:\n" +"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +" **CPU1:** Vindeti niste CPU1 din Magazinul din Chicago si rulati " +"programatorul\n" +" - Depozit: ordin de livrare, Magazinul din Chicago: primire\n" +" **CPU3:**\n" +" - Atunci cand primiti produsul, acesta ajunge in locatia Controlul " +"Calitatii, apoi\n" +" este stocat pe raftul 2.\n" +" - Atunci cand livrati produsul clientului: Lista de Ridicare -> " +"Ambalare -> Ordin de Livrare de la Poarta A\n" +" " #. module: base #: model:ir.module.module,description:base.module_decimal_precision @@ -13591,6 +14130,13 @@ msgid "" "\n" "The decimal precision is configured per company.\n" msgstr "" +"\n" +"Configureaza exactitatea pretului de care aveti nevoie pentru diferite " +"tipuri de utilizare: contabilitate, vanzari, achizitii.\n" +"=============================================================================" +"====================\n" +"\n" +"Precizia zecimala este configurata pentru fiecare companie.\n" #. module: base #: selection:res.company,paper_format:0 @@ -13600,7 +14146,7 @@ msgstr "A4" #. module: base #: view:res.config.installer:0 msgid "Configuration Installer" -msgstr "" +msgstr "Configurare Instalare" #. module: base #: field:res.partner,customer:0 @@ -13620,6 +14166,11 @@ msgid "" "===================================================\n" " " msgstr "" +"\n" +"Acest modul adauga o PLATFORMA la toate vizualizarile kanban ale " +"proiectelor.\n" +"===================================================\n" +" " #. module: base #: field:ir.actions.act_window,context:0 @@ -13686,17 +14237,19 @@ msgid "" "(if you delete a native ACL, it will be re-created when you reload the " "module." msgstr "" +"Daca debifati campul activ, va dezactiva ACL fara a-l sterge (daca stergeti " +"un ACL original, va fi recreat atunci cand reincarcati modulul)." #. module: base #: model:ir.model,name:base.model_ir_fields_converter msgid "ir.fields.converter" -msgstr "" +msgstr "ir.convertor.campuri" #. module: base #: code:addons/base/res/res_partner.py:439 #, python-format msgid "Couldn't create contact without email address !" -msgstr "" +msgstr "Contactul nu a putut fi creat fara adresa de email !" #. module: base #: model:ir.module.category,name:base.module_category_manufacturing @@ -13718,12 +14271,12 @@ msgstr "Anulati Instalarea" #. module: base #: model:ir.model,name:base.model_ir_model_relation msgid "ir.model.relation" -msgstr "" +msgstr "ir.model.relatie" #. module: base #: model:ir.module.module,shortdesc:base.module_account_check_writing msgid "Check Writing" -msgstr "" +msgstr "Bifeaza Scriere" #. module: base #: model:ir.module.module,description:base.module_plugin_outlook @@ -13741,11 +14294,23 @@ msgid "" "into mail.message with attachments.\n" " " msgstr "" +"\n" +"Acest modul ofera Aplicatia Outlook.\n" +"=========================================\n" +"\n" +"Aplicatia Outlook va permite sa selectati un obiect pe care doriti sa il " +"adaugati la\n" +"email-ul dumneavoastra si la atasamentele acestuia din MS Outlook. Puteti " +"selecta un partener, o sarcina,\n" +"un proiect, un cont analitic, sau orice alt obiect si puteti arhiva email-ul " +"selectat\n" +"in mail.mesaj cu atasamente.\n" +" " #. module: base #: model:ir.module.module,shortdesc:base.module_l10n_bo msgid "Bolivia Localization Chart Account" -msgstr "" +msgstr "Plan de Conturi si Localizare boliviana" #. module: base #: model:ir.module.module,description:base.module_plugin_thunderbird @@ -13765,21 +14330,21 @@ msgstr "" "Acest modul este necesar pentru ca Aplicatia Thunderbird sa functioneze cum " "trebuie.\n" "====================================================================\n" -" \n" +"\n" "Aplicatia va permite sa arhivati e-mail-uri si atasamentele lor in obiectele " -"selectate\n" -"OpenERP. Puteti selecta un partener, o sarcina, un proiect, un cont\n" +"OpenERP\n" +"selectate. Puteti selecta un partener, o sarcina, un proiect, un cont\n" "analitic, sau orice alt obiect si sa atasati e-mail-ul selectat ca un fisier " ".eml in\n" -"atasamentul unei inregistrri selectate. Puteti crea documente pentru Pista " +"atasamentul unei inregistrari selectate. Puteti crea documente pentru Pista " "MRC,\n" -"Candidatul RU si Problema Proiectului din e-mail-urile selectate.\n" +"RU Candidat si Problema Proiectului din e-mail-urile selectate.\n" " " #. module: base #: view:res.lang:0 msgid "Legends for Date and Time Formats" -msgstr "Legende pentru formatele de Data si Ora" +msgstr "Legende pentru formatele Data si Ora" #. module: base #: selection:ir.actions.server,state:0 @@ -13789,7 +14354,7 @@ msgstr "Copiati Obiectul" #. module: base #: field:ir.actions.server,trigger_name:0 msgid "Trigger Signal" -msgstr "Semnal declansare" +msgstr "Declanseaza Semnalul" #. module: base #: model:ir.actions.act_window,name:base.action_country_state @@ -13847,6 +14412,38 @@ msgid "" "Some statistics by journals are provided.\n" " " msgstr "" +"\n" +"Modulele registrului de vanzari va permite sa va clasificati vanzarile si " +"livrarile (liste de ridicare) dintre registre diferite.\n" +"=============================================================================" +"===========================================\n" +"\n" +"Acest modul este foarte util pentru companiile mari care functioneaza pe " +"departamente.\n" +"\n" +"Puteti folosi registrul in diferite scopuri, de exemplu:\n" +"----------------------------------------------------------\n" +"\n" +" * izolarea vanzarilor diferitelor departamente\n" +" * registre pentru livrarile prin curierat sau prin posta\n" +"\n" +"Registrele au un responsabil si evolueaza de la o stare la alta:\n" +"-----------------------------------------------------------------\n" +" * ciorna, deschis, anulat, efectuat.\n" +"\n" +"Operatiunile in serie pot fi procesate in registre diferite pentru a " +"confirma toate vanzarile\n" +"odata, pentru a valida sau factura ambalarea.\n" +"\n" +"Suporta, de asemenea, metode de facturare in serie care pot fi configurate " +"de catre parteneri si comenzi de vanzare, de exemplu:\n" +"-----------------------------------------------------------------------------" +"--------------------------\n" +" * facturare zilnica\n" +" * facturare lunara\n" +"\n" +"Sunt furnizate si niste statistici despre registre.\n" +" " #. module: base #: code:addons/base/ir/ir_mail_server.py:469 @@ -13905,7 +14502,7 @@ msgstr "Minut: %(min)s" #. module: base #: model:ir.ui.menu,name:base.menu_ir_cron msgid "Scheduler" -msgstr "Planificare" +msgstr "Planificator" #. module: base #: model:ir.module.module,description:base.module_event_moodle @@ -13953,6 +14550,50 @@ msgid "" "\n" "**PASSWORD:** ${object.moodle_user_password}\n" msgstr "" +"\n" +"Configurati serverul moodle (Modular Object-Oriented Dynamic Learning " +"Environment= Mediu de Invatare Dinamic si Modular Orientat spre Obiect).\n" +"============================= \n" +"\n" +"Cu acest modul puteti sa conectati OpenERP la o platforma moodle.\n" +"Acest modul va crea automat cursuri si studenti in platforma dumneavoastra " +"moodle \n" +"pentru a evita irosirea timpului.\n" +"Astfel aveti o modalitate simpla de a crea perfectionare sau cursuri cu " +"OpenERP si moodle.\n" +"\n" +"PASII CARE TREBUIE CONFIGURATI:\n" +"-------------------\n" +"\n" +"1. Activati serviciul web in moodle.\n" +"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +">administrare website >aplicatii >servicii web >gestioneaza protocoalelor " +"activeaza serviciul web xmlrpc \n" +"\n" +"\n" +">administrare website >aplicatii >servicii web >gestioneaza simboluri " +"creeaza un simbol \n" +"\n" +"\n" +">administrare website >aplicatii >servicii web >activeaza privire generala " +"serviciu web\n" +"\n" +"\n" +"2. Creati email-ul de confirmare cu autentificare si parola.\n" +"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +"Va recomandam sa adaugati urmatoarele linii in partea de jos a email-ului " +"de\n" +"confirmare a evenimentului dumneavoastra pentru a comunica abonatilor " +"autentificarea /parola platformei moodle.\n" +"\n" +"........textul de configurare.......\n" +"\n" +"*URL:** legatura dumneavoastra moodle, de exemplu: " +"http://openerp.moodle.com\n" +"\n" +"**AUTENTIFICARE:** ${obiect.nume_de_utilizator_moodle}\n" +" \n" +"**PAROLA:** ${obiect.parola_utilizator_moodle}\n" #. module: base #: model:ir.module.module,shortdesc:base.module_l10n_uk @@ -13962,7 +14603,7 @@ msgstr "Marea Britanie - Contabilitate" #. module: base #: model:res.partner.title,shortcut:base.res_partner_title_madam msgid "Mrs." -msgstr "" +msgstr "D-na" #. module: base #: code:addons/base/ir/ir_model.py:426 @@ -13984,6 +14625,7 @@ msgstr "Ref. utilizator" #, python-format msgid "'%s' does not seem to be a valid datetime for field '%%(field)s'" msgstr "" +"'%s' nu pare a fi o data si o ora valabila pentru campul '%%(field)s'" #. module: base #: model:res.partner.bank.type.field,name:base.bank_normal_field_bic @@ -13998,7 +14640,7 @@ msgstr "Expresie bucla" #. module: base #: model:res.partner.category,name:base.res_partner_category_16 msgid "Retailer" -msgstr "" +msgstr "Comerciant cu amanuntul" #. module: base #: view:ir.model.fields:0 @@ -14020,7 +14662,7 @@ msgstr "Argumentele care vor fi transmise metodei, de ex. (uid,)." #. module: base #: report:ir.module.reference:0 msgid "Reference Guide" -msgstr "Ghidul referintelor" +msgstr "Ghid de referinta" #. module: base #: model:ir.model,name:base.model_res_partner @@ -14047,6 +14689,8 @@ msgstr "" msgid "" "Please make sure no workitems refer to an activity before deleting it!" msgstr "" +"Asigurati-va ca niciun element de lucru nu se refera la vreo activitate " +"inainte de a o sterge!" #. module: base #: model:res.country,name:base.tr @@ -14093,7 +14737,7 @@ msgstr "4. %b, %B ==> Dec, Decembrie" #. module: base #: model:ir.module.module,shortdesc:base.module_l10n_cl msgid "Chile Localization Chart Account" -msgstr "" +msgstr "Localizare Plan de Conturi Chile" #. module: base #: selection:base.language.install,lang:0 @@ -14129,7 +14773,7 @@ msgstr "Informatii Conexiune" #. module: base #: model:res.partner.title,name:base.res_partner_title_prof msgid "Professor" -msgstr "" +msgstr "Profesor" #. module: base #: model:res.country,name:base.hm @@ -14154,12 +14798,12 @@ msgstr "Referinta Vizualizare" #: model:ir.module.category,description:base.module_category_sales_management msgid "Helps you handle your quotations, sale orders and invoicing." msgstr "" -"Va ajuta sa vagestionati cotatiile, comenzile de vanzare si facturarea." +"Va ajuta sa va gestionati cotatiile, comenzile de vanzare si facturarea." #. module: base #: field:res.users,login_date:0 msgid "Latest connection" -msgstr "" +msgstr "Cea mai recenta conexiune" #. module: base #: field:res.groups,implied_ids:0 @@ -14176,7 +14820,7 @@ msgstr "Selectie" #: model:ir.actions.act_window,name:base.change_password_wizard_action #: view:res.users:0 msgid "Change Password" -msgstr "" +msgstr "Schimba Parola" #. module: base #: model:ir.module.module,description:base.module_l10n_es @@ -14196,6 +14840,20 @@ msgid "" "yearly\n" " account reporting (balance, profit & losses).\n" msgstr "" +"\n" +"Planuri de Conturi spaniole (PGCE 2008).\n" +"=======================================\n" +"\n" +" * Defineste urmatoarele sabloane ale planului de conturi:\n" +" * Plan de Conturi general spaniol 2008\n" +" * Plan de Conturi general spaniol 2008 pentru companiile mici si " +"mijlocii\n" +" * Defineste sabloane pentru TVA vanzari si achizitii\n" +" * Defineste sabloane ale codului fiscal\n" +"\n" +"**Nota:** Ar trebui sa instalati modulul l10n_ES_raport_sold_cont pentru " +"raportul\n" +" anual al contului (sold, profit & pierderi).\n" #. module: base #: field:ir.actions.act_url,type:0 @@ -14244,7 +14902,7 @@ msgstr "Binar" #. module: base #: model:res.partner.title,name:base.res_partner_title_doctor msgid "Doctor" -msgstr "" +msgstr "Doctor" #. module: base #: model:ir.module.module,description:base.module_mrp_repair @@ -14262,11 +14920,24 @@ msgid "" " * Repair quotation report\n" " * Notes for the technician and for the final customer\n" msgstr "" +"\n" +"Scopul este de a avea un modul complet pentru gestionarea tuturor " +"reparatiilor produselor.\n" +"====================================================================\n" +"\n" +"Acest modul acopera urmatoarele subiecte:\n" +"------------------------------------------------------\n" +" * Adauga/sterge produse in reparatie\n" +" * Impactul pentru stocuri\n" +" * Facturare (produse si/sau servicii)\n" +" * Conceptul de garantie\n" +" * Raport cotatii reparatii\n" +" * Note pentru tehnician si pentru clientul final\n" #. module: base #: model:res.country,name:base.cd msgid "Congo, Democratic Republic of the" -msgstr "" +msgstr "Republica Democrata Congo" #. module: base #: model:res.country,name:base.cr @@ -14301,7 +14972,7 @@ msgstr "Alti Parteneri" #: view:workflow.workitem:0 #: field:workflow.workitem,state:0 msgid "Status" -msgstr "" +msgstr "Stare" #. module: base #: model:ir.actions.act_window,name:base.action_currency_form @@ -14313,17 +14984,17 @@ msgstr "Valute" #. module: base #: model:res.partner.category,name:base.res_partner_category_8 msgid "Consultancy Services" -msgstr "" +msgstr "Servicii de Consultanta" #. module: base #: help:ir.values,value:0 msgid "Default value (pickled) or reference to an action" -msgstr "Valoarea implicita (decapata) sau referinta unei actiuni" +msgstr "Valoarea implicita (decapata) sau trimiterea la o actiune" #. module: base #: field:ir.actions.report.xml,auto:0 msgid "Custom Python Parser" -msgstr "" +msgstr "Despartitor personalizat Python" #. module: base #: sql_constraint:res.groups:0 @@ -14333,7 +15004,7 @@ msgstr "Numele grupului trebuie sa fie unic !" #. module: base #: help:ir.translation,module:0 msgid "Module this term belongs to" -msgstr "" +msgstr "Modulul caruia ii apartine acest termen" #. module: base #: model:ir.module.module,description:base.module_web_view_editor @@ -14344,6 +15015,11 @@ msgid "" "\n" " " msgstr "" +"\n" +"OpenERP Web pentru a edita vizualizari.\n" +"==========================\n" +"\n" +" " #. module: base #: view:ir.sequence:0 @@ -14387,6 +15063,13 @@ msgid "" "\n" " " msgstr "" +"\n" +"Plan de conturi argentinian si localizare taxe.\n" +"==================================================\n" +"\n" +"Plan de conturi argentinian si impozite conform legislatiei in vigoare\n" +"\n" +" " #. module: base #: code:addons/fields.py:130 @@ -14423,7 +15106,7 @@ msgstr "Aprovizionari" #. module: base #: model:res.partner.category,name:base.res_partner_category_6 msgid "Bronze" -msgstr "" +msgstr "Bronz" #. module: base #: model:ir.module.module,shortdesc:base.module_hr_payroll_account @@ -14433,7 +15116,7 @@ msgstr "Contabilitate Salarizare" #. module: base #: view:res.users:0 msgid "Change password" -msgstr "Schimbă parola" +msgstr "Schimba parola" #. module: base #: model:res.country,name:base.sr @@ -14453,12 +15136,12 @@ msgstr "Luna crearii" #. module: base #: field:ir.module.module,demo:0 msgid "Demo Data" -msgstr "" +msgstr "Date Demonstrative" #. module: base #: model:res.partner.title,shortcut:base.res_partner_title_mister msgid "Mr." -msgstr "" +msgstr "Dl." #. module: base #: model:res.country,name:base.mv @@ -14468,7 +15151,7 @@ msgstr "Maldive" #. module: base #: model:ir.module.module,shortdesc:base.module_portal_crm msgid "Portal CRM" -msgstr "" +msgstr "Portla MRC" #. module: base #: model:ir.ui.menu,name:base.next_id_4 @@ -14488,7 +15171,7 @@ msgstr "Format Adresa" #. module: base #: model:ir.model,name:base.model_change_password_user msgid "Change Password Wizard User" -msgstr "" +msgstr "Schimba Parola Wizard Utilizator" #. module: base #: model:res.groups,name:base.group_no_one @@ -14529,6 +15212,40 @@ msgid "" "proposed, \n" "but you will need set manually account defaults for taxes.\n" msgstr "" +"\n" +"Plan de Conturi pentru Venezuela.\n" +"===============================\n" +"\n" +"Venezuela nu are nici un plan de conturi impus de lege, dar cel implicit\n" +"propus in OpenERP ar trebui sa respecte unele dintre cele mai acceptate bune " +"practici din Venezuela, \n" +"acest plan respecta aceste practici.\n" +"\n" +"Acest modul a fost testat ca baza pentru mai mult de 1000 de companii, " +"pentru ca \n" +"se bazeaza pe un amestec din cele mai comune programe de pe piata \n" +"venezueleana care cu siguranta ii va face pe contabili sa se simta mai " +"confortabil \n" +"atunci cand vor face primii pasi cu OpenERP.\n" +"\n" +"Acest modul nu pretinde a fi localizarea totala pentru Venezuela, \n" +"dar va va ajuta sa incepeti foarte repede cu OpenERP in aceasta tara.\n" +"\n" +"Acest modul va ofera.\n" +"---------------------\n" +"\n" +"- Taxele de baza pentru Venezuela.\n" +"- Datele de baza pentru a rula teste cu localizarea in comunitate.\n" +"- Inceperea unei companii de la 0 daca nevoile dumneavoastra sunt de baza " +"din punct de vedere contabil.\n" +"\n" +"Va recomandam sa instalati cont_anglo_saxon daca doriti valorificarea \n" +"stocurilor dumneavoastra asa cum Venezuela o face cu facturile de iesire.\n" +"\n" +"Daca instalati acest modul, si selectati plan personalizat, va fi propus un " +"plan de baza, \n" +"dar dumneavoastra trebuie sa configurati manual conturile implicite pentru " +"taxe.\n" #. module: base #: selection:base.language.install,lang:0 diff --git a/openerp/addons/base/ir/ir_cron.py b/openerp/addons/base/ir/ir_cron.py index 790b4a43de0..4bf99cf9934 100644 --- a/openerp/addons/base/ir/ir_cron.py +++ b/openerp/addons/base/ir/ir_cron.py @@ -136,12 +136,13 @@ class ir_cron(osv.osv): except Exception, e: self._handle_callback_exception(cr, uid, model_name, method_name, args, job_id, e) - def _process_job(self, cr, job): + def _process_job(self, job_cr, job, cron_cr): """ Run a given job taking care of the repetition. - The cursor has a lock on the job (aquired by _acquire_job()). - + :param job_cr: cursor to use to execute the job, safe to commit/rollback :param job: job to be run (as a dictionary). + :param cron_cr: cursor holding lock on the cron job row, to use to update the next exec date, + must not be committed/rolled back! """ try: now = datetime.now() @@ -153,19 +154,19 @@ class ir_cron(osv.osv): if numbercall > 0: numbercall -= 1 if not ok or job['doall']: - self._callback(cr, job['user_id'], job['model'], job['function'], job['args'], job['id']) + self._callback(job_cr, job['user_id'], job['model'], job['function'], job['args'], job['id']) if numbercall: nextcall += _intervalTypes[job['interval_type']](job['interval_number']) ok = True addsql = '' if not numbercall: addsql = ', active=False' - cr.execute("UPDATE ir_cron SET nextcall=%s, numbercall=%s"+addsql+" WHERE id=%s", + cron_cr.execute("UPDATE ir_cron SET nextcall=%s, numbercall=%s"+addsql+" WHERE id=%s", (nextcall.strftime(DEFAULT_SERVER_DATETIME_FORMAT), numbercall, job['id'])) finally: - cr.commit() - cr.close() + job_cr.commit() + cron_cr.commit() @classmethod def _acquire_job(cls, db_name): @@ -181,44 +182,14 @@ class ir_cron(osv.osv): """ db = openerp.sql_db.db_connect(db_name) cr = db.cursor() + jobs = [] try: # Careful to compare timestamps with 'UTC' - everything is UTC as of v6.1. cr.execute("""SELECT * FROM ir_cron WHERE numbercall != 0 AND active AND nextcall <= (now() at time zone 'UTC') ORDER BY priority""") - for job in cr.dictfetchall(): - task_cr = db.cursor() - try: - # Try to grab an exclusive lock on the job row from within the task transaction - acquired_lock = False - task_cr.execute("""SELECT * - FROM ir_cron - WHERE id=%s - FOR UPDATE NOWAIT""", - (job['id'],), log_exceptions=False) - acquired_lock = True - except psycopg2.OperationalError, e: - if e.pgcode == '55P03': - # Class 55: Object not in prerequisite state; 55P03: lock_not_available - _logger.debug('Another process/thread is already busy executing job `%s`, skipping it.', job['name']) - continue - else: - # Unexpected OperationalError - raise - finally: - if not acquired_lock: - # we're exiting due to an exception while acquiring the lot - task_cr.close() - - # Got the lock on the job row, run its code - _logger.debug('Starting job `%s`.', job['name']) - openerp.modules.registry.RegistryManager.check_registry_signaling(db_name) - registry = openerp.pooler.get_pool(db_name) - registry[cls._name]._process_job(task_cr, job) - openerp.modules.registry.RegistryManager.signal_caches_change(db_name) - return True - + jobs = cr.dictfetchall() except psycopg2.ProgrammingError, e: if e.pgcode == '42P01': # Class 42 — Syntax Error or Access Rule Violation; 42P01: undefined_table @@ -228,12 +199,43 @@ class ir_cron(osv.osv): raise except Exception: _logger.warning('Exception in cron:', exc_info=True) - finally: - cr.commit() cr.close() - return False + for job in jobs: + lock_cr = db.cursor() + try: + # Try to grab an exclusive lock on the job row from within the task transaction + lock_cr.execute("""SELECT * + FROM ir_cron + WHERE id=%s + FOR UPDATE NOWAIT""", + (job['id'],), log_exceptions=False) + + # Got the lock on the job row, run its code + _logger.debug('Starting job `%s`.', job['name']) + job_cr = db.cursor() + try: + openerp.modules.registry.RegistryManager.check_registry_signaling(db_name) + registry = openerp.pooler.get_pool(db_name) + registry[cls._name]._process_job(job_cr, job, lock_cr) + openerp.modules.registry.RegistryManager.signal_caches_change(db_name) + except Exception: + _logger.exception('Unexpected exception while processing cron job %r', job) + finally: + job_cr.close() + + except psycopg2.OperationalError, e: + if e.pgcode == '55P03': + # Class 55: Object not in prerequisite state; 55P03: lock_not_available + _logger.debug('Another process/thread is already busy executing job `%s`, skipping it.', job['name']) + continue + else: + # Unexpected OperationalError + raise + finally: + # we're exiting due to an exception while acquiring the lock + lock_cr.close() def _try_lock(self, cr, uid, ids, context=None): """Try to grab a dummy exclusive write-lock to the rows with the given ids, diff --git a/openerp/addons/base/ir/ir_model.py b/openerp/addons/base/ir/ir_model.py index ff43a15692f..ed6f843f898 100644 --- a/openerp/addons/base/ir/ir_model.py +++ b/openerp/addons/base/ir/ir_model.py @@ -199,7 +199,7 @@ class ir_model(osv.osv): def instanciate(self, cr, user, model, context=None): class x_custom_model(osv.osv): - pass + _custom = True x_custom_model._name = model x_custom_model._module = False a = x_custom_model.create_instance(self.pool, cr) diff --git a/openerp/addons/base/res/res_partner_view.xml b/openerp/addons/base/res/res_partner_view.xml index d1a803ce2d8..c9215763c3e 100644 --- a/openerp/addons/base/res/res_partner_view.xml +++ b/openerp/addons/base/res/res_partner_view.xml @@ -177,7 +177,6 @@ diff --git a/openerp/netsvc.py b/openerp/netsvc.py index 37de4dd5b8b..533b1659191 100644 --- a/openerp/netsvc.py +++ b/openerp/netsvc.py @@ -36,6 +36,11 @@ import time import types from pprint import pformat +try: + import psutil +except ImportError: + psutil = None + # TODO modules that import netsvc only for things from loglevels must be changed to use loglevels. from loglevels import * import tools @@ -273,6 +278,9 @@ def dispatch_rpc(service_name, method, params): rpc_response_flag = rpc_response.isEnabledFor(logging.DEBUG) if rpc_request_flag or rpc_response_flag: start_time = time.time() + start_rss, start_vms = 0, 0 + if psutil: + start_rss, start_vms = psutil.Process(os.getpid()).get_memory_info() if rpc_request and rpc_response_flag: log(rpc_request,logging.DEBUG,'%s.%s'%(service_name,method), replace_request_password(params)) @@ -282,10 +290,14 @@ def dispatch_rpc(service_name, method, params): if rpc_request_flag or rpc_response_flag: end_time = time.time() + end_rss, end_vms = 0, 0 + if psutil: + end_rss, end_vms = psutil.Process(os.getpid()).get_memory_info() + logline = '%s.%s time:%.3fs mem: %sk -> %sk (diff: %sk)' % (service_name, method, end_time - start_time, start_vms / 1024, end_vms / 1024, (end_vms - start_vms)/1024) if rpc_response_flag: - log(rpc_response,logging.DEBUG,'%s.%s time:%.3fs '%(service_name,method,end_time - start_time), result) + log(rpc_response,logging.DEBUG, logline, result) else: - log(rpc_request,logging.DEBUG,'%s.%s time:%.3fs '%(service_name,method,end_time - start_time), replace_request_password(params), depth=1) + log(rpc_request,logging.DEBUG, logline, replace_request_password(params), depth=1) return result except openerp.exceptions.AccessError: diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 6d78d7fd26c..6056e405b78 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -629,7 +629,8 @@ class MetaModel(type): self._module = module_name # Remember which models to instanciate for this module. - self.module_to_models.setdefault(self._module, []).append(self) + if not self._custom: + self.module_to_models.setdefault(self._module, []).append(self) # Definition of log access columns, automatically added to models if @@ -666,6 +667,7 @@ class BaseModel(object): _name = None _columns = {} _constraints = [] + _custom = False _defaults = {} _rec_name = None _parent_name = 'parent_id' @@ -942,7 +944,8 @@ class BaseModel(object): # managed by the metaclass. module_model_list = MetaModel.module_to_models.setdefault(cls._module, []) if cls not in module_model_list: - module_model_list.append(cls) + if not cls._custom: + module_model_list.append(cls) # Since we don't return an instance here, the __init__ # method won't be called. diff --git a/openerp/release.py b/openerp/release.py index e66d924906b..50bcd0c81e1 100644 --- a/openerp/release.py +++ b/openerp/release.py @@ -30,7 +30,7 @@ RELEASE_LEVELS_DISPLAY = {ALPHA: ALPHA, # properly comparable using normal operarors, for example: # (6,1,0,'beta',0) < (6,1,0,'candidate',1) < (6,1,0,'candidate',2) # (6,1,0,'candidate',2) < (6,1,0,'final',0) < (6,1,2,'final',0) -version_info = (7, 0, 0, ALPHA, 0) +version_info = (7, 0, 0, FINAL, 0) version = '.'.join(map(str, version_info[:2])) + RELEASE_LEVELS_DISPLAY[version_info[3]] + str(version_info[4] or '') serie = major_version = '.'.join(map(str, version_info[:2])) diff --git a/openerp/report/report_sxw.py b/openerp/report/report_sxw.py index e0731b819f5..b1f1e3d5891 100644 --- a/openerp/report/report_sxw.py +++ b/openerp/report/report_sxw.py @@ -617,9 +617,7 @@ class report_sxw(report_rml, preprocess.report): create_doc = self.generators[mime_type] odt = etree.tostring(create_doc(rml_dom, rml_parser.localcontext), encoding='utf-8', xml_declaration=True) - sxw_z = zipfile.ZipFile(sxw_io, mode='a') - sxw_z.writestr('content.xml', odt) - sxw_z.writestr('meta.xml', meta) + sxw_contents = {'content.xml':odt, 'meta.xml':meta} if report_xml.header: #Add corporate header/footer @@ -638,12 +636,25 @@ class report_sxw(report_rml, preprocess.report): rml_parser._add_header(odt) odt = etree.tostring(odt, encoding='utf-8', xml_declaration=True) - sxw_z.writestr('styles.xml', odt) + sxw_contents['styles.xml'] = odt finally: rml_file.close() - sxw_z.close() - final_op = sxw_io.getvalue() + + #created empty zip writing sxw contents to avoid duplication + sxw_out = StringIO.StringIO() + sxw_out_zip = zipfile.ZipFile(sxw_out, mode='w') + sxw_template_zip = zipfile.ZipFile (sxw_io, 'r') + for item in sxw_template_zip.infolist(): + if item.filename not in sxw_contents: + buffer = sxw_template_zip.read(item.filename) + sxw_out_zip.writestr(item.filename, buffer) + for item_filename, buffer in sxw_contents.iteritems(): + sxw_out_zip.writestr(item_filename, buffer) + sxw_template_zip.close() + sxw_out_zip.close() + final_op = sxw_out.getvalue() sxw_io.close() + sxw_out.close() return final_op, mime_type def create_single_html2html(self, cr, uid, ids, data, report_xml, context=None): diff --git a/openerp/service/workers.py b/openerp/service/workers.py index aee639035a6..29ae9de10c0 100644 --- a/openerp/service/workers.py +++ b/openerp/service/workers.py @@ -379,12 +379,17 @@ class WorkerCron(Worker): time.sleep(interval) def process_work(self): + rpc_request = logging.getLogger('openerp.netsvc.rpc.request') + rpc_request_flag = rpc_request.isEnabledFor(logging.DEBUG) _logger.debug("WorkerCron (%s) polling for jobs", self.pid) if config['db_name']: db_names = config['db_name'].split(',') else: db_names = openerp.netsvc.ExportService._services['db'].exp_list(True) for db_name in db_names: + if rpc_request_flag: + start_time = time.time() + start_rss, start_vms = psutil.Process(os.getpid()).get_memory_info() while True: # acquired = openerp.addons.base.ir.ir_cron.ir_cron._acquire_job(db_name) # TODO why isnt openerp.addons.base defined ? @@ -395,7 +400,12 @@ class WorkerCron(Worker): # dont keep cursors in multi database mode if len(db_names) > 1: openerp.sql_db.close_db(db_name) - # TODO Each job should be considered as one request instead of each db + if rpc_request_flag: + end_time = time.time() + end_rss, end_vms = psutil.Process(os.getpid()).get_memory_info() + logline = '%s time:%.3fs mem: %sk -> %sk (diff: %sk)' % (db_name, end_time - start_time, start_vms / 1024, end_vms / 1024, (end_vms - start_vms)/1024) + _logger.debug("WorkerCron (%s) %s", self.pid, logline) + # TODO Each job should be considered as one request instead of each run self.request_count += 1 def start(self): diff --git a/openerp/tests/test_mail.py b/openerp/tests/test_mail.py index b3c11a13bfe..ddb8fc4a195 100644 --- a/openerp/tests/test_mail.py +++ b/openerp/tests/test_mail.py @@ -43,6 +43,47 @@ test12

test link """ +EDI_LIKE_HTML_SOURCE = """
+

Hello ${object.partner_id.name},

+

A new invoice is available for you:

+

+   REFERENCES
+   Invoice number: ${object.number}
+   Invoice total: ${object.amount_total} ${object.currency_id.name}
+   Invoice date: ${object.date_invoice}
+   Order reference: ${object.origin}
+   Your contact: ${object.user_id.name} +

+
+

It is also possible to directly pay with Paypal:

+ + + +
+

If you have any question, do not hesitate to contact us.

+

Thank you for choosing ${object.company_id.name or 'us'}!

+
+
+
+

+ ${object.company_id.name}

+
+
+ + ${object.company_id.street}
+ ${object.company_id.street2}
+ ${object.company_id.zip} ${object.company_id.city}
+ ${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''}
+
+
+ Phone:  ${object.company_id.phone} +
+ +
+
""" + TEXT_MAIL1 = """I contact you about our meeting for tomorrow. Here is the schedule I propose: 9 AM: brainstorming about our new amazing business app 9.45 AM: summary @@ -126,23 +167,85 @@ bert.tartopoils@miam.miam class TestSanitizer(unittest2.TestCase): """ Test the html sanitizer that filters html to remove unwanted attributes """ - def test_simple(self): - x = "yop" - self.assertEqual(x, html_sanitize(x)) + def test_basic_sanitizer(self): + cases = [ + ("yop", "

yop

"), # simple + ("lala

yop

xxx", "

lala

yop

xxx
"), # trailing text + ("Merci à l'intérêt pour notre produit.nous vous contacterons bientôt. Merci", + u"

Merci à l'intérêt pour notre produit.nous vous contacterons bientôt. Merci

"), # unicode + ] + for content, expected in cases: + html = html_sanitize(content) + self.assertEqual(html, expected, 'html_sanitize is broken') - def test_trailing_text(self): - x = 'lala

yop

xxx' - self.assertEqual(x, html_sanitize(x)) + def test_evil_malicious_code(self): + # taken from https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Tests + cases = [ + (""), # no quotes and semicolons + (""), # UTF-8 Unicode encoding + (""), # hex encoding + (""), # embedded carriage return + (""), # embedded newline + (""), # embedded tab + (""), # embedded encoded tab + (""), # spaces and meta-characters + ("\">"), # malformed tag + (""), # non-alpha-non-digits + (""), # non-alpha-non-digits + ("<"), # extraneous open brackets + (" in test_mail) + try: + cleaner = clean.Cleaner(page_structure=True, style=False, safe_attrs_only=False, forms=False, kill_tags=tags_to_kill, remove_tags=tags_to_remove) + cleaned = cleaner.clean_html(src) + except TypeError, e: + # lxml.clean version < 2.3.1 does not have a kill_tags attribute + # to remove in 2014 + cleaner = clean.Cleaner(page_structure=True, style=False, safe_attrs_only=False, forms=False, remove_tags=tags_to_kill+tags_to_remove) + cleaned = cleaner.clean_html(src) + except: + _logger.warning('html_sanitize failed to parse %s' % (src)) + cleaned = '

Impossible to parse

' + return cleaned #---------------------------------------------------------- diff --git a/openerpcommand/__init__.py b/openerpcommand/__init__.py new file mode 100644 index 00000000000..53d456c7639 --- /dev/null +++ b/openerpcommand/__init__.py @@ -0,0 +1,61 @@ +import argparse +import textwrap + +from .call import Call +from .client import Open, Show, ConsumeNothing, ConsumeMemory, LeakMemory, ConsumeCPU +from .benchmarks import Bench, BenchRead, BenchFieldsViewGet, BenchDummy, BenchLogin +from .bench_sale_mrp import BenchSaleMrp +from . import common + +from . import conf # Not really server-side (in the `for` below). +from . import drop +from . import initialize +from . import model +from . import module +from . import read +from . import run_tests +from . import scaffold +from . import uninstall +from . import update + +command_list_server = (conf, drop, initialize, model, module, read, run_tests, + scaffold, uninstall, update, ) + +command_list_client = (Call, Open, Show, ConsumeNothing, ConsumeMemory, + LeakMemory, ConsumeCPU, Bench, BenchRead, + BenchFieldsViewGet, BenchDummy, BenchLogin, + BenchSaleMrp, ) + +def main_parser(): + parser = argparse.ArgumentParser( + usage=argparse.SUPPRESS, + description=textwrap.fill(textwrap.dedent("""\ + OpenERP Command provides a set of command-line tools around + the OpenERP framework: openobject-server. All the tools are + sub-commands of a single oe executable.""")), + epilog="""Use --help to get information about the command.""", + formatter_class=argparse.RawDescriptionHelpFormatter, + ) + description = [] + for x in command_list_server: + description.append(x.__name__[len(__package__)+1:]) + if x.__doc__: + description.extend([ + ":\n", + textwrap.fill(str(x.__doc__).strip(), + subsequent_indent=' ', + initial_indent=' '), + ]) + description.append("\n\n") + subparsers = parser.add_subparsers( + title="Available commands", + help=argparse.SUPPRESS, + description="".join(description[:-1]), + ) + # Server-side commands. + for x in command_list_server: + x.add_parser(subparsers) + # Client-side commands. TODO one per .py file. + for x in command_list_client: + x(subparsers) + return parser diff --git a/openerpcommand/addons/bench_sale_mrp/__init__.py b/openerpcommand/addons/bench_sale_mrp/__init__.py new file mode 100644 index 00000000000..99ae05ee3a1 --- /dev/null +++ b/openerpcommand/addons/bench_sale_mrp/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- +# Nothing here, the module provides only data. +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerpcommand/addons/bench_sale_mrp/__openerp__.py b/openerpcommand/addons/bench_sale_mrp/__openerp__.py new file mode 100644 index 00000000000..3d8781905cc --- /dev/null +++ b/openerpcommand/addons/bench_sale_mrp/__openerp__.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +{ + 'name': 'bench_sale_mrp', + 'version': '0.1', + 'category': 'Benchmarks', + 'description': """Prepare some data to run a benchmark.""", + 'author': 'OpenERP SA', + 'maintainer': 'OpenERP SA', + 'website': 'http://www.openerp.com', + 'depends': ['base', 'sale_mrp'], + 'data': ['data.yml'], + 'installable': True, + 'auto_install': False, +} +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerpcommand/addons/bench_sale_mrp/data.yml b/openerpcommand/addons/bench_sale_mrp/data.yml new file mode 100644 index 00000000000..419492e60e1 --- /dev/null +++ b/openerpcommand/addons/bench_sale_mrp/data.yml @@ -0,0 +1,41 @@ +- + This is a subset of `sale_mrp/test/sale_mrp.yml`. +- + I define a product category `Mobile Products Sellable`. +- + !record {model: product.category, id: my_product_category_0}: + name: Mobile Products Sellable +- + I define a product `Slider Mobile` +- + !record {model: product.product, id: my_slider_mobile_0}: + categ_id: my_product_category_0 + cost_method: standard + list_price: 200.0 + mes_type: fixed + name: Slider Mobile + procure_method: make_to_order + seller_delay: '1' + seller_ids: + - delay: 1 + name: base.res_partner_agrolait + min_qty: 2.0 + qty: 5.0 + standard_price: 189.0 + supply_method: produce + type: product + uom_id: product.product_uom_unit + uom_po_id: product.product_uom_unit +- + I create a Bill of Material for the `Slider Mobile` product. +- + !record {model: mrp.bom, id: mrp_bom_slidermobile0}: + company_id: base.main_company + name: Slider Mobile + product_efficiency: 1.0 + product_id: my_slider_mobile_0 + product_qty: 1.0 + product_uom: product.product_uom_unit + product_uos_qty: 0.0 + sequence: 0.0 + type: normal diff --git a/openerpcommand/bench_sale_mrp.py b/openerpcommand/bench_sale_mrp.py new file mode 100644 index 00000000000..60e53037855 --- /dev/null +++ b/openerpcommand/bench_sale_mrp.py @@ -0,0 +1,68 @@ +""" +Benchmark based on the `sale_mrp` addons (in `sale_mrp/test/sale_mrp.yml`). +""" + +import time + +from .benchmarks import Bench + +class BenchSaleMrp(Bench): + """\ + Similar to `sale_mrp/test/sale_mrp.yml`. + + This benchmarks the OpenERP server `sale_mrp` module by creating and + confirming a sale order. As it creates data in the server, it is necessary + to ensure unique names for the newly created data. You can use the --seed + argument to give a lower bound to those names. (The number of generated + names is --jobs * --samples.) + """ + + command_name = 'bench-sale-mrp' + bench_name = '`sale_mrp/test/sale_mrp.yml`' + + def measure_once(self, i): + if self.worker >= 0: + i = int(self.args.seed) + i + (self.worker * int(self.args.samples)) + else: + i = int(self.args.seed) + i + + # Resolve a few external-ids (this has little impact on the running + # time of the whole method). + product_uom_unit = self.execute('ir.model.data', 'get_object_reference', 'product', 'product_uom_unit')[1] + my_slider_mobile_0 = self.execute('ir.model.data', 'get_object_reference', 'bench_sale_mrp', 'my_slider_mobile_0')[1] + res_partner_4 = self.execute('ir.model.data', 'get_object_reference', 'base', 'res_partner_4')[1] + res_partner_address_7 = self.execute('ir.model.data', 'get_object_reference', 'base', 'res_partner_address_7')[1] + list0 = self.execute('ir.model.data', 'get_object_reference', 'product', 'list0')[1] + shop = self.execute('ir.model.data', 'get_object_reference', 'sale', 'shop')[1] + + # Create a sale order for the product `Slider Mobile`. + data = { + 'client_order_ref': 'ref_xxx_' + str(i).rjust(6, '0'), + 'date_order': time.strftime('%Y-%m-%d'), + 'invoice_quantity': 'order', + 'name': 'sale_order_ref_xxx_' + str(i).rjust(6, '0'), + 'order_line': [(0, 0, { + 'name': 'Slider Mobile', + 'price_unit': 2, + 'product_uom': product_uom_unit, + 'product_uom_qty': 5.0, + 'state': 'draft', + 'delay': 7.0, + 'product_id': my_slider_mobile_0, + 'product_uos_qty': 5, + 'type': 'make_to_order', + })], + 'order_policy': 'manual', + 'partner_id': res_partner_4, + 'partner_invoice_id': res_partner_address_7, + 'partner_order_id': res_partner_address_7, + 'partner_shipping_id': res_partner_address_7, + 'picking_policy': 'direct', + 'pricelist_id': list0, + 'shop_id': shop, + } + sale_order_id = self.execute('sale.order', 'create', data, {}) + + # Confirm the sale order. + self.object_proxy.exec_workflow(self.database, self.uid, self.password, 'sale.order', 'order_confirm', sale_order_id, {}) + diff --git a/openerpcommand/benchmarks.py b/openerpcommand/benchmarks.py new file mode 100644 index 00000000000..401ce077b77 --- /dev/null +++ b/openerpcommand/benchmarks.py @@ -0,0 +1,166 @@ +""" +Define a base class for client-side benchmarking. +""" +import hashlib +import multiprocessing +import sys +import time + +from .client import Client + +class Bench(Client): + """ + Base class for concurrent benchmarks. The measure_once() method must be + overriden. + + Each sub-benchmark will be run in its own process then a report is done + with all the results (shared with the main process using a + `multiprocessing.Array`). + """ + + def __init__(self, subparsers=None): + super(Bench, self).__init__(subparsers) + self.parser.add_argument('-n', '--samples', metavar='INT', + default=100, help='number of measurements to take') + # TODO if -n s is given (instead of -n ), run the + # benchmark for seconds and return the number of iterations. + self.parser.add_argument('-o', '--output', metavar='PATH', + required=True, help='path to save the generated report') + self.parser.add_argument('--append', action='store_true', + default=False, help='append the report to an existing file') + self.parser.add_argument('-j', '--jobs', metavar='JOBS', + default=1, help='number of concurrent workers') + self.parser.add_argument('--seed', metavar='SEED', + default=0, help='a value to ensure different runs can create unique data') + self.worker = -1 + + def work(self, iarr=None): + if iarr: + # If an array is given, it means we are a worker process... + self.work_slave(iarr) + else: + # ... else we are the main process and we will spawn workers, + # passing them an array. + self.work_master() + + def work_master(self): + N = int(self.args.samples) + self.arrs = [(i, multiprocessing.Array('f', range(N))) + for i in xrange(int(self.args.jobs))] + ps = [multiprocessing.Process(target=self.run, args=(arr,)) + for arr in self.arrs] + [p.start() for p in ps] + [p.join() for p in ps] + + self.report_html() + + def work_slave(self, iarr): + j, arr = iarr + self.worker = j + N = int(self.args.samples) + total_t0 = time.time() + for i in xrange(N): + t0 = time.time() + self.measure_once(i) + t1 = time.time() + arr[i] = t1 - t0 + print >> sys.stdout, '\r%s' % ('|' * (i * 60 / N)), + print >> sys.stdout, '%s %s%%' % \ + (' ' * (60 - (i * 60 / N)), int(float(i+1)/N*100)), + sys.stdout.flush() + total_t1 = time.time() + print '\nDone in %ss.' % (total_t1 - total_t0) + + def report_html(self): + series = [] + for arr in self.arrs: + serie = """{ + data: %s, + points: { show: true } + }""" % ([[x, i] for i, x in enumerate(arr)],) + series.append(serie) + chart_id = hashlib.md5(" ".join(sys.argv)).hexdigest() + HEADER = """ +Benchmarks + + + +""" + + CONTENT = """

%s

+%s +
...
+""" % (self.bench_name, ' '.join(sys.argv), chart_id, chart_id, + ','.join(series)) + if self.args.append: + with open(self.args.output, 'a') as f: + f.write(CONTENT,) + else: + with open(self.args.output, 'w') as f: + f.write(HEADER + CONTENT,) + + def measure_once(self, i): + """ + The `measure_once` method is called --jobs times. A `i` argument is + supplied to allow to create unique values for each execution (e.g. to + supply fresh identifiers to a `create` method. + """ + pass + +class BenchRead(Bench): + """Read a record repeatedly.""" + + command_name = 'bench-read' + bench_name = 'res.users.read(1)' + + def __init__(self, subparsers=None): + super(BenchRead, self).__init__(subparsers) + self.parser.add_argument('-m', '--model', metavar='MODEL', + required=True, help='the model') + self.parser.add_argument('-i', '--id', metavar='RECORDID', + required=True, help='the record id') + + def measure_once(self, i): + self.execute(self.args.model, 'read', [self.args.id], []) + +class BenchFieldsViewGet(Bench): + """Read a record's fields and view architecture repeatedly.""" + + command_name = 'bench-view' + bench_name = 'res.users.fields_view_get(1)' + + def __init__(self, subparsers=None): + super(BenchFieldsViewGet, self).__init__(subparsers) + self.parser.add_argument('-m', '--model', metavar='MODEL', + required=True, help='the model') + self.parser.add_argument('-i', '--id', metavar='RECORDID', + required=True, help='the record id') + + def measure_once(self, i): + self.execute(self.args.model, 'fields_view_get', self.args.id) + +class BenchDummy(Bench): + """Dummy (call test.limits.model.consume_nothing()).""" + + command_name = 'bench-dummy' + bench_name = 'test.limits.model.consume_nothing()' + + def __init__(self, subparsers=None): + super(BenchDummy, self).__init__(subparsers) + self.parser.add_argument('-a', '--args', metavar='ARGS', + default='', help='some arguments to serialize') + + def measure_once(self, i): + self.execute('test.limits.model', 'consume_nothing') + +class BenchLogin(Bench): + """Login (update res_users.date).""" + + command_name = 'bench-login' + bench_name = 'res.users.login(1)' + + def measure_once(self, i): + self.common_proxy.login(self.database, self.user, self.password) diff --git a/openerpcommand/call.py b/openerpcommand/call.py new file mode 100644 index 00000000000..b4f9e95075a --- /dev/null +++ b/openerpcommand/call.py @@ -0,0 +1,44 @@ +""" +Call an arbitrary model's method. +""" +import ast +import os +import pprint +import sys +import time +import xmlrpclib + +import client + +class Call(client.Client): + """\ + Call an arbitrary model's method. + + Example: + > oe call res.users.read '[1, 3]' '[]' -u 1 -p admin + """ + # TODO The above docstring is completely borked in the + # --help message. + + command_name = 'call' + + def __init__(self, subparsers=None): + super(Call, self).__init__(subparsers) + self.parser.add_argument('call', metavar='MODEL.METHOD', + help='the model and the method to call, using the ' + '. format.') + self.parser.add_argument('args', metavar='ARGUMENT', + nargs='+', + help='the argument for the method call, must be ' + '`ast.literal_eval` compatible. Can be repeated.') + + def work(self): + try: + model, method = self.args.call.rsplit('.', 1) + except: + print "Invalid syntax `%s` must have the form .." + sys.exit(1) + args = tuple(map(ast.literal_eval, self.args.args)) if self.args.args else () + x = self.execute(model, method, *args) + pprint.pprint(x, indent=4) + diff --git a/openerpcommand/client.py b/openerpcommand/client.py new file mode 100644 index 00000000000..87586b68ddf --- /dev/null +++ b/openerpcommand/client.py @@ -0,0 +1,137 @@ +""" +Define a few common arguments for client-side command-line tools. +""" +import os +import sys +import time +import xmlrpclib + +import common + +class Client(common.Command): + """ + Base class for XML-RPC command-line clients. It must be inherited and the + work() method overriden. + """ + + def __init__(self, subparsers=None): + super(Client, self).__init__(subparsers) + required_or_default = common.required_or_default + self.parser.add_argument('-H', '--host', metavar='HOST', + **required_or_default('HOST', 'the server host')) + self.parser.add_argument('-P', '--port', metavar='PORT', + **required_or_default('PORT', 'the server port')) + + def execute(self, *args): + return self.object_proxy.execute(self.database, self.uid, self.password, *args) + + def initialize(self): + self.host = self.args.host + self.port = int(self.args.port) + self.database = self.args.database + self.user = self.args.user + self.password = self.args.password + + self.url = 'http://%s:%d/xmlrpc/' % (self.host, self.port) + self.common_proxy = xmlrpclib.ServerProxy(self.url + 'common') + self.object_proxy = xmlrpclib.ServerProxy(self.url + 'object') + + try: + self.uid = int(self.user) + except ValueError, e: + self.uid = self.common_proxy.login(self.database, self.user, self.password) + + def run(self, *args): + self.initialize() + self.work(*args) + + def work(self, *args): + pass + +class Open(Client): + """Get the web client's URL to view a specific model.""" + + command_name = 'open' + + def __init__(self, subparsers=None): + super(Open, self).__init__(subparsers) + self.parser.add_argument('-m', '--model', metavar='MODEL', + required=True, help='the view type') + self.parser.add_argument('-v', '--view-mode', metavar='VIEWMODE', + default='tree', help='the view mode') + + def work(self): + ids = self.execute('ir.actions.act_window', 'search', [ + ('res_model', '=', self.args.model), + ('view_mode', 'like', self.args.view_mode), + ]) + xs = self.execute('ir.actions.act_window', 'read', ids, []) + for x in xs: + print x['id'], x['name'] + d = {} + d['host'] = self.host + d['port'] = self.port + d['action_id'] = x['id'] + print " http://%(host)s:%(port)s/web/webclient/home#action_id=%(action_id)s" % d + +class Show(Client): + """Display a record.""" + + command_name = 'show' + + def __init__(self, subparsers=None): + super(Show, self).__init__(subparsers) + self.parser.add_argument('-m', '--model', metavar='MODEL', + required=True, help='the model') + self.parser.add_argument('-i', '--id', metavar='RECORDID', + required=True, help='the record id') + + def work(self): + xs = self.execute(self.args.model, 'read', [self.args.id], []) + if xs: + x = xs[0] + print x['name'] + else: + print "Record not found." + +class ConsumeNothing(Client): + """Call test.limits.model.consume_nothing().""" + + command_name = 'consume-nothing' + + def work(self): + xs = self.execute('test.limits.model', 'consume_nothing') + +class ConsumeMemory(Client): + """Call test.limits.model.consume_memory().""" + + command_name = 'consume-memory' + + def __init__(self, subparsers=None): + super(ConsumeMemory, self).__init__(subparsers) + self.parser.add_argument('--size', metavar='SIZE', + required=True, help='size of the list to allocate') + + def work(self): + xs = self.execute('test.limits.model', 'consume_memory', int(self.args.size)) + +class LeakMemory(ConsumeMemory): + """Call test.limits.model.leak_memory().""" + + command_name = 'leak-memory' + + def work(self): + xs = self.execute('test.limits.model', 'leak_memory', int(self.args.size)) + +class ConsumeCPU(Client): + """Call test.limits.model.consume_cpu_time().""" + + command_name = 'consume-cpu' + + def __init__(self, subparsers=None): + super(ConsumeCPU, self).__init__(subparsers) + self.parser.add_argument('--seconds', metavar='INT', + required=True, help='how much CPU time to consume') + + def work(self): + xs = self.execute('test.limits.model', 'consume_cpu_time', int(self.args.seconds)) diff --git a/openerpcommand/common.py b/openerpcommand/common.py new file mode 100644 index 00000000000..89898ff4c3c --- /dev/null +++ b/openerpcommand/common.py @@ -0,0 +1,88 @@ +""" +Define a few common arguments for server-side command-line tools. +""" +import argparse +import os +import sys + +def add_addons_argument(parser): + """ + Add a common --addons argument to a parser. + """ + parser.add_argument('--addons', metavar='ADDONS', + **required_or_default('ADDONS', + 'colon-separated list of paths to addons')) + +def get_addons_from_paths(paths, exclude): + """ + Build a list of available modules from a list of addons paths. + """ + exclude = exclude or [] + module_names = [] + for p in paths: + if os.path.exists(p): + names = list(set(os.listdir(p))) + names = filter(lambda a: not (a.startswith('.') or a in exclude), names) + module_names.extend(names) + else: + print "The addons path `%s` doesn't exist." % p + sys.exit(1) + return module_names + +def required_or_default(name, h): + """ + Helper to define `argparse` arguments. If the name is the environment, + the argument is optional and draw its value from the environment if not + supplied on the command-line. If it is not in the environment, make it + a mandatory argument. + """ + if os.environ.get('OPENERP_' + name.upper()): + d = {'default': os.environ['OPENERP_' + name.upper()]} + else: + d = {'required': True} + d['help'] = h + '. The environment variable OPENERP_' + \ + name.upper() + ' can be used instead.' + return d + +class Command(object): + """ + Base class to create command-line tools. It must be inherited and the + run() method overriden. + """ + + command_name = 'stand-alone' + + def __init__(self, subparsers=None): + if subparsers: + self.parser = parser = subparsers.add_parser(self.command_name, + description=self.__class__.__doc__) + else: + self.parser = parser = argparse.ArgumentParser( + description=self.__class__.__doc__) + + parser.add_argument('-d', '--database', metavar='DATABASE', + **required_or_default('DATABASE', 'the database to connect to')) + parser.add_argument('-u', '--user', metavar='USER', + **required_or_default('USER', 'the user login or ID. When using ' + 'RPC, providing an ID avoid the login() step')) + parser.add_argument('-p', '--password', metavar='PASSWORD', + **required_or_default('PASSWORD', 'the user password')) # TODO read it from the command line or from file. + + parser.set_defaults(run=self.run_with_args) + + def run_with_args(self, args): + self.args = args + self.run() + + def run(self): + print 'Stub Command.run().' + + @classmethod + def stand_alone(cls): + """ + A single Command object is a complete command-line program. See + `openerp-command/stand-alone` for an example. + """ + command = cls() + args = command.parser.parse_args() + args.run(args) diff --git a/openerpcommand/conf.py b/openerpcommand/conf.py new file mode 100644 index 00000000000..98237ea5ad8 --- /dev/null +++ b/openerpcommand/conf.py @@ -0,0 +1,25 @@ +""" +Display the currently used configuration. The configuration for any +sub-command is normally given by options. But some options can be specified +using environment variables. This sub-command shows those variables. +A `set` sub-command should be provided when the configuration is in a real +configuration file instead of environment variables. +""" +import os +import sys +import textwrap + +def run(args): + for x in ('database', 'addons', 'host', 'port'): + x_ = ('openerp_' + x).upper() + if x_ in os.environ: + print '%s: %s' % (x, os.environ[x_]) + else: + print '%s: ' % (x, ) + os.environ['OPENERP_DATABASE'] = 'yeah' + +def add_parser(subparsers): + parser = subparsers.add_parser('conf', + description='Display the currently used configuration.') + + parser.set_defaults(run=run) diff --git a/openerpcommand/drop.py b/openerpcommand/drop.py new file mode 100644 index 00000000000..aee8c2859f9 --- /dev/null +++ b/openerpcommand/drop.py @@ -0,0 +1,45 @@ +""" +Drop a database. +""" + +import common + +# TODO turn template1 in a parameter +# This should be exposed from openerp (currently in +# openerp/service/web_services.py). +def drop_database(database_name): + import openerp + db = openerp.sql_db.db_connect('template1') + cr = db.cursor() + cr.autocommit(True) # avoid transaction block + try: + # TODO option for doing this. + # Try to terminate all other connections that might prevent + # dropping the database + try: + cr.execute("""SELECT pg_terminate_backend(procpid) + FROM pg_stat_activity + WHERE datname = %s AND + procpid != pg_backend_pid()""", + (database_name,)) + except Exception: + pass + + try: + cr.execute('DROP DATABASE "%s"' % database_name) + except Exception, e: + print "Can't drop %s" % (database_name,) + finally: + cr.close() + +def run(args): + assert args.database + drop_database(args.database) + +def add_parser(subparsers): + parser = subparsers.add_parser('drop', + description='Drop a database.') + parser.add_argument('-d', '--database', metavar='DATABASE', + **common.required_or_default('DATABASE', 'the database to create')) + + parser.set_defaults(run=run) diff --git a/openerpcommand/initialize.py b/openerpcommand/initialize.py new file mode 100644 index 00000000000..3393c9a02d4 --- /dev/null +++ b/openerpcommand/initialize.py @@ -0,0 +1,103 @@ +""" +Install OpenERP on a new (by default) database. +""" +import os +import sys + +import common + +def install_openerp(database_name, create_database_flag, module_names, install_demo_data): + import openerp + config = openerp.tools.config + + if create_database_flag: + create_database(database_name) + + config['init'] = dict.fromkeys(module_names, 1) + + # Install the import hook, to import openerp.addons.. + openerp.modules.module.initialize_sys_path() + if hasattr(openerp.modules.loading, 'open_openerp_namespace'): + openerp.modules.loading.open_openerp_namespace() + + registry = openerp.modules.registry.RegistryManager.get( + database_name, update_module=True, force_demo=install_demo_data) + + return registry + +# TODO turn template1 in a parameter +# This should be exposed from openerp (currently in +# openerp/service/web_services.py). +def create_database(database_name): + import openerp + db = openerp.sql_db.db_connect('template1') + cr = db.cursor() # TODO `with db as cr:` + try: + cr.autocommit(True) + cr.execute("""CREATE DATABASE "%s" + ENCODING 'unicode' TEMPLATE "template1" """ \ + % (database_name,)) + finally: + cr.close() + +def run(args): + assert args.database + assert not (args.module and args.all_modules) + + import openerp + + config = openerp.tools.config + + if args.tests: + config['log_handler'] = [':TEST'] + config['test_enable'] = True + config['without_demo'] = False + else: + config['log_handler'] = [':CRITICAL'] + config['test_enable'] = False + config['without_demo'] = True + + if args.addons: + args.addons = args.addons.split(':') + else: + args.addons = [] + config['addons_path'] = ','.join(args.addons) + + if args.all_modules: + module_names = common.get_addons_from_paths(args.addons, args.exclude) + elif args.module: + module_names = args.module + else: + module_names = ['base'] + + openerp.netsvc.init_logger() + registry = install_openerp(args.database, not args.no_create, module_names, not config['without_demo']) + + # The `_assertion_report` attribute was added on the registry during the + # OpenERP 7.0 development. + if hasattr(registry, '_assertion_report'): + sys.exit(1 if registry._assertion_report.failures else 0) + +def add_parser(subparsers): + parser = subparsers.add_parser('initialize', + description='Create and initialize a new OpenERP database.') + parser.add_argument('-d', '--database', metavar='DATABASE', + **common.required_or_default('DATABASE', 'the database to create')) + common.add_addons_argument(parser) + parser.add_argument('--module', metavar='MODULE', action='append', + help='specify a module to install' + ' (this option can be repeated)') + parser.add_argument('--all-modules', action='store_true', + help='install all visible modules (not compatible with --module)') + parser.add_argument('--no-create', action='store_true', + help='do not create the database, only initialize it') + parser.add_argument('--exclude', metavar='MODULE', action='append', + help='exclude a module from installation' + ' (this option can be repeated)') + parser.add_argument('--tests', action='store_true', + help='run the tests as modules are installed' + ' (use the `run-tests` command to choose specific' + ' tests to run against an existing database).' + ' Demo data are installed.') + + parser.set_defaults(run=run) diff --git a/openerpcommand/main.py b/openerpcommand/main.py new file mode 100644 index 00000000000..f5a1b03f774 --- /dev/null +++ b/openerpcommand/main.py @@ -0,0 +1,7 @@ +import openerpcommand + +def run(): + """ Main entry point for the openerp-command tool.""" + parser = openerpcommand.main_parser() + args = parser.parse_args() + args.run(args) diff --git a/openerpcommand/model.py b/openerpcommand/model.py new file mode 100644 index 00000000000..26a499c647f --- /dev/null +++ b/openerpcommand/model.py @@ -0,0 +1,61 @@ +""" +Display information about a given model. +""" +import os +import sys +import textwrap + +def run(args): + assert args.database + assert args.model + import openerp + openerp.tools.config['log_level'] = 100 + openerp.netsvc.init_logger() + registry = openerp.modules.registry.RegistryManager.get( + args.database, update_module=False) + model = registry.get(args.model) + longest_k = 1 + longest_string = 1 + columns = model._columns + + if args.field and args.field not in columns: + print "No such field." + sys.exit(1) + + if args.field: + columns = { args.field: columns[args.field] } + else: + print "Fields (model `%s`, database `%s`):" % (args.model, args.database) + + for k, v in columns.items(): + longest_k = len(k) if longest_k < len(k) else longest_k + longest_string = len(v.string) \ + if longest_string < len(v.string) else longest_string + for k, v in sorted(columns.items()): + attr = [] + if v.required: + attr.append("Required") + if v.readonly: + attr.append("Read-only") + attr = '/'.join(attr) + attr = '(' + attr + ')' if attr else attr + if args.verbose: + print v.string, '-- ' + k + ', ' + v._type, attr + else: + print k.ljust(longest_k + 2), v._type, attr + if args.verbose and v.help: + print textwrap.fill(v.help, initial_indent=' ', subsequent_indent=' ') + +def add_parser(subparsers): + parser = subparsers.add_parser('model', + description='Display information about a given model for an existing database.') + parser.add_argument('-d', '--database', metavar='DATABASE', required=True, + help='the database to connect to') + parser.add_argument('-m', '--model', metavar='MODEL', required=True, + help='the model for which information should be displayed') + parser.add_argument('-v', '--verbose', action='store_true', + help='display more information') + parser.add_argument('-f', '--field', metavar='FIELD', + help='display information only for this particular field') + + parser.set_defaults(run=run) diff --git a/openerpcommand/module.py b/openerpcommand/module.py new file mode 100644 index 00000000000..550e0a2be28 --- /dev/null +++ b/openerpcommand/module.py @@ -0,0 +1,68 @@ +""" +Show module information for a given database or from the file-system. +""" +import os +import sys +import textwrap + +from . import common + +# TODO provide a --rpc flag to use XML-RPC (with a specific username) instead +# of server-side library. +def run(args): + assert args.database + import openerp + + config = openerp.tools.config + config['log_handler'] = [':CRITICAL'] + if args.addons: + args.addons = args.addons.split(':') + else: + args.addons = [] + config['addons_path'] = ','.join(args.addons) + openerp.netsvc.init_logger() + + if args.filesystem: + module_names = common.get_addons_from_paths(args.addons, []) + print "Modules (addons path %s):" % (', '.join(args.addons),) + for x in sorted(module_names): + print x + else: + registry = openerp.modules.registry.RegistryManager.get( + args.database, update_module=False) + + xs = [] + ir_module_module = registry.get('ir.module.module') + cr = registry.db.cursor() # TODO context manager + try: + ids = ir_module_module.search(cr, openerp.SUPERUSER_ID, [], {}) + xs = ir_module_module.read(cr, openerp.SUPERUSER_ID, ids, [], {}) + finally: + cr.close() + + if xs: + print "Modules (database `%s`):" % (args.database,) + for x in xs: + if args.short: + print '%3d %s' % (x['id'], x['name']) + else: + print '%3d %s %s' % (x['id'], x['name'], {'installed': '(installed)'}.get(x['state'], '')) + else: + print "No module found (database `%s`)." % (args.database,) + +def add_parser(subparsers): + parser = subparsers.add_parser('module', + description='Display modules known from a given database or on file-system.') + parser.add_argument('-d', '--database', metavar='DATABASE', + **common.required_or_default('DATABASE', 'the database to modify')) + common.add_addons_argument(parser) + parser.add_argument('-m', '--module', metavar='MODULE', required=False, + help='the module for which information should be shown') + parser.add_argument('-v', '--verbose', action='store_true', + help='display more information') + parser.add_argument('--short', action='store_true', + help='display less information') + parser.add_argument('-f', '--filesystem', action='store_true', + help='display module in the addons path, not in db') + + parser.set_defaults(run=run) diff --git a/openerpcommand/read.py b/openerpcommand/read.py new file mode 100644 index 00000000000..83d6a5340f8 --- /dev/null +++ b/openerpcommand/read.py @@ -0,0 +1,60 @@ +""" +Read a record. +""" +import os +import sys +import textwrap + +# TODO provide a --rpc flag to use XML-RPC (with a specific username) instead +# of server-side library. +def run(args): + assert args.database + assert args.model + import openerp + config = openerp.tools.config + config['log_handler'] = [':CRITICAL'] + openerp.netsvc.init_logger() + registry = openerp.modules.registry.RegistryManager.get( + args.database, update_module=False) + model = registry.get(args.model) + cr = registry.db.cursor() # TODO context manager + field_names = [args.field] if args.field else [] + if args.short: + # ignore --field + field_names = ['name'] + try: + xs = model.read(cr, 1, args.id, field_names, {}) + finally: + cr.close() + + if xs: + print "Records (model `%s`, database `%s`):" % (args.model, args.database) + x = xs[0] + if args.short: + print str(x['id']) + '.', x['name'] + else: + longest_k = 1 + for k, v in x.items(): + longest_k = len(k) if longest_k < len(k) else longest_k + for k, v in sorted(x.items()): + print (k + ':').ljust(longest_k + 2), v + else: + print "Record not found." + +def add_parser(subparsers): + parser = subparsers.add_parser('read', + description='Display a record.') + parser.add_argument('-d', '--database', metavar='DATABASE', required=True, + help='the database to connect to') + parser.add_argument('-m', '--model', metavar='MODEL', required=True, + help='the model for which a record should be read') + parser.add_argument('-i', '--id', metavar='RECORDID', required=True, + help='the record id') + parser.add_argument('-v', '--verbose', action='store_true', + help='display more information') + parser.add_argument('--short', action='store_true', + help='display less information') + parser.add_argument('-f', '--field', metavar='FIELD', + help='display information only for this particular field') + + parser.set_defaults(run=run) diff --git a/openerpcommand/run_tests.py b/openerpcommand/run_tests.py new file mode 100644 index 00000000000..80e968b6fae --- /dev/null +++ b/openerpcommand/run_tests.py @@ -0,0 +1,202 @@ +""" +Execute the unittest2 tests available in OpenERP addons. +""" + +import os +import sys +import types + +import common + +def get_test_modules(module, submodule, explode): + """ + Return a list of submodules containing tests. + `submodule` can be: + - None + - the name of a submodule + - '__fast_suite__' + - '__sanity_checks__' + """ + # Turn command-line module, submodule into importable names. + if module is None: + pass + elif module == 'openerp': + module = 'openerp.tests' + else: + module = 'openerp.addons.' + module + '.tests' + + # Try to import the module + try: + __import__(module) + except Exception, e: + if explode: + print 'Can not `import %s`.' % module + import logging + logging.exception('') + sys.exit(1) + else: + if str(e) == 'No module named tests': + # It seems the module has no `tests` sub-module, no problem. + pass + else: + print 'Can not `import %s`.' % module + return [] + + # Discover available test sub-modules. + m = sys.modules[module] + submodule_names = sorted([x for x in dir(m) \ + if x.startswith('test_') and \ + isinstance(getattr(m, x), types.ModuleType)]) + submodules = [getattr(m, x) for x in submodule_names] + + def show_submodules_and_exit(): + if submodule_names: + print 'Available submodules are:' + for x in submodule_names: + print ' ', x + sys.exit(1) + + if submodule is None: + # Use auto-discovered sub-modules. + ms = submodules + elif submodule == '__fast_suite__': + # Obtain the explicit test sub-modules list. + ms = getattr(sys.modules[module], 'fast_suite', None) + # `suite` was used before the 6.1 release instead of `fast_suite`. + ms = ms if ms else getattr(sys.modules[module], 'suite', None) + if ms is None: + if explode: + print 'The module `%s` has no defined test suite.' % (module,) + show_submodules_and_exit() + else: + ms = [] + elif submodule == '__sanity_checks__': + ms = getattr(sys.modules[module], 'checks', None) + if ms is None: + if explode: + print 'The module `%s` has no defined sanity checks.' % (module,) + show_submodules_and_exit() + else: + ms = [] + else: + # Pick the command-line-specified test sub-module. + m = getattr(sys.modules[module], submodule, None) + ms = [m] + + if m is None: + if explode: + print 'The module `%s` has no submodule named `%s`.' % \ + (module, submodule) + show_submodules_and_exit() + else: + ms = [] + + return ms + +def run(args): + import unittest2 + + import openerp + + config = openerp.tools.config + config['db_name'] = args.database + if args.port: + config['xmlrpc_port'] = int(args.port) + config['admin_passwd'] = 'admin' + config['db_password'] = 'a2aevl8w' # TODO from .openerpserverrc + config['addons_path'] = args.addons.replace(':',',') + if args.addons: + args.addons = args.addons.split(':') + else: + args.addons = [] + if args.sanity_checks and args.fast_suite: + print 'Only at most one of `--sanity-checks` and `--fast-suite` ' \ + 'can be specified.' + sys.exit(1) + + import logging + openerp.netsvc.init_alternative_logger() + logging.getLogger('openerp').setLevel(logging.CRITICAL) + + # Install the import hook, to import openerp.addons.. + openerp.modules.module.initialize_sys_path() + openerp.modules.loading.open_openerp_namespace() + + # Extract module, submodule from the command-line args. + if args.module is None: + module, submodule = None, None + else: + splitted = args.module.split('.') + if len(splitted) == 1: + module, submodule = splitted[0], None + elif len(splitted) == 2: + module, submodule = splitted + else: + print 'The `module` argument must have the form ' \ + '`module[.submodule]`.' + sys.exit(1) + + # Import the necessary modules and get the corresponding suite. + if module is None: + # TODO + modules = common.get_addons_from_paths(args.addons, []) # TODO openerp.addons.base is not included ? + test_modules = [] + for module in ['openerp'] + modules: + if args.fast_suite: + submodule = '__fast_suite__' + if args.sanity_checks: + submodule = '__sanity_checks__' + test_modules.extend(get_test_modules(module, + submodule, explode=False)) + else: + if submodule and args.fast_suite: + print "Submodule name `%s` given, ignoring `--fast-suite`." % (submodule,) + if submodule and args.sanity_checks: + print "Submodule name `%s` given, ignoring `--sanity-checks`." % (submodule,) + if not submodule and args.fast_suite: + submodule = '__fast_suite__' + if not submodule and args.sanity_checks: + submodule = '__sanity_checks__' + test_modules = get_test_modules(module, + submodule, explode=True) + + # Run the test suite. + if not args.dry_run: + suite = unittest2.TestSuite() + for test_module in test_modules: + suite.addTests(unittest2.TestLoader().loadTestsFromModule(test_module)) + r = unittest2.TextTestRunner(verbosity=2).run(suite) + if r.errors or r.failures: + sys.exit(1) + else: + print 'Test modules:' + for test_module in test_modules: + print ' ', test_module.__name__ + +def add_parser(subparsers): + parser = subparsers.add_parser('run-tests', + description='Run the OpenERP server and/or addons tests.') + parser.add_argument('-d', '--database', metavar='DATABASE', required=True, + help='the database to test. Depending on the test suites, the ' + 'database must already exist or not.') + parser.add_argument('-p', '--port', metavar='PORT', + help='the port used for WML-RPC tests') + common.add_addons_argument(parser) + parser.add_argument('-m', '--module', metavar='MODULE', + default=None, + help='the module to test in `module[.submodule]` notation. ' + 'Use `openerp` for the core OpenERP tests. ' + 'Leave empty to run every declared tests. ' + 'Give a module but no submodule to run all the module\'s declared ' + 'tests. If both the module and the submodule are given, ' + 'the sub-module can be run even if it is not declared in the module.') + parser.add_argument('--fast-suite', action='store_true', + help='run only the tests explicitely declared in the fast suite (this ' + 'makes sense only with the bare `module` notation or no module at ' + 'all).') + parser.add_argument('--sanity-checks', action='store_true', + help='run only the sanity check tests') + parser.add_argument('--dry-run', action='store_true', + help='do not run the tests') + + parser.set_defaults(run=run) diff --git a/openerpcommand/scaffold.py b/openerpcommand/scaffold.py new file mode 100644 index 00000000000..e590653c91b --- /dev/null +++ b/openerpcommand/scaffold.py @@ -0,0 +1,77 @@ +""" +Generate an OpenERP module skeleton. +""" + +import os +import sys + +def run(args): + assert args.module + module = args.module + + if os.path.exists(module): + print "The path `%s` already exists." + sys.exit(1) + + os.mkdir(module) + os.mkdir(os.path.join(module, 'models')) + with open(os.path.join(module, '__openerp__.py'), 'w') as h: + h.write(MANIFEST) + with open(os.path.join(module, '__init__.py'), 'w') as h: + h.write(INIT_PY) + with open(os.path.join(module, 'models', '__init__.py'), 'w') as h: + h.write(MODELS_PY % (module,)) + +def add_parser(subparsers): + parser = subparsers.add_parser('scaffold', + description='Generate an OpenERP module skeleton.') + parser.add_argument('module', metavar='MODULE', + help='the name of the generated module') + + parser.set_defaults(run=run) + +MANIFEST = """\ +# -*- coding: utf-8 -*- +{ + 'name': '', + 'version': '0.0', + 'category': '', + 'description': ''' + +''', + 'author': '', + 'maintainer': '', + 'website': 'http://', + # Add any module that are necessary for this module to correctly work in + # the `depends` list. + 'depends': ['base'], + 'data': [ + ], + 'test': [ + ], + # Set to False if you want to prevent the module to be known by OpenERP + # (and thus appearing in the list of modules). + 'installable': True, + # Set to True if you want the module to be automatically whenever all its + # dependencies are installed. + 'auto_install': False, +} +""" + +INIT_PY = """\ +# -*- coding: utf-8 -*- +import models +""" + +MODELS_PY = """\ +# -*- coding: utf-8 -*- +import openerp + +# Define a new model. +class my_model(openerp.osv.osv.Model): + + _name = '%s.my_model' + + _columns = { + } +""" diff --git a/openerpcommand/uninstall.py b/openerpcommand/uninstall.py new file mode 100644 index 00000000000..9d5d93bda05 --- /dev/null +++ b/openerpcommand/uninstall.py @@ -0,0 +1,67 @@ +""" +Install OpenERP on a new (by default) database. +""" +import os +import sys + +import common + +# TODO turn template1 in a parameter +# This should be exposed from openerp (currently in +# openerp/service/web_services.py). +def create_database(database_name): + import openerp + db = openerp.sql_db.db_connect('template1') + cr = db.cursor() # TODO `with db as cr:` + try: + cr.autocommit(True) + cr.execute("""CREATE DATABASE "%s" + ENCODING 'unicode' TEMPLATE "template1" """ \ + % (database_name,)) + finally: + cr.close() + +def run(args): + assert args.database + assert args.module + + import openerp + + config = openerp.tools.config + config['log_handler'] = [':CRITICAL'] + if args.addons: + args.addons = args.addons.split(':') + else: + args.addons = [] + config['addons_path'] = ','.join(args.addons) + openerp.netsvc.init_logger() + + # Install the import hook, to import openerp.addons.. + openerp.modules.module.initialize_sys_path() + openerp.modules.loading.open_openerp_namespace() + + registry = openerp.modules.registry.RegistryManager.get( + args.database, update_module=False) + + ir_module_module = registry.get('ir.module.module') + cr = registry.db.cursor() # TODO context manager + try: + ids = ir_module_module.search(cr, openerp.SUPERUSER_ID, [('name', 'in', args.module), ('state', '=', 'installed')], {}) + if len(ids) == len(args.module): + ir_module_module.button_immediate_uninstall(cr, openerp.SUPERUSER_ID, ids, {}) + else: + print "At least one module not found (database `%s`)." % (args.database,) + finally: + cr.close() + +def add_parser(subparsers): + parser = subparsers.add_parser('uninstall', + description='Uninstall some modules from an OpenERP database.') + parser.add_argument('-d', '--database', metavar='DATABASE', + **common.required_or_default('DATABASE', 'the database to modify')) + common.add_addons_argument(parser) + parser.add_argument('--module', metavar='MODULE', action='append', + help='specify a module to uninstall' + ' (this option can be repeated)') + + parser.set_defaults(run=run) diff --git a/openerpcommand/update.py b/openerpcommand/update.py new file mode 100644 index 00000000000..66a47dc5692 --- /dev/null +++ b/openerpcommand/update.py @@ -0,0 +1,19 @@ +""" +Update an existing OpenERP database. +""" + +def run(args): + assert args.database + import openerp + config = openerp.tools.config + config['update']['all'] = 1 + openerp.modules.registry.RegistryManager.get( + args.database, update_module=True) + +def add_parser(subparsers): + parser = subparsers.add_parser('update', + description='Update an existing OpenERP database.') + parser.add_argument('-d', '--database', metavar='DATABASE', required=True, + help='the database to update') + + parser.set_defaults(run=run)