[MERGE] with trunk

bzr revid: nco@tinyerp.com-20121210070332-6h5qnlcf4whjv7p5
This commit is contained in:
Nimesh (Open ERP) 2012-12-10 12:33:32 +05:30
commit 480e116b40
45 changed files with 2684 additions and 2353 deletions

View File

@ -1,19 +0,0 @@
#! /usr/bin/env python
# -*- coding: UTF-8 -*-
import os
import sys
if __name__ == "__main__":
print '-' * 70
print "DEPRECATED: you are starting the OpenERP server with its old path,"
print "please use the new executable (available in the parent directory)."
print '-' * 70
# Change to the parent directory ...
os.chdir(os.path.normpath(os.path.dirname(__file__)))
os.chdir('..')
# ... and execute the new executable.
os.execv('openerp-server', sys.argv)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

2
debian/control vendored
View File

@ -23,6 +23,7 @@ Depends:
python-libxslt1,
python-lxml,
python-mako,
python-mock,
python-openid,
python-psutil,
python-psycopg2,
@ -33,6 +34,7 @@ Depends:
python-reportlab,
python-simplejson,
python-tz,
python-unittest2,
python-vatnumber,
python-vobject,
python-webdav,

View File

@ -40,7 +40,6 @@ import service
import sql_db
import test
import tools
import wizard
import workflow
# backward compatilbility
# TODO: This is for the web addons, can be removed later.

View File

@ -7,14 +7,14 @@ msgstr ""
"Project-Id-Version: OpenERP Server 5.0.4\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2012-12-03 16:01+0000\n"
"PO-Revision-Date: 2012-08-20 15:29+0000\n"
"Last-Translator: OpenERP Administrators <Unknown>\n"
"PO-Revision-Date: 2012-12-09 23:26+0000\n"
"Last-Translator: lambdasoftware <development@lambdasoftware.net>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-12-04 05:00+0000\n"
"X-Generator: Launchpad (build 16335)\n"
"X-Launchpad-Export-Date: 2012-12-10 04:36+0000\n"
"X-Generator: Launchpad (build 16341)\n"
#. module: base
#: model:ir.module.module,description:base.module_account_check_writing
@ -24,6 +24,10 @@ msgid ""
"================================================\n"
" "
msgstr ""
"\n"
"Módulo para la emisión e impresión de cheques\n"
"================================================\n"
" "
#. module: base
#: model:res.country,name:base.sh
@ -59,7 +63,7 @@ msgstr "Estructura de la vista"
#. module: base
#: model:ir.module.module,summary:base.module_sale_stock
msgid "Quotation, Sale Orders, Delivery & Invoicing Control"
msgstr ""
msgstr "Presupuesto, órdenes de venta, entrega y control de facturación"
#. module: base
#: selection:ir.sequence,implementation:0
@ -88,7 +92,7 @@ msgstr ""
#. module: base
#: model:ir.module.module,summary:base.module_point_of_sale
msgid "Touchscreen Interface for Shops"
msgstr ""
msgstr "Interfaz de pantalla táctil para tiendas"
#. module: base
#: model:ir.module.module,shortdesc:base.module_l10n_in_hr_payroll
@ -140,7 +144,7 @@ msgstr ""
#. module: base
#: help:res.partner,employee:0
msgid "Check this box if this contact is an Employee."
msgstr ""
msgstr "Marque si el contacto es un empleado"
#. module: base
#: help:ir.model.fields,domain:0
@ -171,7 +175,7 @@ msgstr "Ventana destino"
#. module: base
#: field:ir.actions.report.xml,report_rml:0
msgid "Main Report File Path"
msgstr ""
msgstr "Ruta de archivo del informe principal"
#. module: base
#: model:ir.module.module,shortdesc:base.module_sale_analytic_plans
@ -269,7 +273,7 @@ msgstr "creado."
#. module: base
#: field:ir.actions.report.xml,report_xsl:0
msgid "XSL Path"
msgstr ""
msgstr "Ruta XSL"
#. module: base
#: model:ir.module.module,shortdesc:base.module_l10n_tr
@ -295,7 +299,7 @@ msgstr "Inuktitut / ᐃᓄᒃᑎᑐᑦ"
#. module: base
#: model:res.groups,name:base.group_multi_currency
msgid "Multi Currencies"
msgstr ""
msgstr "Multidivisas"
#. module: base
#: model:ir.module.module,description:base.module_l10n_cl
@ -319,6 +323,7 @@ msgid ""
"The internal user that is in charge of communicating with this contact if "
"any."
msgstr ""
"El usuario interno encargado de comunicarse con este contacto si lo hubiese."
#. module: base
#: view:res.partner:0
@ -531,12 +536,12 @@ msgstr ""
#. module: base
#: field:ir.model.relation,name:0
msgid "Relation Name"
msgstr ""
msgstr "Nombre de relación"
#. module: base
#: view:ir.rule:0
msgid "Create Access Right"
msgstr ""
msgstr "Otorge derecho de acceso"
#. module: base
#: model:res.country,name:base.tv
@ -576,7 +581,7 @@ msgstr ""
#. module: base
#: view:workflow.transition:0
msgid "Workflow Transition"
msgstr ""
msgstr "Transición del flujo de trabajo"
#. module: base
#: model:res.country,name:base.gf
@ -699,7 +704,7 @@ msgstr "Colombia"
#. module: base
#: model:res.partner.title,name:base.res_partner_title_mister
msgid "Mister"
msgstr ""
msgstr "Señor"
#. module: base
#: help:res.country,code:0
@ -728,7 +733,7 @@ msgstr "Sin traducir"
#. module: base
#: view:ir.mail_server:0
msgid "Outgoing Mail Server"
msgstr ""
msgstr "Servidor de correo saliente"
#. module: base
#: help:ir.actions.act_window,context:0
@ -922,7 +927,7 @@ msgstr "Sueco / svenska"
#: field:base.language.export,name:0
#: field:ir.attachment,datas_fname:0
msgid "File Name"
msgstr ""
msgstr "Nombre del archivo"
#. module: base
#: model:res.country,name:base.rs
@ -1019,7 +1024,7 @@ msgstr "Preferencias de email"
#: code:addons/base/ir/ir_fields.py:196
#, python-format
msgid "'%s' does not seem to be a valid date for field '%%(field)s'"
msgstr ""
msgstr "'%s' no parece ser una fecha valida para el campo '%%(field)s'"
#. module: base
#: view:res.partner:0
@ -1151,7 +1156,7 @@ msgstr "Usuarios google"
#. module: base
#: model:ir.module.module,shortdesc:base.module_fleet
msgid "Fleet Management"
msgstr ""
msgstr "Gestión de flotas"
#. module: base
#: help:ir.server.object.lines,value:0
@ -2716,7 +2721,7 @@ msgstr "Nombre de acceso rápido"
#. module: base
#: field:res.partner,contact_address:0
msgid "Complete Address"
msgstr ""
msgstr "Dirección completa"
#. module: base
#: help:ir.actions.act_window,limit:0
@ -2788,7 +2793,7 @@ msgstr "ID de registro"
#. module: base
#: view:ir.filters:0
msgid "My Filters"
msgstr ""
msgstr "Mis filtros"
#. module: base
#: field:ir.actions.server,email:0
@ -2874,7 +2879,7 @@ msgstr "Responsable"
#: code:addons/base/ir/ir_model.py:718
#, python-format
msgid "Sorry, you are not allowed to access this document."
msgstr ""
msgstr "Lo siento, no está autorizado para acceder a este documento."
#. module: base
#: model:res.country,name:base.py
@ -2889,7 +2894,7 @@ msgstr "Fiji"
#. module: base
#: view:ir.actions.report.xml:0
msgid "Report Xml"
msgstr ""
msgstr "Informe Xml"
#. module: base
#: model:ir.module.module,description:base.module_purchase
@ -2960,7 +2965,7 @@ msgstr "Hededado"
#: code:addons/base/ir/ir_fields.py:147
#, python-format
msgid "yes"
msgstr ""
msgstr ""
#. module: base
#: field:ir.model.fields,serialization_field_id:0
@ -3051,7 +3056,7 @@ msgstr ""
#. module: base
#: model:ir.module.module,shortdesc:base.module_web_linkedin
msgid "LinkedIn Integration"
msgstr ""
msgstr "Integración con LinkedIn"
#. module: base
#: code:addons/orm.py:2021
@ -3365,7 +3370,7 @@ msgstr "Calendario"
#. module: base
#: model:ir.module.category,name:base.module_category_knowledge_management
msgid "Knowledge"
msgstr ""
msgstr "Conocimiento"
#. module: base
#: field:workflow.activity,signal_send:0
@ -3530,7 +3535,7 @@ msgstr "workflow.actividad"
#. module: base
#: view:base.language.export:0
msgid "Export Complete"
msgstr ""
msgstr "Exportación completada"
#. module: base
#: help:ir.ui.view_sc,res_id:0
@ -3559,7 +3564,7 @@ msgstr "Finlandés / Suomi"
#. module: base
#: view:ir.config_parameter:0
msgid "System Properties"
msgstr ""
msgstr "Propiedades de sistema"
#. module: base
#: field:ir.sequence,prefix:0
@ -3616,7 +3621,7 @@ msgstr ""
#. module: base
#: field:base.language.export,modules:0
msgid "Modules To Export"
msgstr ""
msgstr "Módulos a exportar"
#. module: base
#: model:res.country,name:base.mt
@ -3719,7 +3724,7 @@ msgstr "Antártida"
#. module: base
#: view:res.partner:0
msgid "Persons"
msgstr ""
msgstr "Personas"
#. module: base
#: view:base.language.import:0
@ -3898,7 +3903,7 @@ msgstr "Tayiko / اردو"
#: code:addons/orm.py:3870
#, python-format
msgid "Access Denied"
msgstr ""
msgstr "Acceso denegado"
#. module: base
#: field:res.company,name:0
@ -3991,7 +3996,7 @@ msgstr "%x - Representación apropiada de fecha."
#. module: base
#: view:res.partner:0
msgid "Tag"
msgstr ""
msgstr "Etiqueta"
#. module: base
#: view:res.lang:0
@ -4043,7 +4048,7 @@ msgstr ""
#. module: base
#: model:res.country,name:base.sk
msgid "Slovakia"
msgstr ""
msgstr "Eslovaquia"
#. module: base
#: model:res.country,name:base.nr
@ -4594,7 +4599,7 @@ msgstr ""
#. module: base
#: field:ir.module.module,latest_version:0
msgid "Installed Version"
msgstr ""
msgstr "Versión instalada"
#. module: base
#: field:ir.module.module,license:0
@ -4890,7 +4895,7 @@ msgstr "Flujos"
#. module: base
#: model:ir.ui.menu,name:base.next_id_73
msgid "Purchase"
msgstr ""
msgstr "Compra"
#. module: base
#: selection:base.language.install,lang:0
@ -5049,7 +5054,7 @@ msgstr "Establecer a NULL"
#. module: base
#: view:res.users:0
msgid "Save"
msgstr ""
msgstr "Guardar"
#. module: base
#: field:ir.actions.report.xml,report_xml:0
@ -5243,7 +5248,7 @@ msgstr "Tasas"
#. module: base
#: model:ir.module.module,shortdesc:base.module_email_template
msgid "Email Templates"
msgstr ""
msgstr "Plantillas de correo electrónico"
#. module: base
#: model:res.country,name:base.sy
@ -5392,7 +5397,7 @@ msgstr ""
#. module: base
#: view:ir.rule:0
msgid "Write Access Right"
msgstr ""
msgstr "Permiso de escritura"
#. module: base
#: model:ir.actions.act_window,help:base.action_res_groups
@ -5427,7 +5432,7 @@ msgstr "Historial"
#. module: base
#: model:res.country,name:base.im
msgid "Isle of Man"
msgstr ""
msgstr "Isla de Man"
#. module: base
#: help:ir.actions.client,res_model:0
@ -6104,7 +6109,7 @@ msgstr "Nombre del recurso"
#. module: base
#: field:res.partner,is_company:0
msgid "Is a Company"
msgstr ""
msgstr "¿Es una empresa?"
#. module: base
#: selection:ir.cron,interval_type:0
@ -6184,7 +6189,7 @@ msgstr ""
#. module: base
#: sql_constraint:ir.filters:0
msgid "Filter names must be unique"
msgstr ""
msgstr "Los nombres de filtro deben ser únicos"
#. module: base
#: help:multi_company.default,object_id:0
@ -6199,7 +6204,7 @@ msgstr ""
#. module: base
#: field:ir.filters,is_default:0
msgid "Default filter"
msgstr ""
msgstr "Filtro por defecto"
#. module: base
#: report:ir.module.reference:0
@ -6927,7 +6932,7 @@ msgstr ""
#. module: base
#: model:ir.module.module,shortdesc:base.module_auth_anonymous
msgid "Anonymous"
msgstr ""
msgstr "Anónimo"
#. module: base
#: model:ir.module.module,description:base.module_base_import
@ -7434,7 +7439,7 @@ msgstr ""
#. module: base
#: model:res.partner.category,name:base.res_partner_category_14
msgid "Manufacturer"
msgstr ""
msgstr "Fabricante"
#. module: base
#: help:res.users,company_id:0
@ -8050,7 +8055,7 @@ msgstr "Francés (CH) / Français (CH)"
#. module: base
#: model:res.partner.category,name:base.res_partner_category_13
msgid "Distributor"
msgstr ""
msgstr "Distribuidor"
#. module: base
#: help:ir.actions.server,subject:0
@ -15506,7 +15511,7 @@ msgstr ""
#. module: base
#: view:res.partner:0
msgid "Internal Notes"
msgstr ""
msgstr "Notas internas"
#. module: base
#: selection:res.partner.address,type:0
@ -15548,7 +15553,7 @@ msgstr "Empresas: "
#. module: base
#: view:res.partner:0
msgid "Is a Company?"
msgstr ""
msgstr "¿Es una empresa?"
#. module: base
#: code:addons/base/res/res_company.py:159
@ -15570,7 +15575,7 @@ msgstr "Crear objeto"
#. module: base
#: model:res.country,name:base.ss
msgid "South Sudan"
msgstr ""
msgstr "Sudán del Sur"
#. module: base
#: field:ir.filters,context:0

File diff suppressed because it is too large Load Diff

View File

@ -7,14 +7,14 @@ msgstr ""
"Project-Id-Version: OpenERP Server 5.0.4\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2012-12-03 16:01+0000\n"
"PO-Revision-Date: 2012-12-03 11:15+0000\n"
"PO-Revision-Date: 2012-12-06 10:07+0000\n"
"Last-Translator: Quentin THEURET <Unknown>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-12-04 04:55+0000\n"
"X-Generator: Launchpad (build 16335)\n"
"X-Launchpad-Export-Date: 2012-12-07 04:34+0000\n"
"X-Generator: Launchpad (build 16341)\n"
#. module: base
#: model:ir.module.module,description:base.module_account_check_writing
@ -26,7 +26,7 @@ msgid ""
msgstr ""
"\n"
"Module pour l'écriture et l'impression de chèques.\n"
"================================================\n"
"==================================================\n"
" "
#. module: base
@ -129,10 +129,10 @@ msgid ""
msgstr ""
"\n"
"Ce module ajoute des fabricants et des attributs sur le formulaire produit.\n"
"==========================================================\n"
"===========================================================================\n"
"\n"
"Vous pouvez maintenant définir pour un produit:\n"
"-------------------------------------------------------------------\n"
"-----------------------------------------------\n"
" * un fabricant\n"
" * le nom du produit chez le fabricant\n"
" * le code produit chez le fabricant\n"
@ -153,7 +153,7 @@ msgid ""
msgstr ""
"\n"
"Ce module ajoute les utilisateurs Google dans res.user.\n"
"========================================\n"
"=======================================================\n"
#. module: base
#: help:res.partner,employee:0
@ -212,7 +212,7 @@ msgid ""
msgstr ""
"\n"
"Génère vos factures à partir des dépenses et des feuilles de temps.\n"
"=====================================================\n"
"===================================================================\n"
"\n"
"Module pour générer les factures basées sur les coûts (RH, dépenses…).\n"
"\n"
@ -253,8 +253,8 @@ msgid ""
"* Opportunities by Stage (graph)\n"
msgstr ""
"\n"
"Le module générique de Gestion de la Relation Client d'OpenERP\n"
"===================================================\n"
"Le module générique de Gestion de la Relation Client d'OpenERP (CRM)\n"
"====================================================================\n"
"Cette application permet à un groupe de personnes de gérer intelligemment et "
"efficacement les pistes, les opportunités, les réunions et les appels "
"téléphoniques.\n"
@ -277,8 +277,8 @@ msgstr ""
"automatiquement à l'équipe appropriée et s'assurer que les correspondances "
"futures se fassent au bon endroit.\n"
"\n"
"Le tableau de bord GRC inclura :\n"
"--------------------------------------------\n"
"Le tableau de bord CRM inclura :\n"
"--------------------------------\n"
"* Le revenu planifié par étape et par utilisateur (graphique)\n"
"* Les opportunités par étape (graphique)\n"
@ -364,7 +364,7 @@ msgid ""
msgstr ""
"\n"
"Comptabilité chilienne et mise en place des taxes pour le Chili\n"
"================================================\n"
"===============================================================\n"
"Plan comptable chilien et taxes en accord avec la législation en vigueur.\n"
"\n"
" "
@ -418,10 +418,10 @@ msgid ""
msgstr ""
"\n"
"Ajoute des informations de date additionnelles au commandes de vente.\n"
"==========================================================\n"
"=====================================================================\n"
"\n"
"Vous pouvez ajouter les dates suivantes à un bon de commande:\n"
"-----------------------------------------------------------\n"
"-------------------------------------------------------------\n"
" * Date de la demande\n"
" * Date de confirmation\n"
" * Date Effective\n"
@ -486,7 +486,7 @@ msgid ""
msgstr ""
"\n"
"Installateur invisible pour la base de connaissances\n"
"==========================================\n"
"====================================================\n"
"\n"
"Rend disponible la configuration de lapplication des connaissances depuis "
"l'endroit où vous\n"
@ -514,7 +514,7 @@ msgstr ""
"Permet d'ajouter des méthodes de livraison dans les commandes de vente et "
"dans les livraisons.\n"
"============================================================================="
"\n"
"=================\n"
"\n"
"Vous pouvez définir vos propres transporteurs et grilles de prix de "
"livraison. Quand vous créez \n"
@ -701,7 +701,7 @@ msgid ""
msgstr ""
"\n"
"Module de définition des comptes analytiques.\n"
"========================================\n"
"=============================================\n"
"\n"
"Dans OpenERP, les comptes analytiques sont liés à des comptes financiers "
"mais sont traités\n"
@ -734,14 +734,14 @@ msgid ""
msgstr ""
"\n"
"Gestion et organisation d'événements\n"
"=================================\n"
"====================================\n"
"\n"
"Le module 'event' vous permet d'organiser efficacement des événements et "
"toutes les tâches liées : planification, suivi des inscriptions,\n"
"des présences, etc.\n"
"\n"
"Fonctionnalités clés\n"
"----------------------------\n"
"--------------------\n"
"* Gestion des événements et des inscriptions\n"
"* Utilisation d'e-mails pour automatiquement confirmer et envoyer des "
"accusés de réception pour chaque inscription à un événement\n"
@ -941,10 +941,10 @@ msgid ""
msgstr ""
"\n"
"Comptabilité et gestion financière\n"
"================================\n"
"==================================\n"
"\n"
"Le module de comptabilité et de finance couvre :\n"
"---------------------------------------------------------------------\n"
"------------------------------------------------\n"
" * Comptabilité générale\n"
" * Comptabilité de coût / comptabilité analytique\n"
" * Comptabilité de tiers\n"
@ -955,8 +955,7 @@ msgstr ""
" * Lettrages par partenaire\n"
"\n"
"Il créé également un tableau de bord pour les comptables qui inclut :\n"
"-----------------------------------------------------------------------------"
"------------------\n"
"---------------------------------------------------------------------\n"
" * Une liste des factures clients à approuver\n"
" * L'analyse de la société\n"
" * Un graphe de la trésorerie\n"
@ -1008,7 +1007,7 @@ msgid ""
msgstr ""
"\n"
"Module LinkedIn pour OpenERP Web.\n"
"===============================\n"
"=================================\n"
"Ce module fournit l'intégration de LinkedIn avec OpenERP.\n"
" "
@ -1153,6 +1152,33 @@ msgid ""
"also possible in order to automatically create a meeting when a holiday "
"request is accepted by setting up a type of meeting in Leave Type.\n"
msgstr ""
"\n"
"Gère les absences et les demandes de congés\n"
"===========================================\n"
"\n"
"Cette application contrôle la programmation des congés de votre société. Il "
"permet aux employés de faire des demandes de congés. Ensuite, les managers "
"peuvent examiner les demandes et les approuver ou les rejeter. De cette "
"façon, vous pouvez contrôler le planning de l'ensemble des congés de votre "
"société ou de votre département.\n"
"\n"
"Vous pouvez configurer plusieurs sortes d'absences (maladie, congés payés, "
"congés sans soldes…) et allouer rapidement ces absences à un employé ou un "
"département en utilisant les demandes de congés. Un employée peut aussi "
"faire une demande de congés pour plus de jours en ajoutant une allocation. "
"Cela va augmenter le total des jours disponibles pour ce type de congés (si "
"la demande est accepté).\n"
"\n"
"Vous pouvez suivre les absences de différentes façons grâce aux rapports "
"suivants : \n"
"\n"
"* Résumé des absences\n"
"* Absences par département\n"
"* Analyse des absences\n"
"\n"
"Une synchronisation avec les agendas internes (Réunions du module CRM) est "
"aussi possible dans le but de créer automatiquement une réunion quand une "
"demande de congés est accepté en ajoutant une type de réunion Absence.\n"
#. module: base
#: selection:base.language.install,lang:0
@ -1257,6 +1283,13 @@ msgid ""
" * the Tax Code Chart for Luxembourg\n"
" * the main taxes used in Luxembourg"
msgstr ""
"\n"
"Ceci est le module de base pour gérer le plan de comptes pour le Luxembourg\n"
"===========================================================================\n"
"\n"
" * le plan de compte KLUWER\n"
" * le plan de codes de taxes pour le Luxembourg\n"
" * les taxes principales utilisées au Luxembourg"
#. module: base
#: model:res.country,name:base.om
@ -1281,7 +1314,7 @@ msgid ""
msgstr ""
"\n"
"Module de pointage des heures pour les employés.\n"
"==========================================\n"
"================================================\n"
"\n"
"Comptabilise les temps de présence des employés sur la base des\n"
"pointages (Entrée / Sorties) réalisés.\n"
@ -1725,7 +1758,7 @@ msgstr ""
"dans la configuration du serveur.\n"
"\n"
"Paramètres de configuration du serveur :\n"
"-------------------------------\n"
"----------------------------------------\n"
"[webdav]:\n"
"+++++++++ \n"
" * enable = True ; Sert le webdav à travers les serveurs http(s)\n"
@ -1801,7 +1834,7 @@ msgstr ""
"Ce module ajoute un menu pour les réclamations et les fonctionnalités pour "
"votre portail si les modules claim et portal sont installés.\n"
"============================================================================="
"=======================================\n"
"==========================================================\n"
" "
#. module: base
@ -1895,7 +1928,7 @@ msgid ""
msgstr ""
"\n"
"Le module de base pour gérer les repas.\n"
"====================================\n"
"=======================================\n"
"\n"
"Des sociétés commandent des sandwiches, des pizzas et d'autres repas, à des "
"fournisseurs, pour leurs employés pour leur offrir plus de commodités.\n"
@ -2114,7 +2147,7 @@ msgstr ""
"Ce module fournit le plan comptable standard pour l'Autriche qui est basé "
"sur le modèle BMF.gv.at.\n"
"============================================================================="
"======================== \n"
"===================== \n"
"Veuillez garder à l'esprit que vous devez le revoir et l'adapter avec votre "
"comptable avant de l'utiliser dans un environnement de production.\n"
@ -2137,7 +2170,7 @@ msgid ""
msgstr ""
"\n"
"Droits d'accès à la comptabilité\n"
"==========================\n"
"================================\n"
"Cela donne à l'administrateur les droits d'accès à toutes les "
"fonctionnalités comptables telles que les mouvements comptables et le plan "
"de comptes.\n"
@ -2234,7 +2267,7 @@ msgstr ""
"Ce module ajoute les outils de partage générique pour votre base de données "
"OpenERP actuelle.\n"
"============================================================================="
"============\n"
"================\n"
"\n"
"Il ajoute spécifiquement un bouton 'Partager' qui est disponible dans le "
"client web pour\n"
@ -2342,7 +2375,8 @@ msgstr ""
"\n"
"Synchronisation les entrées dans les tâches avec les entrées dans les "
"feuilles de temps.\n"
"=========================================================================\n"
"============================================================================="
"===========\n"
"\n"
"Ce module vous permet de transférer les entrées sous les tâches définies "
"pour la gestion de\n"
@ -2421,7 +2455,7 @@ msgid ""
msgstr ""
"\n"
"Module de base pour gérer le plan comptable pour l'Équateur dans OpenERP.\n"
"==============================================================\n"
"=========================================================================\n"
"\n"
"Module pour la comptabilité équatorienne\n"
" "
@ -2472,13 +2506,13 @@ msgid ""
msgstr ""
"\n"
"Gestion des devis et des commandes de vente\n"
"=========================================\n"
"===========================================\n"
"\n"
"Ce module fait le lien entre les ventes et les applications de gestion des "
"entrepôts.\n"
"\n"
"Préférences\n"
"-------------------\n"
"-----------\n"
"* Expédition : Choix de la livraison en une fois ou une livraison partielle\n"
"* Facturation : Choisir comment les factures vont être payées\n"
"* Incoterms : International Commercial terms\n"
@ -2677,6 +2711,29 @@ msgid ""
"* Maximal difference between timesheet and attendances\n"
" "
msgstr ""
"\n"
"Enregistrer et valider facilement les feuilles de temps et les présences\n"
"========================================================================\n"
"\n"
"Cette application fournie un nouvel écran vous permettant de gérer à la fois "
"les présences (entrées/sorties) et l'enregistrement du travail (feuille de "
"temps) par période. Les saisies dans la feuille de temps sont faites par les "
"employés chaque jour. À la fin de la période définie, les employés valident "
"leurs feuilles de temps et le responsable doit approuver les saisies de son "
"équipe. Les périodes sont définis dans le formulaire de la société et vous "
"pouvez les paramétrer sur une base mensuelle ou hebdomadaire.\n"
"\n"
"Le processus complet de la validation des feuilles de temps est :\n"
"-----------------------------------------------------------------\n"
"* Feuille en brouillon\n"
"* Confirmation de la feuille à la fin de la période par les employées\n"
"* Validation par le responsable du projet\n"
"\n"
"La validation peut être paramétrée dans la société :\n"
"----------------------------------------------------\n"
"* Durée de la période (jour, semaine, mois)\n"
"* Différence maximale entre la feuille de temps et les présences\n"
" "
#. module: base
#: code:addons/base/ir/ir_fields.py:342
@ -2844,7 +2901,7 @@ msgid ""
msgstr ""
"\n"
"Gestion de Support (Helpdesk).\n"
"====================\n"
"==============================\n"
"\n"
"Les dossiers et le traitement des réclamations sont bien suivis et tracés "
"avec le Helpdesk.\n"
@ -2932,11 +2989,25 @@ msgid ""
" * Date\n"
" "
msgstr ""
"\n"
"Définir des valeurs par défaut pour les comptes analytiques.\n"
"============================================================\n"
"\n"
"Permet de sélectionner automatiquement les comptes analytiques selon des "
"critères :\n"
"-----------------------------------------------------------------------------"
"------\n"
" * Article\n"
" * Partenaire\n"
" * Utilisateur\n"
" * Société\n"
" * Date\n"
" "
#. module: base
#: field:res.company,rml_header1:0
msgid "Company Slogan"
msgstr ""
msgstr "Slogan de la société"
#. module: base
#: model:res.country,name:base.bb
@ -2960,7 +3031,7 @@ msgstr ""
#. module: base
#: model:ir.module.module,shortdesc:base.module_auth_oauth_signup
msgid "Signup with OAuth2 Authentication"
msgstr ""
msgstr "Se connecter avec l'authentification OAuth2"
#. module: base
#: selection:ir.model,state:0
@ -3021,6 +3092,10 @@ msgid ""
"==================================\n"
" "
msgstr ""
"\n"
"États-Unis d'Amérique - Plan comptable.\n"
"=======================================\n"
" "
#. module: base
#: field:ir.actions.act_url,target:0
@ -3055,7 +3130,7 @@ msgstr "Nom du raccourci"
#. module: base
#: field:res.partner,contact_address:0
msgid "Complete Address"
msgstr ""
msgstr "Adresse complète"
#. module: base
#: help:ir.actions.act_window,limit:0
@ -3127,7 +3202,7 @@ msgstr "ID de lenregistrement"
#. module: base
#: view:ir.filters:0
msgid "My Filters"
msgstr ""
msgstr "Mes filtres"
#. module: base
#: field:ir.actions.server,email:0
@ -3141,12 +3216,16 @@ msgid ""
"Module to attach a google document to any model.\n"
"================================================\n"
msgstr ""
"\n"
"Module pour attacher des documents Google à n'importe quel objet.\n"
"=================================================================\n"
#. module: base
#: code:addons/base/ir/ir_fields.py:334
#, python-format
msgid "Found multiple matches for field '%%(field)s' (%d matches)"
msgstr ""
"Plusieurs résultats ont été trouvés pour le champ '%%(field)s' (%d résultats)"
#. module: base
#: selection:base.language.install,lang:0
@ -3180,7 +3259,7 @@ msgstr "Arguments envoyés au client avec la vue"
#. module: base
#: model:ir.module.module,summary:base.module_contacts
msgid "Contacts, People and Companies"
msgstr ""
msgstr "Contacts, personnes et sociétés"
#. module: base
#: model:res.country,name:base.tt
@ -3213,7 +3292,7 @@ msgstr "Responsable"
#: code:addons/base/ir/ir_model.py:718
#, python-format
msgid "Sorry, you are not allowed to access this document."
msgstr ""
msgstr "Désolé, vous n'êtes pas autorisé à accéder à ce document."
#. module: base
#: model:res.country,name:base.py
@ -3329,7 +3408,7 @@ msgstr ""
#: code:addons/base/ir/ir_fields.py:175
#, python-format
msgid "'%s' does not seem to be an integer for field '%%(field)s'"
msgstr ""
msgstr "'%s' ne semble pas être un entier pour le champ '%%(field)s'"
#. module: base
#: model:ir.module.category,description:base.module_category_report_designer
@ -3390,7 +3469,7 @@ msgstr ""
#. module: base
#: model:ir.module.module,shortdesc:base.module_web_linkedin
msgid "LinkedIn Integration"
msgstr ""
msgstr "Intégration avec LinkedIn"
#. module: base
#: code:addons/orm.py:2021
@ -3439,6 +3518,8 @@ msgid ""
"the user will have an access to the sales configuration as well as statistic "
"reports."
msgstr ""
"l'utilisateur aura un accès à la configuration des ventes ainsi qu'aux "
"rapports statistiques."
#. module: base
#: model:res.country,name:base.nz
@ -3585,7 +3666,7 @@ msgstr "Arménie"
#. module: base
#: model:ir.module.module,summary:base.module_hr_evaluation
msgid "Periodical Evaluations, Appraisals, Surveys"
msgstr ""
msgstr "Évaluations périodiques, appréciations, sondages"
#. module: base
#: model:ir.actions.act_window,name:base.ir_property_form
@ -3625,6 +3706,31 @@ msgid ""
" payslip interface, but not in the payslip report\n"
" "
msgstr ""
"\n"
"Règles pour la paie française.\n"
"==============================\n"
"\n"
" - Configuration du module hr_payroll pour la localisation française\n"
" - Toutes les principales règles de contribution pour la fiche de paie "
"française, pour les cadres et les non-cadres\n"
" - Nouveau rapport des feuilles de paie\n"
"\n"
"Reste à faire :\n"
"---------------\n"
" - Intégration avec le module de gestion des congés pour la déduction des "
"congés et la gestion des indemnités\n"
" - Intégration avec le module hr_payroll_account pour la création des "
"lignes d'écritures \n"
" depuis la feuille de paie\n"
" - Continuer l'intégration des contributions. Seules les principales "
"contributions sont \n"
" actuellement implémentées.\n"
" - Retravailler le rapport sous webkit\n"
" - Les lignes de paie avec appears_in_payslip = False doivent apparaître "
"dans\n"
" l'interface des fiches de paie, mais pas dans le rapport des fiches de "
"paie.\n"
" "
#. module: base
#: model:res.country,name:base.se
@ -3634,7 +3740,7 @@ msgstr "Suède"
#. module: base
#: field:ir.actions.report.xml,report_file:0
msgid "Report File"
msgstr ""
msgstr "Fichier de rapport"
#. module: base
#: selection:ir.actions.act_window.view,view_mode:0
@ -3667,7 +3773,7 @@ msgstr ""
#: code:addons/orm.py:3839
#, python-format
msgid "Missing document(s)"
msgstr ""
msgstr "Document(s) manquant(s)"
#. module: base
#: model:ir.model,name:base.model_res_partner_bank_type
@ -3682,6 +3788,8 @@ msgid ""
"For more details about translating OpenERP in your language, please refer to "
"the"
msgstr ""
"Pour plus de détails concernant la traduction d'OpenERP dans votre langue, "
"veuillez vous référer à"
#. module: base
#: field:res.partner,image:0
@ -3807,6 +3915,28 @@ msgid ""
"database,\n"
" but in the servers rootpad like /server/bin/filestore.\n"
msgstr ""
"\n"
"Ceci est un système complet de gestion de documents.\n"
"=====================================================\n"
"\n"
" * Authentification des utilisateurs\n"
" * Indexation des documents ; les fichiers .pptx et .docx ne sont pas "
"supportés sous Windows.\n"
" * Tableau de bord des documents qui inclut :\n"
" * Nouveaux fichiers (liste)\n"
" * Fichiers par type de ressources (graphique)\n"
" * Fichiers par partenaire (graphique)\n"
" * Taille des fichiers par mois (graphique)\n"
"\n"
"ATTENTION :\n"
"-----------\n"
"\n"
" - Quand vous installez ce module en production dans une société qui a "
"déjà des fichiers PDF\n"
" enregistrés dans la base de données, ils seront tous perdus.\n"
" - Après l'installation de ce module les PDF ne seront plus enregistrés "
"dans la base de données,\n"
" mais à la racine du serveur dans /server/bin/filestore.\n"
#. module: base
#: help:res.currency,name:0
@ -3898,7 +4028,7 @@ msgstr "Finlandais / Suomi"
#. module: base
#: view:ir.config_parameter:0
msgid "System Properties"
msgstr ""
msgstr "Propriétés système"
#. module: base
#: field:ir.sequence,prefix:0
@ -3955,7 +4085,7 @@ msgstr "Personnel"
#. module: base
#: field:base.language.export,modules:0
msgid "Modules To Export"
msgstr ""
msgstr "Modules à exporter"
#. module: base
#: model:res.country,name:base.mt
@ -3968,6 +4098,8 @@ msgstr "Malte"
msgid ""
"Only users with the following access level are currently allowed to do that"
msgstr ""
"Seuls les utilisateurs avec les niveaux d'accès suivants sont actuellement "
"autorisé à faire cela"
#. module: base
#: field:ir.actions.server,fields_lines:0
@ -4128,7 +4260,7 @@ msgstr "De droite à gauche"
#. module: base
#: model:res.country,name:base.sx
msgid "Sint Maarten (Dutch part)"
msgstr ""
msgstr "Saint-Martin (partie néerlandaise)"
#. module: base
#: view:ir.actions.act_window:0

View File

@ -7,14 +7,14 @@ msgstr ""
"Project-Id-Version: OpenERP Server 5.0.4\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2012-12-03 16:01+0000\n"
"PO-Revision-Date: 2012-11-24 21:10+0000\n"
"PO-Revision-Date: 2012-12-09 19:36+0000\n"
"Last-Translator: Goran Kliska <gkliska@gmail.com>\n"
"Language-Team: openerp-translators\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-12-04 04:59+0000\n"
"X-Generator: Launchpad (build 16335)\n"
"X-Launchpad-Export-Date: 2012-12-10 04:36+0000\n"
"X-Generator: Launchpad (build 16341)\n"
"Language: hr\n"
#. module: base
@ -532,7 +532,7 @@ msgstr "Naziv relacije"
#. module: base
#: view:ir.rule:0
msgid "Create Access Right"
msgstr ""
msgstr "Kreiraj pravo pristupa"
#. module: base
#: model:res.country,name:base.tv
@ -582,7 +582,7 @@ msgstr "Francuska Guyana"
#. module: base
#: model:ir.module.module,summary:base.module_hr
msgid "Jobs, Departments, Employees Details"
msgstr ""
msgstr "Radna mjesta, odjeli, podaci o radnicima"
#. module: base
#: model:ir.module.module,description:base.module_analytic
@ -695,7 +695,7 @@ msgstr "Kolumbija"
#. module: base
#: model:res.partner.title,name:base.res_partner_title_mister
msgid "Mister"
msgstr ""
msgstr "Gospodin"
#. module: base
#: help:res.country,code:0
@ -828,6 +828,11 @@ msgid ""
"This module provides the Integration of the LinkedIn with OpenERP.\n"
" "
msgstr ""
"\n"
"OpenERP Web LinkedIn modul.\n"
"============================\n"
"Integracija LinkedIn-a sa OpenERP-om.\n"
" "
#. module: base
#: help:ir.actions.act_window,src_model:0
@ -1132,7 +1137,7 @@ msgstr "Google korisnici"
#. module: base
#: model:ir.module.module,shortdesc:base.module_fleet
msgid "Fleet Management"
msgstr ""
msgstr "Upravljanje flotom (automobili)"
#. module: base
#: help:ir.server.object.lines,value:0
@ -1234,7 +1239,7 @@ msgstr "Zadnja verzija"
#. module: base
#: view:ir.rule:0
msgid "Delete Access Right"
msgstr ""
msgstr "Prava brisanja"
#. module: base
#: code:addons/base/ir/ir_mail_server.py:213
@ -1304,7 +1309,7 @@ msgstr "Vidljivo"
#. module: base
#: model:ir.actions.client,name:base.action_client_base_menu
msgid "Open Settings Menu"
msgstr ""
msgstr "Otvori izbornik postavki"
#. module: base
#: selection:base.language.install,lang:0
@ -1427,7 +1432,7 @@ msgstr "Haiti"
#. module: base
#: model:ir.module.module,shortdesc:base.module_l10n_fr_hr_payroll
msgid "French Payroll"
msgstr ""
msgstr "Francuska plaća"
#. module: base
#: view:ir.ui.view:0
@ -2571,7 +2576,7 @@ msgstr "Grčka / Ελληνικά"
#. module: base
#: field:res.company,custom_footer:0
msgid "Custom Footer"
msgstr ""
msgstr "Prilagođeno podnožje"
#. module: base
#: model:ir.module.module,shortdesc:base.module_sale_crm
@ -2879,7 +2884,7 @@ msgstr "Naslijeđeno"
#: code:addons/base/ir/ir_fields.py:147
#, python-format
msgid "yes"
msgstr ""
msgstr "da"
#. module: base
#: field:ir.model.fields,serialization_field_id:0
@ -14826,7 +14831,7 @@ msgstr ""
#. module: base
#: field:ir.sequence,implementation:0
msgid "Implementation"
msgstr ""
msgstr "Implementacija"
#. module: base
#: model:ir.module.module,shortdesc:base.module_l10n_ve
@ -14931,7 +14936,7 @@ msgstr "Ažuriranje sustava"
#: field:ir.actions.report.xml,report_sxw_content:0
#: field:ir.actions.report.xml,report_sxw_content_data:0
msgid "SXW Content"
msgstr ""
msgstr "SXW Sadržaj"
#. module: base
#: help:ir.sequence,prefix:0
@ -14946,7 +14951,7 @@ msgstr "Sejšeli"
#. module: base
#: model:res.partner.category,name:base.res_partner_category_4
msgid "Gold"
msgstr ""
msgstr "Zlatni"
#. module: base
#: code:addons/base/res/res_company.py:159
@ -14973,7 +14978,7 @@ msgstr "Opći podaci"
#. module: base
#: field:ir.model.data,complete_name:0
msgid "Complete ID"
msgstr ""
msgstr "Puni ID"
#. module: base
#: model:res.country,name:base.tc
@ -14985,7 +14990,7 @@ msgstr "Turks and Caicos Islands"
msgid ""
"Tax Identification Number. Check the box if this contact is subjected to "
"taxes. Used by the some of the legal statements."
msgstr ""
msgstr "Porezni broj (OIB) ."
#. module: base
#: field:res.partner.bank,partner_id:0
@ -15070,7 +15075,7 @@ msgstr ""
#. module: base
#: view:res.partner:0
msgid "Internal Notes"
msgstr ""
msgstr "Interne bilješke"
#. module: base
#: selection:res.partner.address,type:0
@ -15086,7 +15091,7 @@ msgstr "Corp."
#. module: base
#: model:ir.module.module,shortdesc:base.module_purchase_requisition
msgid "Purchase Requisitions"
msgstr ""
msgstr "Natječaji u nabavi"
#. module: base
#: selection:ir.actions.act_window,target:0
@ -15112,7 +15117,7 @@ msgstr "Partneri: "
#. module: base
#: view:res.partner:0
msgid "Is a Company?"
msgstr ""
msgstr "Je tvrtka="
#. module: base
#: code:addons/base/res/res_company.py:159
@ -15134,7 +15139,7 @@ msgstr "Kreiraj objekt"
#. module: base
#: model:res.country,name:base.ss
msgid "South Sudan"
msgstr ""
msgstr "Južni Sudan"
#. module: base
#: field:ir.filters,context:0

View File

@ -7,14 +7,14 @@ msgstr ""
"Project-Id-Version: OpenERP Server 5.0.4\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2012-12-03 16:01+0000\n"
"PO-Revision-Date: 2012-12-01 13:29+0000\n"
"Last-Translator: Sergio Corato <Unknown>\n"
"PO-Revision-Date: 2012-12-09 12:05+0000\n"
"Last-Translator: Davide Corio - agilebg.com <davide.corio@agilebg.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-12-04 04:57+0000\n"
"X-Generator: Launchpad (build 16335)\n"
"X-Launchpad-Export-Date: 2012-12-10 04:36+0000\n"
"X-Generator: Launchpad (build 16341)\n"
#. module: base
#: model:ir.module.module,description:base.module_account_check_writing
@ -92,7 +92,7 @@ msgstr ""
#. module: base
#: model:ir.module.module,summary:base.module_point_of_sale
msgid "Touchscreen Interface for Shops"
msgstr "Interfaccia Touchscreen per Negozzi"
msgstr "Interfaccia Touchscreen per Negozi"
#. module: base
#: model:ir.module.module,shortdesc:base.module_l10n_in_hr_payroll
@ -140,6 +140,9 @@ msgid ""
"The module adds google user in res user.\n"
"========================================\n"
msgstr ""
"\n"
"Aggiunge lo username Google a res.user.\n"
"========================================\n"
#. module: base
#: help:res.partner,employee:0
@ -322,7 +325,7 @@ msgstr "Gestione Vendite"
msgid ""
"The internal user that is in charge of communicating with this contact if "
"any."
msgstr ""
msgstr "L'utente responsabile delle comunicazioni con questo contatto."
#. module: base
#: view:res.partner:0

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -7,14 +7,14 @@ msgstr ""
"Project-Id-Version: OpenERP Server 5.0.4\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2012-12-03 16:01+0000\n"
"PO-Revision-Date: 2012-12-01 11:25+0000\n"
"Last-Translator: Chertykov Denis <chertykov@gmail.com>\n"
"PO-Revision-Date: 2012-12-05 09:43+0000\n"
"Last-Translator: Denis Karataev <dskarataev@gmail.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-12-04 04:59+0000\n"
"X-Generator: Launchpad (build 16335)\n"
"X-Launchpad-Export-Date: 2012-12-06 04:38+0000\n"
"X-Generator: Launchpad (build 16341)\n"
#. module: base
#: model:ir.module.module,description:base.module_account_check_writing
@ -1638,7 +1638,7 @@ msgstr "Настройка нижнего колонтитула отчета"
#. module: base
#: field:ir.translation,comments:0
msgid "Translation comments"
msgstr ""
msgstr "Комментарии перевода"
#. module: base
#: model:ir.module.module,description:base.module_lunch
@ -5663,6 +5663,9 @@ msgid ""
" <p>You should try others search criteria.</p>\n"
" "
msgstr ""
"<p><b>Модули не найдены!</b></p>\n"
" <p>Попробуйте поискать по другим критериям.</p>\n"
" "
#. module: base
#: model:ir.model,name:base.model_ir_module_module
@ -7490,6 +7493,11 @@ msgid ""
"use the same timezone that is otherwise used to pick and render date and "
"time values: your computer's timezone."
msgstr ""
"Часовой пояс партнера, используемый для вывода правильной даты и времени "
"внутри распечатываемых отчетов. Важно установить значение этого поля. Вы "
"должны использовать тот же часовой пояс который есть, в противном случае "
"значения даты и времени будут вычисляться на основе часового пояса "
"установленного на компьютере."
#. module: base
#: model:ir.module.module,shortdesc:base.module_account_analytic_default
@ -8407,6 +8415,8 @@ msgid ""
"Translation features are unavailable until you install an extra OpenERP "
"translation."
msgstr ""
"Возможности перевода не доступны до тех пор, пока вы не установите "
"расширенный OpenERP перевод."
#. module: base
#: model:ir.module.module,description:base.module_l10n_nl

View File

@ -7,14 +7,14 @@ msgstr ""
"Project-Id-Version: OpenERP Server 5.0.4\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2012-12-03 16:01+0000\n"
"PO-Revision-Date: 2012-12-04 02:43+0000\n"
"PO-Revision-Date: 2012-12-06 18:17+0000\n"
"Last-Translator: ccdos <ccdos@163.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-12-04 05:03+0000\n"
"X-Generator: Launchpad (build 16335)\n"
"X-Launchpad-Export-Date: 2012-12-07 04:34+0000\n"
"X-Generator: Launchpad (build 16341)\n"
#. module: base
#: model:ir.module.module,description:base.module_account_check_writing
@ -88,7 +88,7 @@ msgstr "帮助您管理项目、跟踪任务、生成计划等"
#. module: base
#: model:ir.module.module,summary:base.module_point_of_sale
msgid "Touchscreen Interface for Shops"
msgstr "商店的触摸屏接口"
msgstr "触摸屏接口的POS 系统"
#. module: base
#: model:ir.module.module,shortdesc:base.module_l10n_in_hr_payroll
@ -896,6 +896,14 @@ msgid ""
" </p>\n"
" "
msgstr ""
"<p class=\"oe_view_nocontent_create\">\n"
" 点击此处在你的地址本中增加一个新的联系人。\n"
" </p><p>\n"
" Openerp可以帮助你轻松的跟踪所有与客户相关的活动\n"
" 例如讨论,商业机会的历史记录,\n"
" 文档,等等。\n"
" </p>\n"
" "
#. module: base
#: model:ir.module.module,description:base.module_web_linkedin
@ -2645,6 +2653,18 @@ msgid ""
" * Date\n"
" "
msgstr ""
"\n"
"为你的分析帐目设定缺省值。\n"
"==============================================\n"
"\n"
"基于以下规则时,允许自动选择分析帐目:\n"
"---------------------------------------------------------------------\n"
" * 产品\n"
" * 合作伙伴\n"
" * 用户\n"
" * 公司\n"
" * 日期\n"
" "
#. module: base
#: field:res.company,rml_header1:0
@ -4230,7 +4250,7 @@ msgstr "共享任意文档"
#. module: base
#: model:ir.module.module,summary:base.module_crm
msgid "Leads, Opportunities, Phone Calls"
msgstr "线索,上级,电话"
msgstr "线索,商机,电话"
#. module: base
#: view:res.lang:0
@ -7666,6 +7686,10 @@ msgid ""
"==========================================\n"
" "
msgstr ""
"\n"
"CRM 线索和商机的待办事项\n"
"==========================================\n"
" "
#. module: base
#: view:ir.mail_server:0
@ -9849,7 +9873,7 @@ msgstr "将要安装的模块"
#: model:ir.module.module,shortdesc:base.module_base
#: field:res.currency,base:0
msgid "Base"
msgstr "本位币"
msgstr "base 模块"
#. module: base
#: field:ir.model.data,model:0

View File

@ -42,7 +42,7 @@ class actions(osv.osv):
_order = 'name'
_columns = {
'name': fields.char('Name', size=64, required=True),
'type': fields.char('Action Type', required=True, size=32,readonly=True),
'type': fields.char('Action Type', required=True, size=32),
'usage': fields.char('Action Usage', size=32),
'help': fields.text('Action description',
help='Optional help text for the users with a description of the target view, such as its usage and purpose.',

View File

@ -502,6 +502,19 @@
<menuitem id="menu_ir_actions_todo" name="Configuration Wizards" parent="menu_custom" sequence="20" groups="base.group_no_one"/>
<menuitem id="menu_ir_actions_todo_form" action="act_ir_actions_todo_form" parent="menu_ir_actions_todo"/>
<record id="action_run_ir_action_todo" model="ir.actions.server">
<field name="name">Run Remaining Action Todo</field>
<field name="condition">True</field>
<field name="type">ir.actions.server</field>
<field name="model_id" ref="model_res_config"/>
<field name="state">code</field>
<field name="code">
config = self.next(cr, uid, [], context=context) or {}
if config.get('type') not in ('ir.actions.act_window_close',):
action = config
</field>
</record>
</data>
</openerp>

View File

@ -31,7 +31,6 @@ import netsvc
import openerp
import pooler
import tools
from openerp.cron import WAKE_UP_NOW
from osv import fields, osv
from tools import DEFAULT_SERVER_DATETIME_FORMAT
from tools.safe_eval import safe_eval as eval
@ -142,130 +141,6 @@ class ir_cron(osv.osv):
except Exception, e:
self._handle_callback_exception(cr, uid, model_name, method_name, args, job_id, e)
def _run_job(self, cr, job, now):
""" Run a given job taking care of the repetition.
The cursor has a lock on the job (aquired by _run_jobs_multithread()) and this
method is run in a worker thread (spawned by _run_jobs_multithread())).
:param job: job to be run (as a dictionary).
:param now: timestamp (result of datetime.now(), no need to call it multiple time).
"""
try:
nextcall = datetime.strptime(job['nextcall'], DEFAULT_SERVER_DATETIME_FORMAT)
numbercall = job['numbercall']
ok = False
while nextcall < now and numbercall:
if numbercall > 0:
numbercall -= 1
if not ok or job['doall']:
self._callback(cr, job['user_id'], job['model'], job['function'], job['args'], job['id'])
if numbercall:
nextcall += _intervalTypes[job['interval_type']](job['interval_number'])
ok = True
addsql = ''
if not numbercall:
addsql = ', active=False'
cr.execute("UPDATE ir_cron SET nextcall=%s, numbercall=%s"+addsql+" WHERE id=%s",
(nextcall.strftime(DEFAULT_SERVER_DATETIME_FORMAT), numbercall, job['id']))
if numbercall:
# Reschedule our own main cron thread if necessary.
# This is really needed if this job runs longer than its rescheduling period.
nextcall = calendar.timegm(nextcall.timetuple())
openerp.cron.schedule_wakeup(nextcall, cr.dbname)
finally:
cr.commit()
cr.close()
openerp.cron.release_thread_slot()
def _run_jobs_multithread(self):
# TODO remove 'check' argument from addons/base_action_rule/base_action_rule.py
""" Process the cron jobs by spawning worker threads.
This selects in database all the jobs that should be processed. It then
tries to lock each of them and, if it succeeds, spawns a thread to run
the cron job (if it doesn't succeed, it means the job was already
locked to be taken care of by another thread).
The cursor used to lock the job in database is given to the worker
thread (which has to close it itself).
"""
db = self.pool.db
cr = db.cursor()
db_name = db.dbname
try:
jobs = {} # mapping job ids to jobs for all jobs being processed.
now = datetime.now()
# Careful to compare timestamps with 'UTC' - everything is UTC as of v6.1.
cr.execute("""SELECT * FROM ir_cron
WHERE numbercall != 0
AND active AND nextcall <= (now() at time zone 'UTC')
ORDER BY priority""")
for job in cr.dictfetchall():
if not openerp.cron.get_thread_slots():
break
jobs[job['id']] = job
task_cr = db.cursor()
try:
# Try to grab an exclusive lock on the job row from within the task transaction
acquired_lock = False
task_cr.execute("""SELECT *
FROM ir_cron
WHERE id=%s
FOR UPDATE NOWAIT""",
(job['id'],), log_exceptions=False)
acquired_lock = True
except psycopg2.OperationalError, e:
if e.pgcode == '55P03':
# Class 55: Object not in prerequisite state; 55P03: lock_not_available
_logger.debug('Another process/thread is already busy executing job `%s`, skipping it.', job['name'])
continue
else:
# Unexpected OperationalError
raise
finally:
if not acquired_lock:
# we're exiting due to an exception while acquiring the lot
task_cr.close()
# Got the lock on the job row, now spawn a thread to execute it in the transaction with the lock
task_thread = threading.Thread(target=self._run_job, name=job['name'], args=(task_cr, job, now))
# force non-daemon task threads (the runner thread must be daemon, and this property is inherited by default)
task_thread.setDaemon(False)
openerp.cron.take_thread_slot()
task_thread.start()
_logger.debug('Cron execution thread for job `%s` spawned', job['name'])
# Find next earliest job ignoring currently processed jobs (by this and other cron threads)
find_next_time_query = """SELECT min(nextcall) AS min_next_call
FROM ir_cron WHERE numbercall != 0 AND active"""
if jobs:
cr.execute(find_next_time_query + " AND id NOT IN %s", (tuple(jobs.keys()),))
else:
cr.execute(find_next_time_query)
next_call = cr.dictfetchone()['min_next_call']
if next_call:
next_call = calendar.timegm(time.strptime(next_call, DEFAULT_SERVER_DATETIME_FORMAT))
else:
# no matching cron job found in database, re-schedule arbitrarily in 1 day,
# this delay will likely be modified when running jobs complete their tasks
next_call = time.time() + (24*3600)
openerp.cron.schedule_wakeup(next_call, db_name)
except Exception, ex:
_logger.warning('Exception in cron:', exc_info=True)
finally:
cr.commit()
cr.close()
def _process_job(self, cr, job):
""" Run a given job taking care of the repetition.
@ -365,19 +240,6 @@ class ir_cron(osv.osv):
return False
def update_running_cron(self, cr):
""" Schedule as soon as possible a wake-up for this database. """
# Verify whether the server is already started and thus whether we need to commit
# immediately our changes and restart the cron agent in order to apply the change
# immediately. The commit() is needed because as soon as the cron is (re)started it
# will query the database with its own cursor, possibly before the end of the
# current transaction.
# This commit() is not an issue in most cases, but we must absolutely avoid it
# when the server is only starting or loading modules (hence the test on pool._init).
if not self.pool._init:
cr.commit()
openerp.cron.schedule_wakeup(WAKE_UP_NOW, self.pool.db.dbname)
def _try_lock(self, cr, uid, ids, context=None):
"""Try to grab a dummy exclusive write-lock to the rows with the given ids,
to make sure a following write() or unlink() will not block due
@ -393,20 +255,16 @@ class ir_cron(osv.osv):
def create(self, cr, uid, vals, context=None):
res = super(ir_cron, self).create(cr, uid, vals, context=context)
self.update_running_cron(cr)
return res
def write(self, cr, uid, ids, vals, context=None):
self._try_lock(cr, uid, ids, context)
res = super(ir_cron, self).write(cr, uid, ids, vals, context=context)
self.update_running_cron(cr)
return res
def unlink(self, cr, uid, ids, context=None):
self._try_lock(cr, uid, ids, context)
res = super(ir_cron, self).unlink(cr, uid, ids, context=context)
self.update_running_cron(cr)
return res
ir_cron()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -209,6 +209,7 @@ class ir_model(osv.osv):
else:
x_name = a._columns.keys()[0]
x_custom_model._rec_name = x_name
a._rec_name = x_name
class ir_model_fields(osv.osv):
_name = 'ir.model.fields'

View File

@ -44,7 +44,7 @@
<field name="selection" attrs="{'required': [('ttype','in',['selection','reference'])], 'readonly': [('ttype','not in',['selection','reference'])]}"/>
<field name="size" attrs="{'required': [('ttype','in',['char','reference'])], 'readonly': [('ttype','not in',['char','reference'])]}"/>
<field name="domain" attrs="{'readonly': [('relation','=','')]}"/>
<field name="serialization_field_id" attrs="{'readonly': [('state','=','base')]}" domain="[('ttype','=','serialized'), ('model_id', '=', model_id)]"/>
<field name="serialization_field_id" attrs="{'readonly': [('state','=','base')]}" domain="[('ttype','=','serialized'), ('model_id', '=', parent.model)]"/>
</group>
<group>
<field name="required"/>

View File

@ -43,7 +43,7 @@ class ir_ui_menu(osv.osv):
def __init__(self, *args, **kwargs):
self.cache_lock = threading.RLock()
self.clear_cache()
self._cache = {}
r = super(ir_ui_menu, self).__init__(*args, **kwargs)
self.pool.get('ir.model.access').register_cache_clearing_method(self._name, 'clear_cache')
return r
@ -51,6 +51,10 @@ class ir_ui_menu(osv.osv):
def clear_cache(self):
with self.cache_lock:
# radical but this doesn't frequently happen
if self._cache:
# Normally this is done by openerp.tools.ormcache
# but since we do not use it, set it by ourself.
self.pool._any_cache_cleared = True
self._cache = {}
def _filter_visible_menus(self, cr, uid, ids, context=None):

View File

@ -30,6 +30,7 @@ import re
import urllib
import zipimport
import openerp
from openerp import modules, pooler, release, tools, addons
from openerp.modules.db import create_categories
from openerp.tools.parse_version import parse_version
@ -385,6 +386,8 @@ class module(osv.osv):
# Mark them to be installed.
if to_install_ids:
self.button_install(cr, uid, to_install_ids, context=context)
openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
return dict(ACTION_DICT, name=_('Install'))
def button_immediate_install(self, cr, uid, ids, context=None):

View File

@ -258,7 +258,7 @@
</group>
<group>
<field name="customer"/>
<field name="supplier" invisible="not context.get('default_supplier')"/>
<field name="supplier"/>
</group>
<group>
<field name="ref"/>

View File

@ -50,20 +50,25 @@ class test_ir_values(common.TransactionCase):
# Create some action bindings for a non-existing model.
act_id_1 = self.ref('base.act_values_form_action')
act_id_2 = self.ref('base.act_values_form_defaults')
act_id_3 = self.ref('base.action_res_company_form')
act_id_4 = self.ref('base.action_res_company_tree')
ir_values = self.registry('ir.values')
ir_values.set(self.cr, self.uid, 'action', 'tree_but_open', 'OnDblClick Action', ['unexisting_model'], 'ir.actions.act_window,10', isobject=True)
ir_values.set(self.cr, self.uid, 'action', 'tree_but_open', 'OnDblClick Action 2', ['unexisting_model'], 'ir.actions.act_window,11', isobject=True)
ir_values.set(self.cr, self.uid, 'action', 'client_action_multi', 'Side Wizard', ['unexisting_model'], 'ir.actions.act_window,12', isobject=True)
ir_values.set(self.cr, self.uid, 'action', 'tree_but_open', 'OnDblClick Action', ['unexisting_model'], 'ir.actions.act_window,%d' % act_id_1, isobject=True)
ir_values.set(self.cr, self.uid, 'action', 'tree_but_open', 'OnDblClick Action 2', ['unexisting_model'], 'ir.actions.act_window,%d' % act_id_2, isobject=True)
ir_values.set(self.cr, self.uid, 'action', 'client_action_multi', 'Side Wizard', ['unexisting_model'], 'ir.actions.act_window,%d' % act_id_3, isobject=True)
report_ids = self.registry('ir.actions.report.xml').search(self.cr, self.uid, [], {})
reports = self.registry('ir.actions.report.xml').browse(self.cr, self.uid, report_ids, {})
report_id = [report.id for report in reports if not report.groups_id][0] # assume at least one
ir_values.set(self.cr, self.uid, 'action', 'client_print_multi', 'Nice Report', ['unexisting_model'], 'ir.actions.report.xml,%s'%report_id, isobject=True)
ir_values.set(self.cr, self.uid, 'action', 'client_action_relate', 'Related Stuff', ['unexisting_model'], 'ir.actions.act_window,14', isobject=True)
report_id = [report.id for report in reports if not report.groups_id][0] # assume at least one
ir_values.set(self.cr, self.uid, 'action', 'client_print_multi', 'Nice Report', ['unexisting_model'], 'ir.actions.report.xml,%d' % report_id, isobject=True)
ir_values.set(self.cr, self.uid, 'action', 'client_action_relate', 'Related Stuff', ['unexisting_model'], 'ir.actions.act_window,%d' % act_id_4, isobject=True)
# Replace one action binding to set a new name.
ir_values = self.registry('ir.values')
ir_values.set(self.cr, self.uid, 'action', 'tree_but_open', 'OnDblClick Action New', ['unexisting_model'], 'ir.actions.act_window,10', isobject=True)
ir_values.set(self.cr, self.uid, 'action', 'tree_but_open', 'OnDblClick Action New', ['unexisting_model'], 'ir.actions.act_window,%d' % act_id_1, isobject=True)
# Retrieve the action bindings and check they're correct
@ -73,17 +78,17 @@ class test_ir_values(common.TransactionCase):
#first action
assert len(actions[0]) == 3, "Malformed action definition"
assert actions[0][1] == 'OnDblClick Action 2', 'Bound action does not match definition'
assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == 11, 'Bound action does not match definition'
assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == act_id_2, 'Bound action does not match definition'
#second action - this ones comes last because it was re-created with a different name
assert len(actions[1]) == 3, "Malformed action definition"
assert actions[1][1] == 'OnDblClick Action New', 'Re-Registering an action should replace it'
assert isinstance(actions[1][2], dict) and actions[1][2]['id'] == 10, 'Bound action does not match definition'
assert isinstance(actions[1][2], dict) and actions[1][2]['id'] == act_id_1, 'Bound action does not match definition'
actions = ir_values.get(self.cr, self.uid, 'action', 'client_action_multi', ['unexisting_model'])
assert len(actions) == 1, "Mismatching number of bound actions"
assert len(actions[0]) == 3, "Malformed action definition"
assert actions[0][1] == 'Side Wizard', 'Bound action does not match definition'
assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == 12, 'Bound action does not match definition'
assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == act_id_3, 'Bound action does not match definition'
actions = ir_values.get(self.cr, self.uid, 'action', 'client_print_multi', ['unexisting_model'])
assert len(actions) == 1, "Mismatching number of bound actions"
@ -95,7 +100,7 @@ class test_ir_values(common.TransactionCase):
assert len(actions) == 1, "Mismatching number of bound actions"
assert len(actions[0]) == 3, "Malformed action definition"
assert actions[0][1] == 'Related Stuff', 'Bound action does not match definition'
assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == 14, 'Bound action does not match definition'
assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == act_id_4, 'Bound action does not match definition'
if __name__ == '__main__':

View File

@ -2,6 +2,8 @@ import logging
import sys
import openerp
from openerp import tools
from openerp.modules import module
_logger = logging.getLogger(__name__)
@ -32,8 +34,25 @@ import server
def main():
args = sys.argv[1:]
# The only shared option is '--addons-path=' needed to discover additional
# commands from modules
if len(args) > 1 and args[0].startswith('--addons-path=') and not args[1].startswith("-"):
tools.config.parse_config([args[0]])
args = args[1:]
# Default legacy command
command = "server"
# Subcommand discovery
if len(args) and not args[0].startswith("-"):
for m in module.get_modules():
m = 'openerp.addons.' + m
__import__(m)
#try:
#except Exception, e:
# raise
# print e
command = args[0]
args = args[1:]

View File

@ -35,10 +35,6 @@ must be used.
import deprecation
# Maximum number of threads processing concurrently cron jobs.
max_cron_threads = 4 # Actually the default value here is meaningless,
# look at tools.config for the default value.
# Paths to search for OpenERP addons.
addons_paths = []

View File

@ -1,212 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2011 OpenERP SA (<http://www.openerp.com>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
""" Cron jobs scheduling
Cron jobs are defined in the ir_cron table/model. This module deals with all
cron jobs, for all databases of a single OpenERP server instance.
It defines a single master thread that will spawn (a bounded number of)
threads to process individual cron jobs.
The thread runs forever, checking every 60 seconds for new
'database wake-ups'. It maintains a heapq of database wake-ups. At each
wake-up, it will call ir_cron._run_jobs_multithread() for the given database. _run_jobs_multithread
will check the jobs defined in the ir_cron table and spawn accordingly threads
to process them.
This module's behavior depends on the following configuration variable:
openerp.conf.max_cron_threads.
"""
import heapq
import logging
import threading
import time
import openerp
import tools
_logger = logging.getLogger(__name__)
# Heapq of database wake-ups. Note that 'database wake-up' meaning is in
# the context of the cron management. This is not originally about loading
# a database, although having the database name in the queue will
# cause it to be loaded when the schedule time is reached, even if it was
# unloaded in the mean time. Normally a database's wake-up is cancelled by
# the RegistryManager when the database is unloaded - so this should not
# cause it to be reloaded.
#
# TODO: perhaps in the future we could consider a flag on ir.cron jobs
# that would cause database wake-up even if the database has not been
# loaded yet or was already unloaded (e.g. 'force_db_wakeup' or something)
#
# Each element is a triple (timestamp, database-name, boolean). The boolean
# specifies if the wake-up is canceled (so a wake-up can be canceled without
# relying on the heapq implementation detail; no need to remove the job from
# the heapq).
_wakeups = []
# Mapping of database names to the wake-up defined in the heapq,
# so that we can cancel the wake-up without messing with the heapq
# invariant: lookup the wake-up by database-name, then set
# its third element to True.
_wakeup_by_db = {}
# Re-entrant lock to protect the above _wakeups and _wakeup_by_db variables.
# We could use a simple (non-reentrant) lock if the runner function below
# was more fine-grained, but we are fine with the loop owning the lock
# while spawning a few threads.
_wakeups_lock = threading.RLock()
# Maximum number of threads allowed to process cron jobs concurrently. This
# variable is set by start_master_thread using openerp.conf.max_cron_threads.
_thread_slots = None
# A (non re-entrant) lock to protect the above _thread_slots variable.
_thread_slots_lock = threading.Lock()
# Sleep duration limits - must not loop too quickly, but can't sleep too long
# either, because a new job might be inserted in ir_cron with a much sooner
# execution date than current known ones. We won't see it until we wake!
MAX_SLEEP = 60 # 1 min
MIN_SLEEP = 1 # 1 sec
# Dummy wake-up timestamp that can be used to force a database wake-up asap
WAKE_UP_NOW = 1
def get_thread_slots():
""" Return the number of available thread slots """
return _thread_slots
def release_thread_slot():
""" Increment the number of available thread slots """
global _thread_slots
with _thread_slots_lock:
_thread_slots += 1
def take_thread_slot():
""" Decrement the number of available thread slots """
global _thread_slots
with _thread_slots_lock:
_thread_slots -= 1
def cancel(db_name):
""" Cancel the next wake-up of a given database, if any.
:param db_name: database name for which the wake-up is canceled.
"""
_logger.debug("Cancel next wake-up for database '%s'.", db_name)
with _wakeups_lock:
if db_name in _wakeup_by_db:
_wakeup_by_db[db_name][2] = True
def cancel_all():
""" Cancel all database wake-ups. """
_logger.debug("Cancel all database wake-ups")
global _wakeups
global _wakeup_by_db
with _wakeups_lock:
_wakeups = []
_wakeup_by_db = {}
def schedule_wakeup(timestamp, db_name):
""" Schedule a new wake-up for a database.
If an earlier wake-up is already defined, the new wake-up is discarded.
If another wake-up is defined, that wake-up is discarded and the new one
is scheduled.
:param db_name: database name for which a new wake-up is scheduled.
:param timestamp: when the wake-up is scheduled.
"""
if not timestamp:
return
with _wakeups_lock:
if db_name in _wakeup_by_db:
task = _wakeup_by_db[db_name]
if not task[2] and timestamp > task[0]:
# existing wakeup is valid and occurs earlier than new one
return
task[2] = True # cancel existing task
task = [timestamp, db_name, False]
heapq.heappush(_wakeups, task)
_wakeup_by_db[db_name] = task
_logger.debug("Wake-up scheduled for database '%s' @ %s", db_name,
'NOW' if timestamp == WAKE_UP_NOW else timestamp)
def runner():
"""Neverending function (intended to be run in a dedicated thread) that
checks every 60 seconds the next database wake-up. TODO: make configurable
"""
while True:
runner_body()
def runner_body():
with _wakeups_lock:
while _wakeups and _wakeups[0][0] < time.time() and get_thread_slots():
task = heapq.heappop(_wakeups)
timestamp, db_name, canceled = task
if canceled:
continue
del _wakeup_by_db[db_name]
registry = openerp.pooler.get_pool(db_name)
if not registry._init:
_logger.debug("Database '%s' wake-up! Firing multi-threaded cron job processing", db_name)
registry['ir.cron']._run_jobs_multithread()
amount = MAX_SLEEP
with _wakeups_lock:
# Sleep less than MAX_SLEEP if the next known wake-up will happen before that.
if _wakeups and get_thread_slots():
amount = min(MAX_SLEEP, max(MIN_SLEEP, _wakeups[0][0] - time.time()))
_logger.debug("Going to sleep for %ss", amount)
time.sleep(amount)
def start_master_thread():
""" Start the above runner function in a daemon thread.
The thread is a typical daemon thread: it will never quit and must be
terminated when the main process exits - with no consequence (the processing
threads it spawns are not marked daemon).
"""
global _thread_slots
_thread_slots = openerp.conf.max_cron_threads
db_maxconn = tools.config['db_maxconn']
if _thread_slots >= tools.config.get('db_maxconn', 64):
_logger.warning("Connection pool size (%s) is set lower than max number of cron threads (%s), "
"this may cause trouble if you reach that number of parallel cron tasks.",
db_maxconn, _thread_slots)
t = threading.Thread(target=runner, name="openerp.cron.master_thread")
t.setDaemon(True)
t.start()
_logger.debug("Master cron daemon started!")
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -433,8 +433,9 @@ def get_modules():
return name
def is_really_module(name):
name = opj(dir, name)
return os.path.isdir(name) or zipfile.is_zipfile(name)
manifest_name = opj(dir, name, '__openerp__.py')
zipfile_name = opj(dir, name)
return os.path.isfile(manifest_name) or zipfile.is_zipfile(zipfile_name)
return map(clean, filter(is_really_module, os.listdir(dir)))
plist = []

View File

@ -28,7 +28,6 @@ import threading
import openerp.sql_db
import openerp.osv.orm
import openerp.cron
import openerp.tools
import openerp.modules.db
import openerp.tools.config
@ -58,6 +57,21 @@ class Registry(object):
self.db_name = db_name
self.db = openerp.sql_db.db_connect(db_name)
# In monoprocess cron jobs flag (pooljobs)
self.cron = False
# Inter-process signaling (used only when openerp.multi_process is True):
# The `base_registry_signaling` sequence indicates the whole registry
# must be reloaded.
# The `base_cache_signaling sequence` indicates all caches must be
# invalidated (i.e. cleared).
self.base_registry_signaling_sequence = 1
self.base_cache_signaling_sequence = 1
# Flag indicating if at least one model cache has been cleared.
# Useful only in a multi-process context.
self._any_cache_cleared = False
cr = self.db.cursor()
has_unaccent = openerp.modules.db.has_unaccent(cr)
if openerp.tools.config['unaccent'] and not has_unaccent:
@ -112,7 +126,7 @@ class Registry(object):
monitor the ir.cron model for future jobs. See openerp.cron for
details.
"""
openerp.cron.schedule_wakeup(openerp.cron.WAKE_UP_NOW, self.db.dbname)
self.cron = True
def clear_caches(self):
""" Clear the caches
@ -121,6 +135,36 @@ class Registry(object):
"""
for model in self.models.itervalues():
model.clear_caches()
# Special case for ir_ui_menu which does not use openerp.tools.ormcache.
ir_ui_menu = self.models.get('ir.ui.menu')
if ir_ui_menu:
ir_ui_menu.clear_cache()
# Useful only in a multi-process context.
def reset_any_cache_cleared(self):
self._any_cache_cleared = False
# Useful only in a multi-process context.
def any_cache_cleared(self):
return self._any_cache_cleared
@classmethod
def setup_multi_process_signaling(cls, cr):
if not openerp.multi_process:
return
# Inter-process signaling:
# The `base_registry_signaling` sequence indicates the whole registry
# must be reloaded.
# The `base_cache_signaling sequence` indicates all caches must be
# invalidated (i.e. cleared).
cr.execute("""SELECT sequence_name FROM information_schema.sequences WHERE sequence_name='base_registry_signaling'""")
if not cr.fetchall():
cr.execute("""CREATE SEQUENCE base_registry_signaling INCREMENT BY 1 START WITH 1""")
cr.execute("""SELECT nextval('base_registry_signaling')""")
cr.execute("""CREATE SEQUENCE base_cache_signaling INCREMENT BY 1 START WITH 1""")
cr.execute("""SELECT nextval('base_cache_signaling')""")
@contextmanager
def cursor(self, auto_commit=True):
@ -182,6 +226,7 @@ class RegistryManager(object):
cr = registry.db.cursor()
try:
Registry.setup_multi_process_signaling(cr)
registry.do_parent_store(cr)
registry.get('ir.actions.report.xml').register_all(cr)
cr.commit()
@ -195,20 +240,11 @@ class RegistryManager(object):
@classmethod
def delete(cls, db_name):
"""Delete the registry linked to a given database.
This also cleans the associated caches. For good measure this also
cancels the associated cron job. But please note that the cron job can
be running and take some time before ending, and that you should not
remove a registry if it can still be used by some thread. So it might
be necessary to call yourself openerp.cron.Agent.cancel(db_name) and
and join (i.e. wait for) the thread.
"""
"""Delete the registry linked to a given database. """
with cls.registries_lock:
if db_name in cls.registries:
cls.registries[db_name].clear_caches()
del cls.registries[db_name]
openerp.cron.cancel(db_name)
@classmethod
def delete_all(cls):
@ -232,5 +268,71 @@ class RegistryManager(object):
if db_name in cls.registries:
cls.registries[db_name].clear_caches()
@classmethod
def check_registry_signaling(cls, db_name):
if openerp.multi_process and db_name in cls.registries:
registry = cls.get(db_name, pooljobs=False)
cr = registry.db.cursor()
try:
cr.execute("""
SELECT base_registry_signaling.last_value,
base_cache_signaling.last_value
FROM base_registry_signaling, base_cache_signaling""")
r, c = cr.fetchone()
# Check if the model registry must be reloaded (e.g. after the
# database has been updated by another process).
if registry.base_registry_signaling_sequence != r:
_logger.info("Reloading the model registry after database signaling.")
# Don't run the cron in the Gunicorn worker.
registry = cls.new(db_name, pooljobs=False)
registry.base_registry_signaling_sequence = r
# Check if the model caches must be invalidated (e.g. after a write
# occured on another process). Don't clear right after a registry
# has been reload.
elif registry.base_cache_signaling_sequence != c:
_logger.info("Invalidating all model caches after database signaling.")
registry.base_cache_signaling_sequence = c
registry.clear_caches()
registry.reset_any_cache_cleared()
# One possible reason caches have been invalidated is the
# use of decimal_precision.write(), in which case we need
# to refresh fields.float columns.
for model in registry.models.values():
for column in model._columns.values():
if hasattr(column, 'digits_change'):
column.digits_change(cr)
finally:
cr.close()
@classmethod
def signal_caches_change(cls, db_name):
if openerp.multi_process and db_name in cls.registries:
# Check the registries if any cache has been cleared and signal it
# through the database to other processes.
registry = cls.get(db_name, pooljobs=False)
if registry.any_cache_cleared():
_logger.info("At least one model cache has been cleared, signaling through the database.")
cr = registry.db.cursor()
r = 1
try:
cr.execute("select nextval('base_cache_signaling')")
r = cr.fetchone()[0]
finally:
cr.close()
registry.base_cache_signaling_sequence = r
registry.reset_any_cache_cleared()
@classmethod
def signal_registry_change(cls, db_name):
if openerp.multi_process and db_name in cls.registries:
registry = cls.get(db_name, pooljobs=False)
cr = registry.db.cursor()
r = 1
try:
cr.execute("select nextval('base_registry_signaling')")
r = cr.fetchone()[0]
finally:
cr.close()
registry.base_registry_signaling_sequence = r
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -40,23 +40,6 @@ import openerp
_logger = logging.getLogger(__name__)
def close_socket(sock):
""" Closes a socket instance cleanly
:param sock: the network socket to close
:type sock: socket.socket
"""
try:
sock.shutdown(socket.SHUT_RDWR)
except socket.error, e:
# On OSX, socket shutdowns both sides if any side closes it
# causing an error 57 'Socket is not connected' on shutdown
# of the other side (or something), see
# http://bugs.python.org/issue4397
# note: stdlib fixed test, not behavior
if e.errno != errno.ENOTCONN or platform.system() != 'Darwin':
raise
sock.close()
#.apidoc title: Common Services: netsvc
@ -67,9 +50,9 @@ def abort_response(dummy_1, description, dummy_2, details):
raise openerp.osv.osv.except_osv(description, details)
class Service(object):
""" Base class for *Local* services
Functionality here is trusted, no authentication.
""" Base class for Local services
Functionality here is trusted, no authentication.
Workflow engine and reports subclass this.
"""
_services = {}
def __init__(self, name):
@ -145,7 +128,6 @@ class ColoredFormatter(DBFormatter):
record.levelname = COLOR_PATTERN % (30 + fg_color, 40 + bg_color, record.levelname)
return DBFormatter.format(self, record)
def init_logger():
from tools.translate import resetlocale
resetlocale()
@ -246,85 +228,6 @@ def init_alternative_logger():
logger.addHandler(handler)
logger.setLevel(logging.ERROR)
class Server:
""" Generic interface for all servers with an event loop etc.
Override this to impement http, net-rpc etc. servers.
Servers here must have threaded behaviour. start() must not block,
there is no run().
"""
__is_started = False
__servers = []
__starter_threads = []
# we don't want blocking server calls (think select()) to
# wait forever and possibly prevent exiting the process,
# but instead we want a form of polling/busy_wait pattern, where
# _server_timeout should be used as the default timeout for
# all I/O blocking operations
_busywait_timeout = 0.5
def __init__(self):
Server.__servers.append(self)
if Server.__is_started:
# raise Exception('All instances of servers must be inited before the startAll()')
# Since the startAll() won't be called again, allow this server to
# init and then start it after 1sec (hopefully). Register that
# timer thread in a list, so that we can abort the start if quitAll
# is called in the meantime
t = threading.Timer(1.0, self._late_start)
t.name = 'Late start timer for %s' % str(self.__class__)
Server.__starter_threads.append(t)
t.start()
def start(self):
_logger.debug("called stub Server.start")
def _late_start(self):
self.start()
for thr in Server.__starter_threads:
if thr.finished.is_set():
Server.__starter_threads.remove(thr)
def stop(self):
_logger.debug("called stub Server.stop")
def stats(self):
""" This function should return statistics about the server """
return "%s: No statistics" % str(self.__class__)
@classmethod
def startAll(cls):
if cls.__is_started:
return
_logger.info("Starting %d services" % len(cls.__servers))
for srv in cls.__servers:
srv.start()
cls.__is_started = True
@classmethod
def quitAll(cls):
if not cls.__is_started:
return
_logger.info("Stopping %d services" % len(cls.__servers))
for thr in cls.__starter_threads:
if not thr.finished.is_set():
thr.cancel()
cls.__starter_threads.remove(thr)
for srv in cls.__servers:
srv.stop()
cls.__is_started = False
@classmethod
def allStats(cls):
res = ["Servers %s" % ('stopped', 'started')[cls.__is_started]]
res.extend(srv.stats() for srv in cls.__servers)
return '\n'.join(res)
def _close_socket(self):
close_socket(self.socket)
def replace_request_password(args):
# password is always 3rd argument in a request, we replace it in RPC logs
# so it's easier to forward logs for diagnostics/debugging purposes...

View File

@ -163,7 +163,7 @@ class boolean(_column):
_logger.debug(
"required=True is deprecated: making a boolean field"
" `required` has no effect, as NULL values are "
"automatically turned into False.")
"automatically turned into False. args: %r",args)
class integer(_column):
_type = 'integer'

View File

@ -916,7 +916,16 @@ class BaseModel(object):
else:
new.extend(cls.__dict__.get(s, []))
nattr[s] = new
# Keep links to non-inherited constraints, e.g. useful when exporting translations
nattr['_local_constraints'] = cls.__dict__.get('_constraints', [])
nattr['_local_sql_constraints'] = cls.__dict__.get('_sql_constraints', [])
cls = type(name, (cls, parent_class), dict(nattr, _register=False))
else:
cls._local_constraints = getattr(cls, '_constraints', [])
cls._local_sql_constraints = getattr(cls, '_sql_constraints', [])
if not getattr(cls, '_original_module', None):
cls._original_module = cls._module
obj = object.__new__(cls)
@ -1515,6 +1524,8 @@ class BaseModel(object):
error_msgs = []
for constraint in self._constraints:
fun, msg, fields = constraint
# We don't pass around the context here: validation code
# must always yield the same results.
if not fun(self, cr, uid, ids):
# Check presence of __call__ directly instead of using
# callable() because it will be deprecated as of Python 3.0
@ -2498,6 +2509,7 @@ class BaseModel(object):
try:
getattr(self, '_ormcache')
self._ormcache = {}
self.pool._any_cache_cleared = True
except AttributeError:
pass
@ -3223,7 +3235,7 @@ class BaseModel(object):
def _create_table(self, cr):
cr.execute('CREATE TABLE "%s" (id SERIAL NOT NULL, PRIMARY KEY(id)) WITHOUT OIDS' % (self._table,))
cr.execute('CREATE TABLE "%s" (id SERIAL NOT NULL, PRIMARY KEY(id))' % (self._table,))
cr.execute(("COMMENT ON TABLE \"%s\" IS %%s" % self._table), (self._description,))
_schema.debug("Table '%s': created", self._table)
@ -3307,7 +3319,7 @@ class BaseModel(object):
raise except_orm('Programming Error', ('Many2Many destination model does not exist: `%s`') % (f._obj,))
dest_model = self.pool.get(f._obj)
ref = dest_model._table
cr.execute('CREATE TABLE "%s" ("%s" INTEGER NOT NULL, "%s" INTEGER NOT NULL, UNIQUE("%s","%s")) WITH OIDS' % (m2m_tbl, col1, col2, col1, col2))
cr.execute('CREATE TABLE "%s" ("%s" INTEGER NOT NULL, "%s" INTEGER NOT NULL, UNIQUE("%s","%s"))' % (m2m_tbl, col1, col2, col1, col2))
# create foreign key references with ondelete=cascade, unless the targets are SQL views
cr.execute("SELECT relkind FROM pg_class WHERE relkind IN ('v') AND relname=%s", (ref,))
if not cr.fetchall():

View File

@ -23,6 +23,8 @@
from functools import wraps
import logging
import threading
from psycopg2 import IntegrityError, errorcodes
import orm
@ -168,6 +170,7 @@ class object_proxy(object):
@check
def execute(self, db, uid, obj, method, *args, **kw):
threading.currentThread().dbname = db
cr = pooler.get_db(db).cursor()
try:
try:

View File

@ -1,8 +1,9 @@
# -*- coding: utf-8 -*-
##############################################################################
#
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
# Copyright (C) 2010-2012 OpenERP SA (<http://www.openerp.com>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@ -15,7 +16,7 @@
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
@ -23,17 +24,16 @@ import logging
import threading
import time
import http_server
import cron
import netrpc_server
import web_services
import websrv_lib
import web_services
import wsgi_server
import openerp.cron
import openerp.modules
import openerp.netsvc
import openerp.osv
import openerp.tools
import openerp.service.wsgi_server
#.apidoc title: RPC Services
@ -61,6 +61,7 @@ Maybe you forgot to add those addons in your addons_path configuration."""
_logger.exception('Failed to load server-wide module `%s`.%s', m, msg)
start_internal_done = False
main_thread_id = threading.currentThread().ident
def start_internal():
global start_internal_done
@ -68,38 +69,32 @@ def start_internal():
return
openerp.netsvc.init_logger()
openerp.modules.loading.open_openerp_namespace()
# Instantiate local services (this is a legacy design).
openerp.osv.osv.start_object_proxy()
# Export (for RPC) services.
web_services.start_web_services()
web_services.start_service()
load_server_wide_modules()
start_internal_done = True
def start_services():
""" Start all services including http, netrpc and cron """
start_internal()
# Initialize the HTTP stack.
netrpc_server.init_servers()
# Start the main cron thread.
if openerp.conf.max_cron_threads:
openerp.cron.start_master_thread()
# Start the top-level servers threads (normally HTTP, HTTPS, and NETRPC).
openerp.netsvc.Server.startAll()
# Initialize the NETRPC server.
netrpc_server.start_service()
# Start the WSGI server.
openerp.service.wsgi_server.start_server()
wsgi_server.start_service()
# Start the main cron thread.
cron.start_service()
def stop_services():
""" Stop all services. """
# stop scheduling new jobs; we will have to wait for the jobs to complete below
openerp.cron.cancel_all()
# stop services
cron.stop_service()
netrpc_server.stop_service()
wsgi_server.stop_service()
openerp.netsvc.Server.quitAll()
openerp.service.wsgi_server.stop_server()
config = openerp.tools.config
_logger.info("Initiating shutdown")
_logger.info("Hit CTRL-C again or send a second signal to force the shutdown.")
logging.shutdown()
@ -107,8 +102,9 @@ def stop_services():
# Manually join() all threads before calling sys.exit() to allow a second signal
# to trigger _force_quit() in case some non-daemon threads won't exit cleanly.
# threading.Thread.join() should not mask signals (at least in python 2.5).
me = threading.currentThread()
for thread in threading.enumerate():
if thread != threading.currentThread() and not thread.isDaemon():
if thread != me and not thread.isDaemon() and thread.ident != main_thread_id:
while thread.isAlive():
# Need a busyloop here as thread.join() masks signals
# and would prevent the forced shutdown.
@ -119,10 +115,9 @@ def stop_services():
def start_services_workers():
import openerp.service.workers
openerp.multi_process = True # Nah!
openerp.multi_process = True
openerp.service.workers.Multicorn(openerp.service.wsgi_server.application).run()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

74
openerp/service/cron.py Normal file
View File

@ -0,0 +1,74 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2011 OpenERP SA (<http://www.openerp.com>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
""" Cron jobs scheduling
Cron jobs are defined in the ir_cron table/model. This module deals with all
cron jobs, for all databases of a single OpenERP server instance.
"""
import logging
import threading
import time
import openerp
_logger = logging.getLogger(__name__)
SLEEP_INTERVAL = 60 # 1 min
def cron_runner(number):
while True:
time.sleep(SLEEP_INTERVAL + number) # Steve Reich timing style
registries = openerp.modules.registry.RegistryManager.registries
_logger.debug('cron%d polling for jobs', number)
for db_name, registry in registries.items():
while True and registry.cron:
# acquired = openerp.addons.base.ir.ir_cron.ir_cron._acquire_job(db_name)
# TODO why isnt openerp.addons.base defined ?
import sys
base = sys.modules['addons.base']
acquired = base.ir.ir_cron.ir_cron._acquire_job(db_name)
if not acquired:
break
def start_service():
""" Start the above runner function in a daemon thread.
The thread is a typical daemon thread: it will never quit and must be
terminated when the main process exits - with no consequence (the processing
threads it spawns are not marked daemon).
"""
for i in range(openerp.tools.config['max_cron_threads']):
def target():
cron_runner(i)
t = threading.Thread(target=target, name="openerp.service.cron.cron%d" % i)
t.setDaemon(True)
t.start()
_logger.debug("cron%d started!" % i)
def stop_service():
pass
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -176,6 +176,4 @@ class OpenERPAuthProvider(AuthProvider):
self.auth_tries += 1
raise AuthRequiredExc(atype='Basic', realm=self.realm)
#eof
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -38,6 +38,103 @@ import openerp.tools as tools
_logger = logging.getLogger(__name__)
def close_socket(sock):
""" Closes a socket instance cleanly
:param sock: the network socket to close
:type sock: socket.socket
"""
try:
sock.shutdown(socket.SHUT_RDWR)
except socket.error, e:
# On OSX, socket shutdowns both sides if any side closes it
# causing an error 57 'Socket is not connected' on shutdown
# of the other side (or something), see
# http://bugs.python.org/issue4397
# note: stdlib fixed test, not behavior
if e.errno != errno.ENOTCONN or platform.system() not in ['Darwin', 'Windows']:
raise
sock.close()
class Server:
""" Generic interface for all servers with an event loop etc.
Override this to impement http, net-rpc etc. servers.
Servers here must have threaded behaviour. start() must not block,
there is no run().
"""
__is_started = False
__servers = []
__starter_threads = []
# we don't want blocking server calls (think select()) to
# wait forever and possibly prevent exiting the process,
# but instead we want a form of polling/busy_wait pattern, where
# _server_timeout should be used as the default timeout for
# all I/O blocking operations
_busywait_timeout = 0.5
def __init__(self):
Server.__servers.append(self)
if Server.__is_started:
# raise Exception('All instances of servers must be inited before the startAll()')
# Since the startAll() won't be called again, allow this server to
# init and then start it after 1sec (hopefully). Register that
# timer thread in a list, so that we can abort the start if quitAll
# is called in the meantime
t = threading.Timer(1.0, self._late_start)
t.name = 'Late start timer for %s' % str(self.__class__)
Server.__starter_threads.append(t)
t.start()
def start(self):
_logger.debug("called stub Server.start")
def _late_start(self):
self.start()
for thr in Server.__starter_threads:
if thr.finished.is_set():
Server.__starter_threads.remove(thr)
def stop(self):
_logger.debug("called stub Server.stop")
def stats(self):
""" This function should return statistics about the server """
return "%s: No statistics" % str(self.__class__)
@classmethod
def startAll(cls):
if cls.__is_started:
return
_logger.info("Starting %d services" % len(cls.__servers))
for srv in cls.__servers:
srv.start()
cls.__is_started = True
@classmethod
def quitAll(cls):
if not cls.__is_started:
return
_logger.info("Stopping %d services" % len(cls.__servers))
for thr in cls.__starter_threads:
if not thr.finished.is_set():
thr.cancel()
cls.__starter_threads.remove(thr)
for srv in cls.__servers:
srv.stop()
cls.__is_started = False
@classmethod
def allStats(cls):
res = ["Servers %s" % ('stopped', 'started')[cls.__is_started]]
res.extend(srv.stats() for srv in cls.__servers)
return '\n'.join(res)
def _close_socket(self):
close_socket(self.socket)
class TinySocketClientThread(threading.Thread):
def __init__(self, sock, threads):
spn = sock and sock.getpeername()
@ -99,10 +196,10 @@ def netrpc_handle_exception_legacy(e):
return 'AccessDenied ' + str(e)
return openerp.tools.exception_to_unicode(e)
class TinySocketServerThread(threading.Thread,netsvc.Server):
class TinySocketServerThread(threading.Thread,Server):
def __init__(self, interface, port, secure=False):
threading.Thread.__init__(self, name="NetRPCDaemon-%d"%port)
netsvc.Server.__init__(self)
Server.__init__(self)
self.__port = port
self.__interface = interface
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@ -157,11 +254,12 @@ class TinySocketServerThread(threading.Thread,netsvc.Server):
netrpcd = None
def init_servers():
def start_service():
global netrpcd
if tools.config.get('netrpc', False):
netrpcd = TinySocketServerThread(
tools.config.get('netrpc_interface', ''),
int(tools.config.get('netrpc_port', 8070)))
netrpcd = TinySocketServerThread(tools.config.get('netrpc_interface', ''), int(tools.config.get('netrpc_port', 8070)))
def stop_service():
Server.quitAll()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -165,6 +165,7 @@ class db(netsvc.ExportService):
def exp_duplicate_database(self, db_original_name, db_name):
_logger.info('Duplicate database `%s` to `%s`.', db_original_name, db_name)
sql_db.close_db(db_original_name)
db = sql_db.db_connect('postgres')
cr = db.cursor()
try:
@ -603,68 +604,13 @@ class objects_proxy(netsvc.ExportService):
raise NameError("Method not available %s" % method)
security.check(db,uid,passwd)
assert openerp.osv.osv.service, "The object_proxy class must be started with start_object_proxy."
openerp.modules.registry.RegistryManager.check_registry_signaling(db)
fn = getattr(openerp.osv.osv.service, method)
res = fn(db, uid, *params)
openerp.modules.registry.RegistryManager.signal_caches_change(db)
return res
#
# Wizard ID: 1
# - None = end of wizard
#
# Wizard Type: 'form'
# - form
# - print
#
# Wizard datas: {}
# TODO: change local request to OSE request/reply pattern
#
class wizard(netsvc.ExportService):
def __init__(self, name='wizard'):
netsvc.ExportService.__init__(self,name)
self.id = 0
self.wiz_datas = {}
self.wiz_name = {}
self.wiz_uid = {}
def dispatch(self, method, params):
(db, uid, passwd ) = params[0:3]
threading.current_thread().uid = uid
params = params[3:]
if method not in ['execute','create']:
raise KeyError("Method not supported %s" % method)
security.check(db,uid,passwd)
fn = getattr(self, 'exp_'+method)
res = fn(db, uid, *params)
return res
def _execute(self, db, uid, wiz_id, datas, action, context):
self.wiz_datas[wiz_id].update(datas)
wiz = netsvc.LocalService('wizard.'+self.wiz_name[wiz_id])
return wiz.execute(db, uid, self.wiz_datas[wiz_id], action, context)
def exp_create(self, db, uid, wiz_name, datas=None):
if not datas:
datas={}
#FIXME: this is not thread-safe
self.id += 1
self.wiz_datas[self.id] = {}
self.wiz_name[self.id] = wiz_name
self.wiz_uid[self.id] = uid
return self.id
def exp_execute(self, db, uid, wiz_id, datas, action='init', context=None):
if not context:
context={}
if wiz_id in self.wiz_uid:
if self.wiz_uid[wiz_id] == uid:
return self._execute(db, uid, wiz_id, datas, action, context)
else:
raise openerp.exceptions.AccessDenied()
else:
raise openerp.exceptions.Warning('Wizard not found.')
#
# TODO: set a maximum report number per user to avoid DOS attacks
#
@ -686,8 +632,10 @@ class report_spool(netsvc.ExportService):
if method not in ['report', 'report_get', 'render_report']:
raise KeyError("Method not supported %s" % method)
security.check(db,uid,passwd)
openerp.modules.registry.RegistryManager.check_registry_signaling(db)
fn = getattr(self, 'exp_' + method)
res = fn(db, uid, *params)
openerp.modules.registry.RegistryManager.signal_caches_change(db)
return res
def exp_render_report(self, db, uid, object, ids, datas=None, context=None):
@ -799,13 +747,11 @@ class report_spool(netsvc.ExportService):
raise Exception, 'ReportNotFound'
def start_web_services():
def start_service():
db()
common()
objects_proxy()
wizard()
report_spool()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -16,6 +16,10 @@ import sys
import time
import werkzeug.serving
try:
from setproctitle import setproctitle
except ImportError:
setproctitle = lambda x: None
import openerp
import openerp.tools.config as config
@ -134,7 +138,7 @@ class Multicorn(object):
def process_spawn(self):
while len(self.workers_http) < self.population:
self.worker_spawn(WorkerHTTP, self.workers_http)
while len(self.workers_cron) < 1: # config option ?
while len(self.workers_cron) < config['max_cron_threads']:
self.worker_spawn(WorkerCron, self.workers_cron)
def sleep(self):
@ -189,8 +193,7 @@ class Multicorn(object):
for pid in self.workers.keys():
self.worker_kill(pid, signal.SIGTERM)
self.socket.close()
import __main__
__main__.quit_signals_received = 1
openerp.cli.server.quit_signals_received = 1
def run(self):
self.start()
@ -252,7 +255,7 @@ class Worker(object):
# Reset the worker if it consumes too much memory (e.g. caused by a memory leak).
rss, vms = psutil.Process(os.getpid()).get_memory_info()
if vms > config['limit_memory_soft']:
_logger.info('Virtual memory consumption too high, rebooting the worker.')
_logger.info('Worker (%d) virtual memory limit (%s) reached.', self.pid, vms)
self.alive = False # Commit suicide after the request.
# VMS and RLIMIT_AS are the same thing: virtual memory, a.k.a. address space
@ -263,7 +266,8 @@ class Worker(object):
r = resource.getrusage(resource.RUSAGE_SELF)
cpu_time = r.ru_utime + r.ru_stime
def time_expired(n, stack):
_logger.info('CPU time limit exceeded.')
_logger.info('Worker (%d) CPU time limit (%s) reached.', config['limit_time_cpu'])
# We dont suicide in such case
raise Exception('CPU time limit exceeded.')
signal.signal(signal.SIGXCPU, time_expired)
soft, hard = resource.getrlimit(resource.RLIMIT_CPU)
@ -274,6 +278,7 @@ class Worker(object):
def start(self):
self.pid = os.getpid()
setproctitle('openerp: %s %s' % (self.__class__.__name__, self.pid))
_logger.info("Worker %s (%s) alive", self.__class__.__name__, self.pid)
# Reseed the random number generator
random.seed()
@ -346,19 +351,27 @@ class WorkerBaseWSGIServer(werkzeug.serving.BaseWSGIServer):
class WorkerCron(Worker):
""" Cron workers """
def sleep(self):
time.sleep(60)
interval = 60 + self.pid % 10 # chorus effect
time.sleep(interval)
def process_work(self):
_logger.debug("WorkerCron (%s) polling for jobs", self.pid)
if config['db_name']:
db_names = config['db_name'].split(',')
else:
db_names = openerp.netsvc.ExportService._services['db'].exp_list(True)
for db_name in db_names:
while True:
# TODO Each job should be considered as one request in multiprocessing
acquired = openerp.addons.base.ir.ir_cron.ir_cron._acquire_job(db_name)
# acquired = openerp.addons.base.ir.ir_cron.ir_cron._acquire_job(db_name)
# TODO why isnt openerp.addons.base defined ?
import base
acquired = base.ir.ir_cron.ir_cron._acquire_job(db_name)
if not acquired:
break
# dont keep cursors in multi database mode
if len(db_names) > 1:
openerp.sql_db.close_db(db_name)
# TODO Each job should be considered as one request instead of each db
self.request_count += 1
def start(self):

View File

@ -428,14 +428,14 @@ def serve():
_logger.info('HTTP service (werkzeug) running on %s:%s', interface, port)
httpd.serve_forever()
def start_server():
def start_service():
""" Call serve() in its own thread.
The WSGI server can be shutdown with stop_server() below.
"""
threading.Thread(target=serve).start()
def stop_server():
def stop_service():
""" Initiate the shutdown of the WSGI server.
The server is supposed to have been started by start_server() above.

View File

@ -57,10 +57,12 @@ class ormcache(object):
try:
key = args[self.skiparg-2:]
del d[key]
self2.pool._any_cache_cleared = True
except KeyError:
pass
else:
d.clear()
self2.pool._any_cache_cleared = True
class ormcache_multi(ormcache):
def __init__(self, skiparg=2, size=8192, multi=3):

View File

@ -146,8 +146,9 @@ class configmanager(object):
help="specify the TCP IP address for the NETRPC protocol")
group.add_option("--netrpc-port", dest="netrpc_port", my_default=8070,
help="specify the TCP port for the NETRPC protocol", type="int")
group.add_option("--no-netrpc", dest="netrpc", action="store_false", my_default=True,
help="disable the NETRPC protocol")
# Needed a few day for runbot and saas
group.add_option("--no-netrpc", dest="netrpc", action="store_false", my_default=False, help="disable the NETRPC protocol")
group.add_option("--netrpc", dest="netrpc", action="store_true", my_default=False, help="enable the NETRPC protocol")
parser.add_option_group(group)
# WEB
@ -269,8 +270,8 @@ class configmanager(object):
"osv_memory tables. This is a decimal value expressed in hours, "
"and the default is 1 hour.",
type="float")
group.add_option("--max-cron-threads", dest="max_cron_threads", my_default=4,
help="Maximum number of threads processing concurrently cron jobs.",
group.add_option("--max-cron-threads", dest="max_cron_threads", my_default=2,
help="Maximum number of threads processing concurrently cron jobs (default 2).",
type="int")
group.add_option("--unaccent", dest="unaccent", my_default=False, action="store_true",
help="Use the unaccent function provided by the database when available.")
@ -282,19 +283,19 @@ class configmanager(object):
help="Specify the number of workers, 0 disable prefork mode.",
type="int")
group.add_option("--limit-memory-soft", dest="limit_memory_soft", my_default=640 * 1024 * 1024,
help="Maximum allowed virtual memory per worker, when reached the worker be reset after the current request.",
help="Maximum allowed virtual memory per worker, when reached the worker be reset after the current request (default 640M).",
type="int")
group.add_option("--limit-memory-hard", dest="limit_memory_hard", my_default=768 * 1024 * 1024,
help="Maximum allowed virtual memory per worker, when reached, any memory allocation will fail.",
help="Maximum allowed virtual memory per worker, when reached, any memory allocation will fail (default 768M).",
type="int")
group.add_option("--limit-time-cpu", dest="limit_time_cpu", my_default=60,
help="Maximum allowed CPU time per request.",
help="Maximum allowed CPU time per request (default 60).",
type="int")
group.add_option("--limit-time-real", dest="limit_time_real", my_default=60,
help="Maximum allowed Real time per request. ",
group.add_option("--limit-time-real", dest="limit_time_real", my_default=120,
help="Maximum allowed Real time per request (default 120).",
type="int")
group.add_option("--limit-request", dest="limit_request", my_default=8192,
help="Maximum number of request to be processed per worker.",
help="Maximum number of request to be processed per worker (default 8192).",
type="int")
parser.add_option_group(group)
@ -479,8 +480,6 @@ class configmanager(object):
if opt.save:
self.save()
openerp.conf.max_cron_threads = self.options['max_cron_threads']
openerp.conf.addons_paths = self.options['addons_path'].split(',')
if opt.server_wide_modules:
openerp.conf.server_wide_modules = map(lambda m: m.strip(), opt.server_wide_modules.split(','))

View File

@ -68,7 +68,7 @@ def image_resize_image(base64_source, size=(1024, 1024), encoding='base64', file
return base64_source
# create a thumbnail: will resize and keep ratios, then sharpen for better looking result
image.thumbnail(size, Image.ANTIALIAS)
sharpener = ImageEnhance.Sharpness(image.convert('RGB'))
sharpener = ImageEnhance.Sharpness(image.convert('RGBA'))
image = sharpener.enhance(2.0)
# create a transparent image for background
background = Image.new('RGBA', size, (255, 255, 255, 0))

View File

@ -27,6 +27,7 @@ Miscellaneous tools used by OpenERP.
"""
from functools import wraps
import cProfile
import subprocess
import logging
import os
@ -592,16 +593,10 @@ class profile(object):
def __call__(self, f):
@wraps(f)
def wrapper(*args, **kwargs):
class profile_wrapper(object):
def __init__(self):
self.result = None
def __call__(self):
self.result = f(*args, **kwargs)
pw = profile_wrapper()
import cProfile
fname = self.fname or ("%s.cprof" % (f.func_name,))
cProfile.runctx('pw()', globals(), locals(), filename=fname)
return pw.result
profile = cProfile.Profile()
result = profile.runcall(f, *args, **kwargs)
profile.dump_stats(self.fname or ("%s.cprof" % (f.func_name,)))
return result
return wrapper

View File

@ -1,269 +0,0 @@
# -*- coding: utf-8 -*-
#Copyright (c) 2004-2005, CherryPy Team (team@cherrypy.org)
#All rights reserved.
#
#Redistribution and use in source and binary forms, with or without
#modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of the CherryPy Team nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
#ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
#WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
#DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
#FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
#DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
#SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
#CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
#OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
#OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# This is a backport of Python-2.4's threading.local() implementation
"""Thread-local objects
(Note that this module provides a Python version of thread
threading.local class. Depending on the version of Python you're
using, there may be a faster one available. You should always import
the local class from threading.)
Thread-local objects support the management of thread-local data.
If you have data that you want to be local to a thread, simply create
a thread-local object and use its attributes:
>>> mydata = local()
>>> mydata.number = 42
>>> mydata.number
42
You can also access the local-object's dictionary:
>>> mydata.__dict__
{'number': 42}
>>> mydata.__dict__.setdefault('widgets', [])
[]
>>> mydata.widgets
[]
What's important about thread-local objects is that their data are
local to a thread. If we access the data in a different thread:
>>> log = []
>>> def f():
... items = mydata.__dict__.items()
... items.sort()
... log.append(items)
... mydata.number = 11
... log.append(mydata.number)
>>> import threading
>>> thread = threading.Thread(target=f)
>>> thread.start()
>>> thread.join()
>>> log
[[], 11]
we get different data. Furthermore, changes made in the other thread
don't affect data seen in this thread:
>>> mydata.number
42
Of course, values you get from a local object, including a __dict__
attribute, are for whatever thread was current at the time the
attribute was read. For that reason, you generally don't want to save
these values across threads, as they apply only to the thread they
came from.
You can create custom local objects by subclassing the local class:
>>> class MyLocal(local):
... number = 2
... initialized = False
... def __init__(self, **kw):
... if self.initialized:
... raise SystemError('__init__ called too many times')
... self.initialized = True
... self.__dict__.update(kw)
... def squared(self):
... return self.number ** 2
This can be useful to support default values, methods and
initialization. Note that if you define an __init__ method, it will be
called each time the local object is used in a separate thread. This
is necessary to initialize each thread's dictionary.
Now if we create a local object:
>>> mydata = MyLocal(color='red')
Now we have a default number:
>>> mydata.number
2
an initial color:
>>> mydata.color
'red'
>>> del mydata.color
And a method that operates on the data:
>>> mydata.squared()
4
As before, we can access the data in a separate thread:
>>> log = []
>>> thread = threading.Thread(target=f)
>>> thread.start()
>>> thread.join()
>>> log
[[('color', 'red'), ('initialized', True)], 11]
without affecting this thread's data:
>>> mydata.number
2
>>> mydata.color
Traceback (most recent call last):
...
AttributeError: 'MyLocal' object has no attribute 'color'
Note that subclasses can define slots, but they are not thread
local. They are shared across threads:
>>> class MyLocal(local):
... __slots__ = 'number'
>>> mydata = MyLocal()
>>> mydata.number = 42
>>> mydata.color = 'red'
So, the separate thread:
>>> thread = threading.Thread(target=f)
>>> thread.start()
>>> thread.join()
affects what we see:
>>> mydata.number
11
>>> del mydata
"""
# Threading import is at end
class _localbase(object):
__slots__ = '_local__key', '_local__args', '_local__lock'
def __new__(cls, *args, **kw):
self = object.__new__(cls)
key = '_local__key', 'thread.local.' + str(id(self))
object.__setattr__(self, '_local__key', key)
object.__setattr__(self, '_local__args', (args, kw))
object.__setattr__(self, '_local__lock', RLock())
if args or kw and (cls.__init__ is object.__init__):
raise TypeError("Initialization arguments are not supported")
# We need to create the thread dict in anticipation of
# __init__ being called, to make sure we don't call it
# again ourselves.
dict = object.__getattribute__(self, '__dict__')
currentThread().__dict__[key] = dict
return self
def _patch(self):
key = object.__getattribute__(self, '_local__key')
d = currentThread().__dict__.get(key)
if d is None:
d = {}
currentThread().__dict__[key] = d
object.__setattr__(self, '__dict__', d)
# we have a new instance dict, so call out __init__ if we have
# one
cls = type(self)
if cls.__init__ is not object.__init__:
args, kw = object.__getattribute__(self, '_local__args')
cls.__init__(self, *args, **kw)
else:
object.__setattr__(self, '__dict__', d)
class local(_localbase):
def __getattribute__(self, name):
lock = object.__getattribute__(self, '_local__lock')
lock.acquire()
try:
_patch(self)
return object.__getattribute__(self, name)
finally:
lock.release()
def __setattr__(self, name, value):
lock = object.__getattribute__(self, '_local__lock')
lock.acquire()
try:
_patch(self)
return object.__setattr__(self, name, value)
finally:
lock.release()
def __delattr__(self, name):
lock = object.__getattribute__(self, '_local__lock')
lock.acquire()
try:
_patch(self)
return object.__delattr__(self, name)
finally:
lock.release()
def __del__():
threading_enumerate = enumerate
__getattribute__ = object.__getattribute__
def __del__(self):
key = __getattribute__(self, '_local__key')
try:
threads = list(threading_enumerate())
except:
# if enumerate fails, as it seems to do during
# shutdown, we'll skip cleanup under the assumption
# that there is nothing to clean up
return
for thread in threads:
try:
__dict__ = thread.__dict__
except AttributeError:
# Thread is dying, rest in peace
continue
if key in __dict__:
try:
del __dict__[key]
except KeyError:
pass # didn't have anything in this thread
return __del__
__del__ = __del__()
from threading import currentThread, enumerate, RLock
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -793,26 +793,33 @@ def trans_generate(lang, modules, cr):
cr.execute(query_models, query_param)
def push_constraint_msg(module, term_type, model, msg):
# Check presence of __call__ directly instead of using
# callable() because it will be deprecated as of Python 3.0
if not hasattr(msg, '__call__'):
push_translation(module, term_type, model, 0, encode(msg))
push_translation(encode(module), term_type, encode(model), 0, encode(msg))
def push_local_constraints(module, model, cons_type='sql_constraints'):
"""Climb up the class hierarchy and ignore inherited constraints
from other modules"""
term_type = 'sql_constraint' if cons_type == 'sql_constraints' else 'constraint'
msg_pos = 2 if cons_type == 'sql_constraints' else 1
for cls in model.__class__.__mro__:
if getattr(cls, '_module', None) != module:
continue
constraints = getattr(cls, '_local_' + cons_type, [])
for constraint in constraints:
push_constraint_msg(module, term_type, model._name, constraint[msg_pos])
for (_, model, module) in cr.fetchall():
module = encode(module)
model = encode(model)
model_obj = pool.get(model)
if not model_obj:
_logger.error("Unable to find object %r", model)
continue
for constraint in getattr(model_obj, '_constraints', []):
push_constraint_msg(module, 'constraint', model, constraint[1])
if model_obj._constraints:
push_local_constraints(module, model_obj, 'constraints')
for constraint in getattr(model_obj, '_sql_constraints', []):
push_constraint_msg(module, 'sql_constraint', model, constraint[2])
if model_obj._sql_constraints:
push_local_constraints(module, model_obj, 'sql_constraints')
def get_module_from_path(path, mod_paths=None):
if not mod_paths:

View File

@ -1,191 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import copy
import logging
import openerp.netsvc as netsvc
from openerp.tools.misc import UpdateableStr, UpdateableDict
from openerp.tools.translate import translate
from lxml import etree
import openerp.pooler as pooler
from openerp.osv.osv import except_osv
from openerp.osv.orm import except_orm
_logger = logging.getLogger(__name__)
class except_wizard(Exception):
def __init__(self, name, value):
self.name = name
self.value = value
self.args = (name, value)
class interface(netsvc.Service):
"""
This is the base class used to implement Wizards. This class is deprecated
and `openerp.osv.TransientModel` must be used instead.
"""
states = {}
def __init__(self, name):
assert not self.exists('wizard.'+name), 'The wizard "%s" already exists!' % (name,)
_logger.warning(
"The wizard %s uses the deprecated openerp.wizard.interface class.\n"
"It must use the openerp.osv.TransientModel class instead." % \
name)
super(interface, self).__init__('wizard.'+name)
self.wiz_name = name
def translate_view(self, cr, node, state, lang):
if node.get('string'):
trans = translate(cr, self.wiz_name+','+state, 'wizard_view', lang, node.get('string').encode('utf8'))
if trans:
node.set('string', trans)
for n in node:
self.translate_view(cr, n, state, lang)
def execute_cr(self, cr, uid, data, state='init', context=None):
if not context:
context={}
res = {}
try:
state_def = self.states[state]
result_def = state_def.get('result', {})
actions_res = {}
# iterate through the list of actions defined for this state
for action in state_def.get('actions', []):
# execute them
action_res = action(self, cr, uid, data, context)
assert isinstance(action_res, dict), 'The return value of wizard actions should be a dictionary'
actions_res.update(action_res)
res = copy.copy(result_def)
res['datas'] = actions_res
lang = context.get('lang', False)
if result_def['type'] == 'action':
res['action'] = result_def['action'](self, cr, uid, data, context)
elif result_def['type'] == 'form':
fields = copy.deepcopy(result_def['fields'])
arch = copy.copy(result_def['arch'])
button_list = copy.copy(result_def['state'])
if isinstance(fields, UpdateableDict):
fields = fields.dict
if isinstance(arch, UpdateableStr):
arch = arch.string
# fetch user-set defaut values for the field... shouldn't we pass it the uid?
ir_values_obj = pooler.get_pool(cr.dbname).get('ir.values')
defaults = ir_values_obj.get(cr, uid, 'default', False, [('wizard.'+self.wiz_name, False)])
default_values = dict([(x[1], x[2]) for x in defaults])
for val in fields.keys():
if 'default' in fields[val]:
# execute default method for this field
if callable(fields[val]['default']):
fields[val]['value'] = fields[val]['default'](uid, data, state)
else:
fields[val]['value'] = fields[val]['default']
del fields[val]['default']
else:
# if user has set a default value for the field, use it
if val in default_values:
fields[val]['value'] = default_values[val]
if 'selection' in fields[val]:
if not isinstance(fields[val]['selection'], (tuple, list)):
fields[val] = copy.copy(fields[val])
fields[val]['selection'] = fields[val]['selection'](self, cr, uid, context)
elif lang:
res_name = "%s,%s,%s" % (self.wiz_name, state, val)
trans = lambda x: translate(cr, res_name, 'selection', lang, x) or x
for idx, (key, val2) in enumerate(fields[val]['selection']):
fields[val]['selection'][idx] = (key, trans(val2))
if lang:
# translate fields
for field in fields:
res_name = "%s,%s,%s" % (self.wiz_name, state, field)
trans = translate(cr, res_name, 'wizard_field', lang)
if trans:
fields[field]['string'] = trans
if 'help' in fields[field]:
t = translate(cr, res_name, 'help', lang, fields[field]['help'])
if t:
fields[field]['help'] = t
# translate arch
if not isinstance(arch, UpdateableStr):
doc = etree.XML(arch)
self.translate_view(cr, doc, state, lang)
arch = etree.tostring(doc)
# translate buttons
button_list = list(button_list)
for i, aa in enumerate(button_list):
button_name = aa[0]
trans = translate(cr, self.wiz_name+','+state+','+button_name, 'wizard_button', lang)
if trans:
aa = list(aa)
aa[1] = trans
button_list[i] = aa
res['fields'] = fields
res['arch'] = arch
res['state'] = button_list
elif result_def['type'] == 'choice':
next_state = result_def['next_state'](self, cr, uid, data, context)
return self.execute_cr(cr, uid, data, next_state, context)
except Exception, e:
if isinstance(e, except_wizard) \
or isinstance(e, except_osv) \
or isinstance(e, except_orm):
netsvc.abort_response(2, e.name, 'warning', e.value)
else:
_logger.exception('Exception in call:')
raise
return res
def execute(self, db, uid, data, state='init', context=None):
if not context:
context={}
cr = pooler.get_db(db).cursor()
try:
try:
res = self.execute_cr(cr, uid, data, state, context)
cr.commit()
except Exception:
cr.rollback()
raise
finally:
cr.close()
return res
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -21,31 +21,29 @@
##############################################################################
import glob, os, re, setuptools, sys
from os.path import join, isfile
from os.path import join
# List all data files
def data():
files = []
r = {}
for root, dirnames, filenames in os.walk('openerp'):
for filename in filenames:
if not re.match(r'.*(\.pyc|\.pyo|\~)$',filename):
files.append(os.path.join(root, filename))
d = {}
for v in files:
k=os.path.dirname(v)
if k in d:
d[k].append(v)
else:
d[k]=[v]
r = d.items()
if not re.match(r'.*(\.pyc|\.pyo|\~)$', filename):
r.setdefault(root, []).append(os.path.join(root, filename))
if os.name == 'nt':
r.append(("Microsoft.VC90.CRT", glob.glob('C:\Microsoft.VC90.CRT\*.*')))
r["Microsoft.VC90.CRT"] = glob.glob('C:\Microsoft.VC90.CRT\*.*')
import babel
r.append(("localedata",
glob.glob(os.path.join(os.path.dirname(babel.__file__), "localedata" , '*'))))
import babel
r["localedata"] = glob.glob(os.path.join(os.path.dirname(babel.__file__), "localedata", '*'))
return r
import pytz
tzdir = os.path.dirname(pytz.__file__)
for root, _, filenames in os.walk(os.path.join(tzdir, "zoneinfo")):
base = os.path.join('pytz', root[len(tzdir) + 1:])
r[base] = [os.path.join(root, f) for f in filenames]
return r.items()
def gen_manifest():
file_list="\n".join(data())
@ -64,7 +62,7 @@ def py2exe_options():
"skip_archive": 1,
"optimize": 2,
"dist_dir": 'dist',
"packages": [ "DAV", "HTMLParser", "PIL", "asynchat", "asyncore", "commands", "dateutil", "decimal", "docutils", "email", "encodings", "imaplib", "lxml", "lxml._elementpath", "lxml.builder", "lxml.etree", "lxml.objectify", "mako", "openerp", "poplib", "pychart", "pydot", "pyparsing", "reportlab", "select", "simplejson", "smtplib", "uuid", "vatnumber", "vobject", "xml", "xml.dom", "yaml", ],
"packages": [ "DAV", "HTMLParser", "PIL", "asynchat", "asyncore", "commands", "dateutil", "decimal", "docutils", "email", "encodings", "imaplib", "lxml", "lxml._elementpath", "lxml.builder", "lxml.etree", "lxml.objectify", "mako", "openerp", "poplib", "pychart", "pydot", "pyparsing", "pytz", "reportlab", "select", "simplejson", "smtplib", "uuid", "vatnumber", "vobject", "xml", "xml.dom", "yaml", ],
"excludes" : ["Tkconstants","Tkinter","tcl"],
}
}
@ -110,6 +108,7 @@ setuptools.setup(
'gdata',
'lxml < 3', # windows binary http://www.lfd.uci.edu/~gohlke/pythonlibs/
'mako',
'mock',
'PIL', # windows binary http://www.lfd.uci.edu/~gohlke/pythonlibs/
'psutil', # windows binary code.google.com/p/psutil/downloads/list
'psycopg2',
@ -122,6 +121,7 @@ setuptools.setup(
'pyyaml',
'reportlab', # windows binary pypi.python.org/pypi/reportlab
'simplejson',
'unittest2',
'vatnumber',
'vobject',
'werkzeug',