[MERGE] merged with lp:~openerp/openobject-server/trunk
bzr revid: hmo@hmo-20100524074613-apd7uhbsogt38imy
This commit is contained in:
+2
-1
@@ -9,7 +9,8 @@ include bin/server.pkey
|
||||
include bin/gpl.txt
|
||||
include man/openerp-server.1
|
||||
include man/openerp_serverrc.5
|
||||
recursive-include pixmaps
|
||||
recursive-include pixmaps *
|
||||
recursive-include win32 *
|
||||
recursive-include doc *
|
||||
recursive-include bin *xml *xsl *sql *rml *sxw *csv *rng
|
||||
graft bin/addons/
|
||||
|
||||
@@ -644,16 +644,13 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs):
|
||||
try:
|
||||
_load_data(cr, module_name, id_map, mode, 'test')
|
||||
except Exception, e:
|
||||
if tools.config.options['test-continue']:
|
||||
logger.notifyChannel('ERROR', netsvc.LOG_TEST, e)
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
logger.notifyChannel('ERROR', netsvc.LOG_TEST, e)
|
||||
pass
|
||||
finally:
|
||||
if tools.config.options['test-rollback']:
|
||||
cr.rollback()
|
||||
else:
|
||||
if tools.config.options['test-commit']:
|
||||
cr.commit()
|
||||
else:
|
||||
cr.rollback()
|
||||
|
||||
def _load_data(cr, module_name, id_map, mode, kind):
|
||||
noupdate = (kind == 'demo')
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
'module/module_report.xml',
|
||||
'res/res_request_view.xml',
|
||||
'res/res_lang_view.xml',
|
||||
'res/res_log_view.xml',
|
||||
'res/res_company_view.xml',
|
||||
'res/partner/partner_report.xml',
|
||||
'res/partner/partner_view.xml',
|
||||
@@ -87,6 +87,7 @@
|
||||
<label colspan="4" string="Please note that you will have to logout and relog if you change your password."/>
|
||||
<field name="context_lang" completion="1" readonly="0"/>
|
||||
<field name="context_tz" completion="1" readonly="0" colspan="4"/>
|
||||
<field name="view" colspan="4" readonly="0" />
|
||||
<newline/>
|
||||
<field colspan="4" name="signature" readonly="0"/>
|
||||
</page>
|
||||
@@ -108,16 +109,18 @@
|
||||
<notebook colspan="4">
|
||||
<page string="User">
|
||||
<field name="address_id" select="1"/>
|
||||
<field name="user_email" widget="email"/>
|
||||
<field name="company_id" required="1"/>
|
||||
<field name="action_id" required="True"/>
|
||||
<field domain="[('usage','=','menu')]" name="menu_id" required="True"/>
|
||||
<field name="context_lang"/>
|
||||
<field name="context_tz"/>
|
||||
<field name="view" readonly="0" />
|
||||
<group colspan="2" col="2">
|
||||
<separator string="Signature" colspan="2"/>
|
||||
<field colspan="2" name="signature" nolabel="1"/>
|
||||
</group>
|
||||
<group colspan="2" col="2">
|
||||
<group colspan="2" col="2" expand="1">
|
||||
<separator string="Access Rights (groups)" colspan="2"/>
|
||||
<field colspan="2" nolabel="1" name="groups_id"/>
|
||||
</group>
|
||||
@@ -284,14 +287,14 @@
|
||||
</form>
|
||||
<xpath expr="//label[@string='description']"
|
||||
position="attributes">
|
||||
<attribute name="string">Choose between the simplified interface and the extended one. If you are testing OpenERP or using it for the first time, We suggest you use the simplified interface. It has less options and fields but is easier to understand. You will be able to switch to the extended interface later.</attribute>
|
||||
<attribute name="string">Choose between the simplified interface and the extended one. We suggest you use the simplified interface. It has less options and fields but is easier to understand. You will be able to switch to the extended interface later.</attribute>
|
||||
</xpath>
|
||||
<xpath expr='//separator[@string="title"]' position='attributes'>
|
||||
<attribute name='string'>Choose Your Interface</attribute>
|
||||
</xpath>
|
||||
<xpath expr='//separator[@string="vsep"]' position='attributes'>
|
||||
<attribute name='string'></attribute>
|
||||
<attribute name='rowspan'>14</attribute>
|
||||
<attribute name='rowspan'>12</attribute>
|
||||
</xpath>
|
||||
<group string="res_config_contents" position="replace">
|
||||
<group colspan="4">
|
||||
|
||||
@@ -1,52 +1,52 @@
|
||||
"country_id:id","name","code"
|
||||
us,"Alabama","AL"
|
||||
us,"Alaska","AK"
|
||||
us,"Arizona","AZ"
|
||||
us,"Arkansas","AR"
|
||||
us,"California","CA"
|
||||
us,"Colorado","CO"
|
||||
us,"Connecticut","CT"
|
||||
us,"Delaware","DE"
|
||||
us,"District of Columbia","DC"
|
||||
us,"Florida","FL"
|
||||
us,"Georgia","GA"
|
||||
us,"Hawaii","HI"
|
||||
us,"Idaho","ID"
|
||||
us,"Illinois","IL"
|
||||
us,"Indiana","IN"
|
||||
us,"Iowa","IA"
|
||||
us,"Kansas","KS"
|
||||
us,"Kentucky","KY"
|
||||
us,"Louisiana","LA"
|
||||
us,"Maine","ME"
|
||||
us,"Montana","MT"
|
||||
us,"Nebraska","NE"
|
||||
us,"Nevada","NV"
|
||||
us,"New Hampshire","NH"
|
||||
us,"New Jersey","NJ"
|
||||
us,"New Mexico","NM"
|
||||
us,"New York","NY"
|
||||
us,"North Carolina","NC"
|
||||
us,"North Dakota","ND"
|
||||
us,"Ohio","OH"
|
||||
us,"Oklahoma","OK"
|
||||
us,"Oregon","OR"
|
||||
us,"Maryland","MD"
|
||||
us,"Massachusetts","MA"
|
||||
us,"Michigan","MI"
|
||||
us,"Minnesota","MN"
|
||||
us,"Mississippi","MS"
|
||||
us,"Missouri","MO"
|
||||
us,"Pennsylvania","PA"
|
||||
us,"Rhode Island","RI"
|
||||
us,"South Carolina","SC"
|
||||
us,"South Dakota","SD"
|
||||
us,"Tennessee","TN"
|
||||
us,"Texas","TX"
|
||||
us,"Utah","UT"
|
||||
us,"Vermont","VT"
|
||||
us,"Virginia","VA"
|
||||
us,"Washington","WA"
|
||||
us,"West Virginia","WV"
|
||||
us,"Wisconsin","WI"
|
||||
us,"Wyoming","WY"
|
||||
"id","country_id:id","name","code"
|
||||
state_us_1,us,"Alabama","AL"
|
||||
state_us_2,us,"Alaska","AK"
|
||||
state_us_3,us,"Arizona","AZ"
|
||||
state_us_4,us,"Arkansas","AR"
|
||||
state_us_5,us,"California","CA"
|
||||
state_us_6,us,"Colorado","CO"
|
||||
state_us_7,us,"Connecticut","CT"
|
||||
state_us_8,us,"Delaware","DE"
|
||||
state_us_9,us,"District of Columbia","DC"
|
||||
state_us_10,us,"Florida","FL"
|
||||
state_us_11,us,"Georgia","GA"
|
||||
state_us_12,us,"Hawaii","HI"
|
||||
state_us_13,us,"Idaho","ID"
|
||||
state_us_14,us,"Illinois","IL"
|
||||
state_us_15,us,"Indiana","IN"
|
||||
state_us_16,us,"Iowa","IA"
|
||||
state_us_17,us,"Kansas","KS"
|
||||
state_us_18,us,"Kentucky","KY"
|
||||
state_us_19,us,"Louisiana","LA"
|
||||
state_us_20,us,"Maine","ME"
|
||||
state_us_21,us,"Montana","MT"
|
||||
state_us_22,us,"Nebraska","NE"
|
||||
state_us_23,us,"Nevada","NV"
|
||||
state_us_24,us,"New Hampshire","NH"
|
||||
state_us_25,us,"New Jersey","NJ"
|
||||
state_us_26,us,"New Mexico","NM"
|
||||
state_us_27,us,"New York","NY"
|
||||
state_us_28,us,"North Carolina","NC"
|
||||
state_us_29,us,"North Dakota","ND"
|
||||
state_us_30,us,"Ohio","OH"
|
||||
state_us_31,us,"Oklahoma","OK"
|
||||
state_us_32,us,"Oregon","OR"
|
||||
state_us_33,us,"Maryland","MD"
|
||||
state_us_34,us,"Massachusetts","MA"
|
||||
state_us_35,us,"Michigan","MI"
|
||||
state_us_36,us,"Minnesota","MN"
|
||||
state_us_37,us,"Mississippi","MS"
|
||||
state_us_38,us,"Missouri","MO"
|
||||
state_us_39,us,"Pennsylvania","PA"
|
||||
state_us_40,us,"Rhode Island","RI"
|
||||
state_us_41,us,"South Carolina","SC"
|
||||
state_us_42,us,"South Dakota","SD"
|
||||
state_us_43,us,"Tennessee","TN"
|
||||
state_us_44,us,"Texas","TX"
|
||||
state_us_45,us,"Utah","UT"
|
||||
state_us_46,us,"Vermont","VT"
|
||||
state_us_47,us,"Virginia","VA"
|
||||
state_us_48,us,"Washington","WA"
|
||||
state_us_49,us,"West Virginia","WV"
|
||||
state_us_50,us,"Wisconsin","WI"
|
||||
state_us_51,us,"Wyoming","WY"
|
||||
|
||||
|
+16
-16
@@ -7,13 +7,13 @@ msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 5.0.4\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2009-12-18 08:39+0000\n"
|
||||
"PO-Revision-Date: 2010-04-21 04:53+0000\n"
|
||||
"Last-Translator: OpenERP Administrators <Unknown>\n"
|
||||
"PO-Revision-Date: 2010-05-17 05:31+0000\n"
|
||||
"Last-Translator: Boris <boris.t.ivanov@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: 2010-04-22 03:42+0000\n"
|
||||
"X-Launchpad-Export-Date: 2010-05-18 05:14+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#. module: base
|
||||
@@ -83,7 +83,7 @@ msgstr "Създадени изгледи"
|
||||
#. module: base
|
||||
#: view:workflow.activity:0
|
||||
msgid "Outgoing transitions"
|
||||
msgstr "Изходящи промени"
|
||||
msgstr "Изходящи преходи"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.report.custom,frequency:0
|
||||
@@ -1754,7 +1754,7 @@ msgstr "XSL"
|
||||
#: code:addons/base/module/module.py:0
|
||||
#, python-format
|
||||
msgid "Can not upgrade module '%s'. It is not installed."
|
||||
msgstr "Не може да бъде обновен модул '%s'. Той не е инсталиран."
|
||||
msgstr "Модул '%s' не може да бъде обновен. Той не е инсталиран."
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.cu
|
||||
@@ -2039,7 +2039,7 @@ msgstr "Извличане на език"
|
||||
#. module: base
|
||||
#: selection:maintenance.contract.wizard,state:0
|
||||
msgid "Unvalidated"
|
||||
msgstr "Непроверен"
|
||||
msgstr "Невалидирано"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.ui.menu,name:base.next_id_9
|
||||
@@ -2066,7 +2066,7 @@ msgstr "Също така може да вмъкнете \".po\" файлове.
|
||||
#: code:addons/base/maintenance/maintenance.py:0
|
||||
#, python-format
|
||||
msgid "Unable to find a valid contract"
|
||||
msgstr "Не може да се намери валиден договор"
|
||||
msgstr "Не е намерeн валиден договор"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/ir/ir_actions.py:0
|
||||
@@ -5002,7 +5002,7 @@ msgstr "Python код"
|
||||
#: code:addons/base/module/wizard/wizard_module_import.py:0
|
||||
#, python-format
|
||||
msgid "Can not create the module file: %s !"
|
||||
msgstr "Модулния файл: %s, не можеда да бъде създаден !"
|
||||
msgstr "Модулния файл: %s, не можеда да бъде създаден !"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_meta_information
|
||||
@@ -5028,7 +5028,7 @@ msgstr "Откажи"
|
||||
#: code:addons/base/ir/ir_actions.py:0
|
||||
#, python-format
|
||||
msgid "Please specify server option --smtp-from !"
|
||||
msgstr "Моля укажете опцията на сървъра --smtp-from !"
|
||||
msgstr "Моля укажете опция на сървъра --smtp-from !"
|
||||
|
||||
#. module: base
|
||||
#: selection:wizard.module.lang.export,format:0
|
||||
@@ -6225,7 +6225,7 @@ msgstr "Извличане на файл с превод"
|
||||
#. module: base
|
||||
#: field:ir.ui.view_sc,user_id:0
|
||||
msgid "User Ref."
|
||||
msgstr "Справка за потребител"
|
||||
msgstr "Потребителска справка"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.ui.menu,name:base.menu_base_config
|
||||
@@ -6812,7 +6812,7 @@ msgstr "RML съдържание"
|
||||
#. module: base
|
||||
#: view:workflow.activity:0
|
||||
msgid "Incoming transitions"
|
||||
msgstr "Входящи промени"
|
||||
msgstr "Входящи преходи"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.cn
|
||||
@@ -7277,7 +7277,7 @@ msgstr "Оригинал"
|
||||
#. module: base
|
||||
#: help:res.partner.address,partner_id:0
|
||||
msgid "Keep empty for a private address, not related to partner."
|
||||
msgstr "Оставете празно за личен адрес несвързан с партньора"
|
||||
msgstr "Оставете празно за личен адрес нямащ отношение към партньора."
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.vu
|
||||
@@ -7296,8 +7296,8 @@ msgid ""
|
||||
"Save this document to a .tgz file. This archive containt UTF-8 %s files and "
|
||||
"may be uploaded to launchpad."
|
||||
msgstr ""
|
||||
"Запази документа като .tgz файл. Този архив съдържа %s UTF-8 файлове и може "
|
||||
"да бъде качен в launchpad."
|
||||
"Запазете документа като .tgz файл. Този архив съдържа %s UTF-8 файлове и "
|
||||
"може да бъде качен в launchpad."
|
||||
|
||||
#. module: base
|
||||
#: wizard_button:module.upgrade,end,config:0
|
||||
@@ -7728,7 +7728,7 @@ msgid ""
|
||||
"invoice, then `object.invoice_address_id.mobile` is the field which gives "
|
||||
"the correct mobile number"
|
||||
msgstr ""
|
||||
"Оказва полетата които ще се използват за доставяне на мобилен номер, напр., "
|
||||
"Указва полетата които ще се използват за доставяне на мобилен номер, напр., "
|
||||
"когато изберете фактура, тогава 'object.invoice_address_id.mobile' е полето "
|
||||
"което дава правилния мобилен номер"
|
||||
|
||||
@@ -8080,7 +8080,7 @@ msgstr "полски / Język polski"
|
||||
#. module: base
|
||||
#: field:ir.exports,name:0
|
||||
msgid "Export Name"
|
||||
msgstr "Име на извлечането"
|
||||
msgstr "Име на изнасянето"
|
||||
|
||||
#. module: base
|
||||
#: help:res.partner.address,type:0
|
||||
|
||||
@@ -7,13 +7,13 @@ msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 5.0.4\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2009-12-18 08:39+0000\n"
|
||||
"PO-Revision-Date: 2010-05-11 05:41+0000\n"
|
||||
"PO-Revision-Date: 2010-05-23 04:28+0000\n"
|
||||
"Last-Translator: goranc <goranc@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: 2010-05-12 04:19+0000\n"
|
||||
"X-Launchpad-Export-Date: 2010-05-24 03:45+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
"Language: hr\n"
|
||||
|
||||
|
||||
@@ -7,13 +7,13 @@ msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 5.0.4\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2009-12-18 08:39+0000\n"
|
||||
"PO-Revision-Date: 2010-04-29 04:41+0000\n"
|
||||
"PO-Revision-Date: 2010-05-17 05:31+0000\n"
|
||||
"Last-Translator: eLBati - albatos.com <lorenzo.battistini@albatos.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: 2010-04-30 03:50+0000\n"
|
||||
"X-Launchpad-Export-Date: 2010-05-18 05:14+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#. module: base
|
||||
@@ -901,7 +901,7 @@ msgstr "Relazione"
|
||||
#. module: base
|
||||
#: field:ir.model.access,perm_read:0
|
||||
msgid "Read Access"
|
||||
msgstr "Accesso di Lettura"
|
||||
msgstr "Accesso in lettura"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_ir_exports
|
||||
@@ -2150,10 +2150,7 @@ msgstr "Crea Menu"
|
||||
msgid ""
|
||||
"Value Added Tax number. Check the box if the partner is subjected to the "
|
||||
"VAT. Used by the VAT legal statement."
|
||||
msgstr ""
|
||||
"numero di Partita IVA (Imposta sul valore Aggiunto - VAT). Seleziona la "
|
||||
"casella se il partner è soggetto IVA. Usato dalla dichiarazione legale "
|
||||
"sull'IVA."
|
||||
msgstr "Seleziona la casella se il partner è soggetto IVA."
|
||||
|
||||
#. module: base
|
||||
#: model:ir.actions.act_window,name:base.action_module_category_tree
|
||||
@@ -2204,7 +2201,7 @@ msgstr "Registra Regole"
|
||||
#. module: base
|
||||
#: field:res.partner,vat:0
|
||||
msgid "VAT"
|
||||
msgstr "IVA"
|
||||
msgstr "Partita IVA"
|
||||
|
||||
#. module: base
|
||||
#: view:res.lang:0
|
||||
@@ -3878,7 +3875,7 @@ msgstr "Esegui Conteggio"
|
||||
#. module: base
|
||||
#: field:ir.model.access,perm_create:0
|
||||
msgid "Create Access"
|
||||
msgstr "Crea Accesso"
|
||||
msgstr "Accesso in creazione"
|
||||
|
||||
#. module: base
|
||||
#: field:res.partner.address,state_id:0
|
||||
@@ -5509,7 +5506,7 @@ msgstr "Sig.rina"
|
||||
#. module: base
|
||||
#: field:ir.model.access,perm_write:0
|
||||
msgid "Write Access"
|
||||
msgstr "Accesso Scrittura"
|
||||
msgstr "Accesso in scrittura"
|
||||
|
||||
#. module: base
|
||||
#: field:res.bank,city:0
|
||||
@@ -7414,7 +7411,7 @@ msgstr ">"
|
||||
#. module: base
|
||||
#: field:ir.model.access,perm_unlink:0
|
||||
msgid "Delete Permission"
|
||||
msgstr "Cancella Autorizzazione"
|
||||
msgstr "Permesso di cancellazione"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_multi_company_default
|
||||
@@ -7682,7 +7679,7 @@ msgstr ""
|
||||
#. module: base
|
||||
#: constraint:res.partner:0
|
||||
msgid "The VAT doesn't seem to be correct."
|
||||
msgstr "L'IVA non sembra essere corretta"
|
||||
msgstr "La P.IVA non sembra essere corretta"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.report.custom.fields,operation:0
|
||||
|
||||
@@ -8,13 +8,13 @@ msgstr ""
|
||||
"Project-Id-Version: openobject-server\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2009-12-18 08:39+0000\n"
|
||||
"PO-Revision-Date: 2010-05-11 05:36+0000\n"
|
||||
"PO-Revision-Date: 2010-05-23 04:32+0000\n"
|
||||
"Last-Translator: Harry (Open ERP) <hmo@tinyerp.com>\n"
|
||||
"Language-Team: Japanese <ja@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2010-05-12 04:19+0000\n"
|
||||
"X-Launchpad-Export-Date: 2010-05-24 03:45+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#. module: base
|
||||
|
||||
+28
-28
@@ -8,13 +8,13 @@ msgstr ""
|
||||
"Project-Id-Version: openobject-addons\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2009-12-18 08:39+0000\n"
|
||||
"PO-Revision-Date: 2010-01-04 06:03+0000\n"
|
||||
"Last-Translator: Fabien (Open ERP) <fp@tinyerp.com>\n"
|
||||
"PO-Revision-Date: 2010-05-19 12:29+0000\n"
|
||||
"Last-Translator: Olivier Dony (OpenERP) <Unknown>\n"
|
||||
"Language-Team: Latvian <lv@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2010-04-17 03:51+0000\n"
|
||||
"X-Launchpad-Export-Date: 2010-05-21 03:37+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#. module: base
|
||||
@@ -176,7 +176,7 @@ msgstr "ir.report.custom.fields"
|
||||
#. module: base
|
||||
#: view:res.partner:0
|
||||
msgid "Search Partner"
|
||||
msgstr ""
|
||||
msgstr "Meklēt Partneri"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/module/wizard/wizard_export_lang.py:0
|
||||
@@ -419,7 +419,7 @@ msgstr "Kolumbija"
|
||||
#. module: base
|
||||
#: view:ir.module.module:0
|
||||
msgid "Schedule Upgrade"
|
||||
msgstr ""
|
||||
msgstr "Ieplānot Jauninājumus"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.actions.report.custom,report_id:0
|
||||
@@ -593,7 +593,7 @@ msgstr ","
|
||||
#. module: base
|
||||
#: view:res.partner:0
|
||||
msgid "My Partners"
|
||||
msgstr ""
|
||||
msgstr "Mani Partneri"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.es
|
||||
@@ -745,7 +745,7 @@ msgstr "Irāna"
|
||||
#: model:ir.actions.act_window,name:base.res_request-act
|
||||
#: model:ir.ui.menu,name:base.menu_res_request_act
|
||||
msgid "My Requests"
|
||||
msgstr ""
|
||||
msgstr "Mani Pieprasījumi"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.sequence,name:0
|
||||
@@ -1009,7 +1009,7 @@ msgstr "STOCK_COPY"
|
||||
#: code:addons/base/ir/ir_model.py:0
|
||||
#, python-format
|
||||
msgid "Model %s Does not Exist !"
|
||||
msgstr ""
|
||||
msgstr "Modelis %s neeksistē !"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/module/module.py:0
|
||||
@@ -1137,7 +1137,7 @@ msgstr "Lietotāja Atskaite"
|
||||
#: code:addons/base/res/res_user.py:0
|
||||
#, python-format
|
||||
msgid " (copy)"
|
||||
msgstr ""
|
||||
msgstr " (kopija)"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.sequence:0
|
||||
@@ -1303,7 +1303,7 @@ msgstr "Copy text \t STOCK_PROPERTIES"
|
||||
#. module: base
|
||||
#: view:res.partner.address:0
|
||||
msgid "Search Contact"
|
||||
msgstr ""
|
||||
msgstr "Meklēt Kontaktu"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.module.module:0
|
||||
@@ -1484,7 +1484,7 @@ msgstr "Esošā Likme"
|
||||
#. module: base
|
||||
#: selection:module.lang.install,init,lang:0
|
||||
msgid "Greek / Ελληνικά"
|
||||
msgstr ""
|
||||
msgstr "Grieķu / Ελληνικά"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.values:0
|
||||
@@ -1565,7 +1565,7 @@ msgstr "Epasta Adrese"
|
||||
#. module: base
|
||||
#: selection:module.lang.install,init,lang:0
|
||||
msgid "French (BE) / Français (BE)"
|
||||
msgstr ""
|
||||
msgstr "Franču (BE) / Français (BE)"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/ir/ir_model.py:0
|
||||
@@ -1603,7 +1603,7 @@ msgstr "Lauka Sasaistes"
|
||||
#: model:ir.actions.act_window,name:base.res_request-closed
|
||||
#: model:ir.ui.menu,name:base.next_id_12_close
|
||||
msgid "My Closed Requests"
|
||||
msgstr ""
|
||||
msgstr "Mani Slēgie Pieprasījumi"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.ui.menu,name:base.menu_custom
|
||||
@@ -1861,7 +1861,7 @@ msgstr "Pieejas Kontrole"
|
||||
#: view:ir.module.module:0
|
||||
#: field:ir.module.module,dependencies_id:0
|
||||
msgid "Dependencies"
|
||||
msgstr "Atkarīgie objekti"
|
||||
msgstr "Atkarīgs no"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.report.custom.fields,bgcolor:0
|
||||
@@ -1988,7 +1988,7 @@ msgstr "Modulis"
|
||||
#: model:ir.actions.act_window,name:base.action_res_bank_form
|
||||
#: model:ir.ui.menu,name:base.menu_action_res_bank_form
|
||||
msgid "Bank List"
|
||||
msgstr ""
|
||||
msgstr "Banku Saraksts"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.attachment,description:0
|
||||
@@ -2401,7 +2401,7 @@ msgstr ""
|
||||
#: model:ir.actions.act_window,name:base.action_partner_customer_form
|
||||
#: view:res.partner:0
|
||||
msgid "Customers"
|
||||
msgstr ""
|
||||
msgstr "Klienti"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.au
|
||||
@@ -2603,7 +2603,7 @@ msgstr "STOCK_SAVE_AS"
|
||||
#. module: base
|
||||
#: selection:ir.translation,type:0
|
||||
msgid "SQL Constraint"
|
||||
msgstr ""
|
||||
msgstr "SQL Ierobežojums"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.actions.server,srcmodel_id:0
|
||||
@@ -2669,7 +2669,7 @@ msgstr "%c - Atbilstošais datuma un laika attēlojums."
|
||||
#. module: base
|
||||
#: selection:module.lang.install,init,lang:0
|
||||
msgid "Finland / Suomi"
|
||||
msgstr ""
|
||||
msgstr "Somu / Suomi"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.bo
|
||||
@@ -2931,7 +2931,7 @@ msgstr "Likmes"
|
||||
#. module: base
|
||||
#: selection:module.lang.install,init,lang:0
|
||||
msgid "Albanian / Shqipëri"
|
||||
msgstr ""
|
||||
msgstr "Albāņu / Shqipëri"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sy
|
||||
@@ -3192,7 +3192,7 @@ msgstr "Demo dati"
|
||||
#. module: base
|
||||
#: selection:module.lang.install,init,lang:0
|
||||
msgid "English (UK)"
|
||||
msgstr ""
|
||||
msgstr "Angļu (UK)"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.aq
|
||||
@@ -3217,7 +3217,7 @@ msgstr "Tīmeklis"
|
||||
#. module: base
|
||||
#: selection:module.lang.install,init,lang:0
|
||||
msgid "English (CA)"
|
||||
msgstr ""
|
||||
msgstr "Angļu (CA)"
|
||||
|
||||
#. module: base
|
||||
#: field:res.partner.event,planned_revenue:0
|
||||
@@ -3460,7 +3460,7 @@ msgstr "Partnera Adreses"
|
||||
#. module: base
|
||||
#: selection:module.lang.install,init,lang:0
|
||||
msgid "Indonesian / Bahasa Indonesia"
|
||||
msgstr ""
|
||||
msgstr "Indonēziešu / Bahasa Indonesia"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.cv
|
||||
@@ -3625,7 +3625,7 @@ msgstr "Izveidot darbību"
|
||||
#. module: base
|
||||
#: selection:ir.actions.report.xml,report_type:0
|
||||
msgid "HTML from HTML"
|
||||
msgstr ""
|
||||
msgstr "HTML no HTML"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.actions.report.xml,report_type:0
|
||||
@@ -4086,7 +4086,7 @@ msgstr "Faila Formāts"
|
||||
#. module: base
|
||||
#: field:res.lang,iso_code:0
|
||||
msgid "ISO code"
|
||||
msgstr ""
|
||||
msgstr "ISO kods"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_res_config_view
|
||||
@@ -4222,7 +4222,7 @@ msgstr "Izmainīt manus Uzstādījumus"
|
||||
#. module: base
|
||||
#: constraint:ir.actions.act_window:0
|
||||
msgid "Invalid model name in the action definition."
|
||||
msgstr ""
|
||||
msgstr "Procesa definīcijā nepareizs modeļa nosaukums."
|
||||
|
||||
#. module: base
|
||||
#: wizard_field:res.partner.sms_send,init,text:0
|
||||
@@ -4344,7 +4344,7 @@ msgstr "Objekta Lauks"
|
||||
#. module: base
|
||||
#: selection:module.lang.install,init,lang:0
|
||||
msgid "French (CH) / Français (CH)"
|
||||
msgstr ""
|
||||
msgstr "Franču (CH) / Français (CH)"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.ui.menu,icon:0
|
||||
@@ -4418,7 +4418,7 @@ msgstr "Atcelt atinstalāciju"
|
||||
#: view:res.partner:0
|
||||
#: view:res.partner.address:0
|
||||
msgid "Communication"
|
||||
msgstr ""
|
||||
msgstr "Saziņa"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_ir_server_object_lines
|
||||
@@ -6981,7 +6981,7 @@ msgstr "(year)="
|
||||
#. module: base
|
||||
#: rml:ir.module.reference:0
|
||||
msgid "Dependencies :"
|
||||
msgstr "Atkarīgie objekti:"
|
||||
msgstr "Atkarīgs no:"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.ui.menu,icon:0
|
||||
|
||||
@@ -7,13 +7,13 @@ msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 5.0.0\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2009-12-18 08:39+0000\n"
|
||||
"PO-Revision-Date: 2010-05-11 05:58+0000\n"
|
||||
"PO-Revision-Date: 2010-05-23 04:30+0000\n"
|
||||
"Last-Translator: Anders Wallenquist <anders.wallenquist@vertel.se>\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: 2010-05-12 04:19+0000\n"
|
||||
"X-Launchpad-Export-Date: 2010-05-24 03:45+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#. module: base
|
||||
|
||||
@@ -7,13 +7,13 @@ msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 5.0.0\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2009-12-18 08:39+0000\n"
|
||||
"PO-Revision-Date: 2010-05-11 05:47+0000\n"
|
||||
"PO-Revision-Date: 2010-05-23 04:34+0000\n"
|
||||
"Last-Translator: Fabien (Open ERP) <fp@tinyerp.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: 2010-05-12 04:19+0000\n"
|
||||
"X-Launchpad-Export-Date: 2010-05-24 03:45+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#. module: base
|
||||
|
||||
@@ -7,13 +7,13 @@ msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 5.0.4\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2009-12-18 08:39+0000\n"
|
||||
"PO-Revision-Date: 2010-05-11 06:16+0000\n"
|
||||
"PO-Revision-Date: 2010-05-23 04:32+0000\n"
|
||||
"Last-Translator: OpenERP Administrators <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: 2010-05-12 04:20+0000\n"
|
||||
"X-Launchpad-Export-Date: 2010-05-24 03:46+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#. module: base
|
||||
|
||||
@@ -28,6 +28,7 @@ import ir_actions
|
||||
import ir_report_custom
|
||||
import ir_attachment
|
||||
import ir_cron
|
||||
import ir_filters
|
||||
import ir_values
|
||||
import ir_translation
|
||||
import ir_exports
|
||||
|
||||
@@ -230,8 +230,59 @@
|
||||
</record>
|
||||
<menuitem id="next_id_6" name="Actions" parent="base.next_id_4"/>
|
||||
<menuitem action="ir_sequence_actions" id="menu_ir_sequence_actions" parent="next_id_6"/>
|
||||
|
||||
<!-- Filters form view-->
|
||||
|
||||
<record id="act_report_custom_view" model="ir.ui.view">
|
||||
<record id="ir_filters_view_form" model="ir.ui.view">
|
||||
<field name="name">ir.filters.form</field>
|
||||
<field name="model">ir.filters</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Filters">
|
||||
<field name="name" select="1"/>
|
||||
<field name="user_id" select="1"/>
|
||||
<field name="model_id"/>
|
||||
<newline/>
|
||||
<field name="domain"/>
|
||||
<field name="context"/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Filters tree view-->
|
||||
|
||||
<record id="ir_filters_view_tree" model="ir.ui.view">
|
||||
<field name="name">ir.filters.tree</field>
|
||||
<field name="model">ir.filters</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Filters">
|
||||
<field name="name" select="1"/>
|
||||
<field name="user_id" select="1"/>
|
||||
<field name="model_id"/>
|
||||
<field name="domain"/>
|
||||
<field name="context"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Filters action-->
|
||||
|
||||
<record id="actions_ir_filters_view" model="ir.actions.act_window">
|
||||
<field name="name">Filters</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">ir.filters</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_id" ref="ir_filters_view_tree"/>
|
||||
</record>
|
||||
|
||||
<!-- Filters view menu-->
|
||||
|
||||
<menuitem parent="base.next_id_6" name="Filters"
|
||||
id="menu_ir_filters" action="actions_ir_filters_view" />
|
||||
|
||||
|
||||
<record id="act_report_custom_view" model="ir.ui.view">
|
||||
<field name="name">ir.actions.report.custom</field>
|
||||
<field name="model">ir.actions.report.custom</field>
|
||||
<field name="type">form</field>
|
||||
@@ -1251,14 +1302,14 @@
|
||||
<field name="subject" colspan="4" attrs="{'required':[('state','=','email')]}"/>
|
||||
<field name="message" select="2" colspan="4" attrs="{'required':[('state','=','email')]}"/>
|
||||
<newline/>
|
||||
<label colspan="4" string="Access all the fields related to the current object using expression in double brackets, i.e.[[ object.partner_id.name ]]" align="0.0"/>
|
||||
<label colspan="4" string="Access all the fields related to the current object using expressions, i.e. object.partner_id.name " align="0.0"/>
|
||||
</page>
|
||||
<page string="SMS Configuration" attrs="{'invisible':[('state','!=','sms')]}">
|
||||
<separator colspan="4" string="SMS Configuration"/>
|
||||
<field name="mobile" domain="[('model_id','=',model_id)]" attrs="{'required':[('state','=','sms')]}"/>
|
||||
<field name="sms" colspan="4" attrs="{'required':[('state','=','sms')]}"/>
|
||||
<newline/>
|
||||
<label colspan="4" string="Access all the fields related to the current object using expression in double brackets, i.e. [[ object.partner_id.name ]]" align="0.0"/>
|
||||
<label colspan="4" string="Access all the fields related to the current object using expressions, i.e. object.partner_id.name " align="0.0"/>
|
||||
</page>
|
||||
<page string="Create / Write / Copy" attrs="{'invisible':[('state','!=','object_create'), ('state','!=','object_write'), ('state','!=','object_copy')]}">
|
||||
<separator colspan="4" string="Fields Mapping"/>
|
||||
|
||||
@@ -455,7 +455,7 @@ class actions_server(osv.osv):
|
||||
'type': lambda *a: 'ir.actions.server',
|
||||
'sequence': lambda *a: 5,
|
||||
'code': lambda *a: """# You can use the following variables
|
||||
# - object
|
||||
# - object or obj
|
||||
# - time
|
||||
# - cr
|
||||
# - uid
|
||||
@@ -532,7 +532,6 @@ class actions_server(osv.osv):
|
||||
|
||||
def run(self, cr, uid, ids, context={}):
|
||||
logger = netsvc.Logger()
|
||||
|
||||
for action in self.browse(cr, uid, ids, context):
|
||||
obj_pool = self.pool.get(action.model_id.model)
|
||||
obj = obj_pool.browse(cr, uid, context['active_id'], context=context)
|
||||
@@ -554,19 +553,26 @@ class actions_server(osv.osv):
|
||||
return self.pool.get(action.action_id.type)\
|
||||
.read(cr, uid, action.action_id.id, context=context)
|
||||
|
||||
if action.state == 'code':
|
||||
localdict = {
|
||||
'self': self.pool.get(action.model_id.model),
|
||||
'context': context,
|
||||
'time': time,
|
||||
'ids': ids,
|
||||
'cr': cr,
|
||||
'uid': uid,
|
||||
'object':obj
|
||||
}
|
||||
exec action.code in localdict
|
||||
if 'action' in localdict:
|
||||
return localdict['action']
|
||||
if config['server_actions_allow_code']:
|
||||
localdict = {
|
||||
'self': self.pool.get(action.model_id.model),
|
||||
'context': context,
|
||||
'time': time,
|
||||
'ids': ids,
|
||||
'cr': cr,
|
||||
'uid': uid,
|
||||
'object':obj,
|
||||
'obj': obj,
|
||||
}
|
||||
exec action.code in localdict
|
||||
if 'action' in localdict:
|
||||
return localdict['action']
|
||||
else:
|
||||
netsvc.Logger().notifyChannel(
|
||||
self._name, netsvc.LOG_ERROR,
|
||||
"%s is a `code` server action, but "
|
||||
"it isn't allowed in this configuration.\n\n"
|
||||
"See server options to enable it"%action)
|
||||
|
||||
if action.state == 'email':
|
||||
user = config['email_from']
|
||||
@@ -580,7 +586,7 @@ class actions_server(osv.osv):
|
||||
logger.notifyChannel('email', netsvc.LOG_INFO, 'Partner Email address not Specified!')
|
||||
continue
|
||||
if not user:
|
||||
raise osv.except_osv(_('Error'), _("Please specify server option --smtp-from !"))
|
||||
raise osv.except_osv(_('Error'), _("Please specify server option --email-from !"))
|
||||
|
||||
subject = self.merge_message(cr, uid, action.subject, action, context)
|
||||
body = self.merge_message(cr, uid, action.message, action, context)
|
||||
|
||||
@@ -146,16 +146,19 @@ class ir_cron(osv.osv, netsvc.Agent):
|
||||
def create(self, cr, uid, vals, context=None):
|
||||
res = super(ir_cron, self).create(cr, uid, vals, context=context)
|
||||
cr.commit()
|
||||
self.cancel(cr.dbname)
|
||||
self._poolJobs(cr.dbname)
|
||||
return res
|
||||
def write(self, cr, user, ids, vals, context=None):
|
||||
res = super(ir_cron, self).write(cr, user, ids, vals, context=context)
|
||||
cr.commit()
|
||||
self.cancel(cr.dbname)
|
||||
self._poolJobs(cr.dbname)
|
||||
return res
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
res = super(ir_cron, self).unlink(cr, uid, ids, context=context)
|
||||
cr.commit()
|
||||
self.cancel(cr.dbname)
|
||||
self._poolJobs(cr.dbname)
|
||||
return res
|
||||
ir_cron()
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2010 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/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from osv import osv, fields
|
||||
|
||||
class ir_filters(osv.osv):
|
||||
'''
|
||||
Filters
|
||||
'''
|
||||
_name = 'ir.filters'
|
||||
_description = 'Filters'
|
||||
|
||||
def _list_all_models(self, cr, uid, context=None):
|
||||
cr.execute("SELECT model, name from ir_model")
|
||||
return cr.fetchall()
|
||||
|
||||
_columns = {
|
||||
'name': fields.char('Action Name', size=64, translate=True, required=True),
|
||||
'user_id':fields.many2one('res.users', 'User', help='False means for every user'),
|
||||
'domain': fields.char('Domain Value', size=250, required=True),
|
||||
'context': fields.char('Context Value', size=250, required=True),
|
||||
'model_id': fields.selection(_list_all_models, 'Model', required=True),
|
||||
}
|
||||
|
||||
ir_filters()
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
@@ -82,6 +82,8 @@ class ir_model(osv.osv):
|
||||
return super(ir_model,self).write(cr, user, ids, vals, context)
|
||||
|
||||
def create(self, cr, user, vals, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
if context and context.get('manual',False):
|
||||
vals['state']='manual'
|
||||
res = super(ir_model,self).create(cr, user, vals, context)
|
||||
@@ -260,6 +262,8 @@ class ir_model_fields(osv.osv):
|
||||
if 'model_id' in vals:
|
||||
model_data = self.pool.get('ir.model').browse(cr, user, vals['model_id'])
|
||||
vals['model'] = model_data.model
|
||||
if context is None:
|
||||
context = {}
|
||||
if context and context.get('manual',False):
|
||||
vals['state'] = 'manual'
|
||||
res = super(ir_model_fields,self).create(cr, user, vals, context)
|
||||
@@ -267,7 +271,7 @@ class ir_model_fields(osv.osv):
|
||||
if not vals['name'].startswith('x_'):
|
||||
raise except_orm(_('Error'), _("Custom fields must have a name that starts with 'x_' !"))
|
||||
|
||||
if 'relation' in vals and not self.pool.get('ir.model').search(cr, user, [('model','=',vals['relation'])]):
|
||||
if vals.get('relation',False) and not self.pool.get('ir.model').search(cr, user, [('model','=',vals['relation'])]):
|
||||
raise except_orm(_('Error'), _("Model %s Does not Exist !" % vals['relation']))
|
||||
|
||||
if self.pool.get(vals['model']):
|
||||
@@ -286,7 +290,7 @@ class ir_model_access(osv.osv):
|
||||
_columns = {
|
||||
'name': fields.char('Name', size=64, required=True),
|
||||
'model_id': fields.many2one('ir.model', 'Object', required=True),
|
||||
'group_id': fields.many2one('res.groups', 'Group'),
|
||||
'group_id': fields.many2one('res.groups', 'Group', ondelete='cascade'),
|
||||
'perm_read': fields.boolean('Read Access'),
|
||||
'perm_write': fields.boolean('Write Access'),
|
||||
'perm_create': fields.boolean('Create Access'),
|
||||
@@ -453,7 +457,7 @@ class ir_model_data(osv.osv):
|
||||
def _get_id(self, cr, uid, module, xml_id):
|
||||
ids = self.search(cr, uid, [('module','=',module),('name','=', xml_id)])
|
||||
if not ids:
|
||||
raise Exception('No references to %s.%s' % (module, xml_id))
|
||||
raise ValueError('No references to %s.%s' % (module, xml_id))
|
||||
# the sql constraints ensure us we have only one result
|
||||
return ids[0]
|
||||
|
||||
|
||||
@@ -150,8 +150,8 @@ class ir_translation(osv.osv):
|
||||
if not context:
|
||||
context = {}
|
||||
ids = super(ir_translation, self).create(cursor, user, vals, context=context)
|
||||
for trans_obj in self.read(cursor, user, [ids], ['name','type','res_id'], context=context):
|
||||
self._get_source.clear_cache(cursor.dbname, user, trans_obj['name'], trans_obj['type'], lang=context.get('lang','en_US'))
|
||||
for trans_obj in self.read(cursor, user, [ids], ['name','type','res_id','src','lang'], context=context):
|
||||
self._get_source.clear_cache(cursor.dbname, user, trans_obj['name'], trans_obj['type'], trans_obj['lang'], source=trans_obj['src'])
|
||||
self._get_ids.clear_cache(cursor.dbname, user, trans_obj['name'], trans_obj['type'], context.get('lang','en_US'), [trans_obj['res_id']])
|
||||
return ids
|
||||
|
||||
@@ -159,16 +159,16 @@ class ir_translation(osv.osv):
|
||||
if not context:
|
||||
context = {}
|
||||
result = super(ir_translation, self).write(cursor, user, ids, vals, context=context)
|
||||
for trans_obj in self.read(cursor, user, ids, ['name','type','res_id'], context=context):
|
||||
self._get_source.clear_cache(cursor.dbname, user, trans_obj['name'], trans_obj['type'], lang=context.get('lang','en_US'))
|
||||
for trans_obj in self.read(cursor, user, ids, ['name','type','res_id','src','lang'], context=context):
|
||||
self._get_source.clear_cache(cursor.dbname, user, trans_obj['name'], trans_obj['type'], trans_obj['lang'], source=trans_obj['src'])
|
||||
self._get_ids.clear_cache(cursor.dbname, user, trans_obj['name'], trans_obj['type'], context.get('lang','en_US'), [trans_obj['res_id']])
|
||||
return result
|
||||
|
||||
def unlink(self, cursor, user, ids, context=None):
|
||||
if not context:
|
||||
context = {}
|
||||
for trans_obj in self.read(cursor, user, ids, ['name','type','res_id'], context=context):
|
||||
self._get_source.clear_cache(cursor.dbname, user, trans_obj['name'], trans_obj['type'], lang=context.get('lang','en_US'))
|
||||
for trans_obj in self.read(cursor, user, ids, ['name','type','res_id','src','lang'], context=context):
|
||||
self._get_source.clear_cache(cursor.dbname, user, trans_obj['name'], trans_obj['type'], trans_obj['lang'], source=trans_obj['src'])
|
||||
self._get_ids.clear_cache(cursor.dbname, user, trans_obj['name'], trans_obj['type'], context.get('lang','en_US'), [trans_obj['res_id']])
|
||||
result = super(ir_translation, self).unlink(cursor, user, ids, context=context)
|
||||
return result
|
||||
|
||||
@@ -31,6 +31,10 @@ def one_in(setA, setB):
|
||||
return True
|
||||
return False
|
||||
|
||||
def cond(C, X, Y):
|
||||
if C: return X
|
||||
return Y
|
||||
|
||||
class many2many_unique(fields.many2many):
|
||||
def set(self, cr, obj, id, name, values, user=None, context=None):
|
||||
if not values:
|
||||
@@ -63,16 +67,24 @@ class ir_ui_menu(osv.osv):
|
||||
# radical but this doesn't frequently happen
|
||||
self._cache = {}
|
||||
|
||||
def search(self, cr, uid, args, offset=0, limit=2000, order=None,
|
||||
context=None, count=False):
|
||||
if context is None:
|
||||
context = {}
|
||||
ids = osv.orm.orm.search(self, cr, uid, args, offset, limit, order, context=context, count=(count and uid==1))
|
||||
def search(self, cr, uid, args, offset=0, limit=None, order=None,
|
||||
context=None, count=False):
|
||||
|
||||
super_offset = cond(uid == 1, offset, 0)
|
||||
super_limit = cond(uid == 1, limit, None)
|
||||
super_count = cond(uid == 1, count, False)
|
||||
|
||||
ids = super(ir_ui_menu, self).search(cr, uid, args, super_offset,
|
||||
super_limit, order,
|
||||
context=context, count=super_count)
|
||||
if uid == 1 or count:
|
||||
return ids
|
||||
|
||||
if not ids:
|
||||
if count:
|
||||
return 0
|
||||
return []
|
||||
|
||||
|
||||
modelaccess = self.pool.get('ir.model.access')
|
||||
user_groups = set(self.pool.get('res.users').read(cr, 1, uid, ['groups_id'])['groups_id'])
|
||||
@@ -120,6 +132,11 @@ class ir_ui_menu(osv.osv):
|
||||
result.append(menu.id)
|
||||
self._cache[key] = True
|
||||
|
||||
if offset:
|
||||
result = result[long(offset):]
|
||||
if limit:
|
||||
result = result[:long(limit)]
|
||||
|
||||
if count:
|
||||
return len(result)
|
||||
return result
|
||||
|
||||
@@ -200,6 +200,10 @@ class view_sc(osv.osv):
|
||||
'resource': lambda *a: 'ir.ui.menu',
|
||||
'user_id': lambda obj, cr, uid, context: uid,
|
||||
}
|
||||
_sql_constraints = [
|
||||
('shortcut_unique', 'unique(res_id, user_id)', 'Shortcut for this menu already exists!'),
|
||||
]
|
||||
|
||||
view_sc()
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
# -*- coding: utf8 -*-
|
||||
|
||||
__name__ = "res.partner.address: change type of 'function' field many2one to char"
|
||||
|
||||
def migrate(cr, version):
|
||||
change_column_type(cr,'res_partner_address')
|
||||
|
||||
def change_column_type(cr,table):
|
||||
cr.execute('SELECT id, name FROM res_partner_function')
|
||||
all_function = cr.fetchall()
|
||||
cr.execute('ALTER TABLE %s ADD COLUMN temp_function VARCHAR(64)' % table)
|
||||
for fn in all_function:
|
||||
cr.execute("UPDATE %s SET temp_function = '%s' WHERE function = %s" % (table,fn[1],fn[0]))
|
||||
cr.execute("ALTER TABLE %s DROP COLUMN function CASCADE" % table)
|
||||
cr.execute("ALTER TABLE %s RENAME COLUMN temp_function TO function" % table)
|
||||
|
||||
@@ -146,11 +146,13 @@ class module(osv.osv):
|
||||
], string='State', readonly=True),
|
||||
'demo': fields.boolean('Demo data'),
|
||||
'license': fields.selection([
|
||||
('GPL-2', 'GPL-2'),
|
||||
('GPL-2', 'GPL Version 2'),
|
||||
('GPL-2 or any later version', 'GPL-2 or later version'),
|
||||
('GPL-3', 'GPL-3'),
|
||||
('GPL-3', 'GPL Version 3'),
|
||||
('GPL-3 or any later version', 'GPL-3 or later version'),
|
||||
('Other proprietary', 'Other proprietary')
|
||||
('AGPL-3', 'Affero GPL-3'),
|
||||
('Other OSI approved licence', 'Other OSI Approved Licence'),
|
||||
('Other proprietary', 'Other Proprietary')
|
||||
], string='License', readonly=True),
|
||||
'menus_by_module': fields.function(_get_views, method=True, string='Menus', type='text', multi="meta", store=True),
|
||||
'reports_by_module': fields.function(_get_views, method=True, string='Reports', type='text', multi="meta", store=True),
|
||||
@@ -161,7 +163,7 @@ class module(osv.osv):
|
||||
_defaults = {
|
||||
'state': lambda *a: 'uninstalled',
|
||||
'demo': lambda *a: False,
|
||||
'license': lambda *a: 'GPL-2',
|
||||
'license': lambda *a: 'AGPL-3',
|
||||
}
|
||||
_order = 'name'
|
||||
|
||||
|
||||
@@ -31,8 +31,6 @@ import res_company
|
||||
import res_user
|
||||
import res_request
|
||||
import res_lang
|
||||
|
||||
import res_log
|
||||
import ir_property
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 16 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 13 KiB |
@@ -40,8 +40,7 @@ class ir_property(osv.osv):
|
||||
return res
|
||||
|
||||
def _models_get(self, cr, uid, context=None):
|
||||
return self._models_field_get(cr, uid, 'model_id.model', 'model_id.name',
|
||||
context)
|
||||
return self._models_field_get(cr, uid, 'model', 'name', context)
|
||||
|
||||
def _models_get2(self, cr, uid, context=None):
|
||||
return self._models_field_get(cr, uid, 'relation', 'relation', context)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
@@ -15,7 +15,7 @@
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
@@ -27,21 +27,6 @@ import ir
|
||||
import pooler
|
||||
from tools.translate import _
|
||||
|
||||
class res_partner_function(osv.osv):
|
||||
_name = 'res.partner.function'
|
||||
_description = 'Function of the contact'
|
||||
_columns = {
|
||||
'name': fields.char('Function Name', size=64, required=True),
|
||||
'code': fields.char('Code', size=8, required=True),
|
||||
'ref':fields.char('Notes', size=32,),
|
||||
}
|
||||
_order = 'name'
|
||||
_sql_constraints = [
|
||||
('code_uniq', 'unique (code)', 'The Code of the Partner Function must be unique !')
|
||||
]
|
||||
res_partner_function()
|
||||
|
||||
|
||||
class res_payterm(osv.osv):
|
||||
_description = 'Payment term'
|
||||
_name = 'res.payterm'
|
||||
@@ -149,12 +134,12 @@ class res_partner(osv.osv):
|
||||
'active': fields.boolean('Active'),
|
||||
'customer': fields.boolean('Customer', help="Check this box if the partner is a customer."),
|
||||
'supplier': fields.boolean('Supplier', help="Check this box if the partner is a supplier. If it's not checked, purchase people will not see it when encoding a purchase order."),
|
||||
'city': fields.related('address', 'city', type='char', string='City'),
|
||||
'phone': fields.related('address', 'phone', type='char', string='Phone'),
|
||||
'city': fields.related('address', 'city', type='char', string='City'),
|
||||
'phone': fields.related('address', 'phone', type='char', string='Phone'),
|
||||
'country': fields.related('address', 'country_id', type='many2one', relation='res.country', string='Country'),
|
||||
'employee': fields.boolean('Employee', help="Check this box if the partner is an Employee."),
|
||||
'email': fields.related('address', 'email', type='char', size=240, string='E-mail'),
|
||||
'company_id': fields.many2one('res.company', 'Company', select=1),
|
||||
'company_id': fields.many2one('res.company', 'Company', select=1),
|
||||
}
|
||||
|
||||
def _default_category(self, cr, uid, context={}):
|
||||
@@ -291,7 +276,7 @@ class res_partner_address(osv.osv):
|
||||
_columns = {
|
||||
'partner_id': fields.many2one('res.partner', 'Partner', ondelete='set null', select=True, help="Keep empty for a private address, not related to partner."),
|
||||
'type': fields.selection( [ ('default','Default'),('invoice','Invoice'), ('delivery','Delivery'), ('contact','Contact'), ('other','Other') ],'Address Type', help="Used to select automatically the right address according to the context in sales and purchases documents."),
|
||||
'function': fields.many2one('res.partner.function', 'Function'),
|
||||
'function': fields.char('Function', size=64),
|
||||
'title': fields.selection(_contact_title_get, 'Title', size=32),
|
||||
'name': fields.char('Contact Name', size=64, select=1),
|
||||
'street': fields.char('Street', size=128),
|
||||
@@ -469,7 +454,7 @@ class res_partner_category(osv.osv):
|
||||
_columns = {
|
||||
'partner_ids': fields.many2many('res.partner', 'res_partner_category_rel', 'category_id', 'partner_id', 'Partners'),
|
||||
}
|
||||
|
||||
|
||||
res_partner_category()
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -33,19 +33,6 @@
|
||||
<field name="shortcut">M.</field>
|
||||
</record>
|
||||
|
||||
<record id="function_director" model="res.partner.function">
|
||||
<field name="name">Director</field>
|
||||
<field name="code">CEO</field>
|
||||
</record>
|
||||
<record id="function_it" model="res.partner.function">
|
||||
<field name="name">Chief Technical Officer</field>
|
||||
<field name="code">CTO</field>
|
||||
</record>
|
||||
<record id="function_sale" model="res.partner.function">
|
||||
<field name="name">Salesman</field>
|
||||
<field name="code">SAL</field>
|
||||
</record>
|
||||
|
||||
<!-- Default bank account description -->
|
||||
<record id="bank_normal" model="res.partner.bank.type">
|
||||
<field name="name">Bank account</field>
|
||||
|
||||
@@ -275,6 +275,26 @@
|
||||
<field name="type">default</field>
|
||||
<field name="partner_id" ref="res_partner_agrolait"/>
|
||||
</record>
|
||||
<record id="res_partner_address_8delivery" model="res.partner.address">
|
||||
<field name="city">Wavre</field>
|
||||
<field name="name">Paul Lelitre</field>
|
||||
<field name="zip">5478</field>
|
||||
<field name="title">M.</field>
|
||||
<field model="res.country" name="country_id" search="[('name','=','Belgium')]"/>
|
||||
<field name="street">71 rue de Chimay</field>
|
||||
<field name="type">delivery</field>
|
||||
<field name="partner_id" ref="res_partner_agrolait"/>
|
||||
</record>
|
||||
<record id="res_partner_address_8invoice" model="res.partner.address">
|
||||
<field name="city">Wavre</field>
|
||||
<field name="name">Serge Lelitre</field>
|
||||
<field name="zip">5478</field>
|
||||
<field name="title">M.</field>
|
||||
<field model="res.country" name="country_id" search="[('name','=','Belgium')]"/>
|
||||
<field name="street">69 rue de Chimay</field>
|
||||
<field name="type">invoice</field>
|
||||
<field name="partner_id" ref="res_partner_agrolait"/>
|
||||
</record>
|
||||
<record id="res_partner_address_9" model="res.partner.address">
|
||||
<field name="city">Paris</field>
|
||||
<field name="name">Arthur Grosbonnet</field>
|
||||
@@ -357,7 +377,7 @@
|
||||
<record id="res_partner_asustek_request" model="res.request">
|
||||
<field name="name">ASUSTEK-Subject</field>
|
||||
<field name="act_to" ref="user_demo"/>
|
||||
<field name="ref_doc1">res.partner,2</field>
|
||||
<field name="ref_doc1" ref="res_partner_asus"/>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -9,41 +9,6 @@
|
||||
<menuitem id="menu_base_config_partner" name="Partners" parent="menu_base_config" sequence="10" groups="base.group_extended"/>
|
||||
<menuitem id="menu_base_config_contact" name="Contacts" parent="menu_base_config" sequence="20" />
|
||||
|
||||
<!--
|
||||
================================
|
||||
Function
|
||||
================================
|
||||
-->
|
||||
<record id="view_partner_function_form" model="ir.ui.view">
|
||||
<field name="name">res.partner.function.form</field>
|
||||
<field name="model">res.partner.function</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Contact Functions">
|
||||
<field name="name" select="1"/>
|
||||
<field name="code" select="1"/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_partner_function_tree" model="ir.ui.view">
|
||||
<field name="name">res.partner.function.tree</field>
|
||||
<field name="model">res.partner.function</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Contact Functions">
|
||||
<field name="code"/>
|
||||
<field name="name"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record id="action_partner_function_form" model="ir.actions.act_window">
|
||||
<field name="name">Contact Functions</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">res.partner.function</field>
|
||||
<field name="view_type">form</field>
|
||||
</record>
|
||||
<menuitem action="action_partner_function_form" id="menu_partner_function_form" parent="base.menu_base_config_contact" sequence="20"/>
|
||||
|
||||
<!--
|
||||
=====================
|
||||
Partner Address
|
||||
@@ -347,24 +312,27 @@
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Partner">
|
||||
<group col='10' colspan='4'>
|
||||
<filter string="My Partners" icon="terp-partner" domain="[('user_id','=',uid)]"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter string="Customers" icon="terp-partner" domain="[('customer','=',1)]" help="Customer Partners"/>
|
||||
<filter string="Suppliers" icon="terp-partner" domain="[('supplier','=',1)]" help="Supplier Partners"/>
|
||||
<filter string="Employees" icon="terp-partner" domain="[('employee','=',1)]" help="Employee Partners"/>
|
||||
<separator orientation="vertical"/>
|
||||
<field name="name" select="1"/>
|
||||
<field name="address" select="1"/>
|
||||
<field name="country" select="1"/>
|
||||
<field name="category_id" select="1"/>
|
||||
<field name="user_id" select="1"/>
|
||||
<field name="user_id" select="1">
|
||||
<filter string="My Partners" icon="terp-partner" domain="[('user_id','=',uid)]"/>
|
||||
</field>
|
||||
</group>
|
||||
<newline />
|
||||
<group expand="0" string="Group By..." colspan="16">
|
||||
<filter string="Salesman" icon="terp-crm" domain="[]" context="{'group_by' : 'user_id'}" />
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_partner_form" model="ir.actions.act_window">
|
||||
<field name="name">Partners</field>
|
||||
<field name="name">Customers</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">res.partner</field>
|
||||
<field name="view_type">form</field>
|
||||
@@ -639,7 +607,7 @@
|
||||
/>
|
||||
|
||||
<record id="ir_ui_view_sc_partner0" model="ir.ui.view_sc">
|
||||
<field name="name">Partner</field>
|
||||
<field name="name">Address Book</field>
|
||||
<field name="resource">ir.ui.menu</field>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="res_id" ref="menu_partner_form"/>
|
||||
|
||||
@@ -25,7 +25,6 @@ import os
|
||||
import tools
|
||||
from tools.translate import _
|
||||
|
||||
|
||||
class multi_company_default(osv.osv):
|
||||
"""
|
||||
Manage multi company default value
|
||||
@@ -85,7 +84,8 @@ class res_company(osv.osv):
|
||||
'logo' : fields.binary('Logo'),
|
||||
'currency_id': fields.many2one('res.currency', 'Currency', required=True),
|
||||
'currency_ids': fields.one2many('res.currency', 'company_id', 'Currency'),
|
||||
'user_ids': fields.many2many('res.users', 'res_company_users_rel', 'cid', 'user_id', 'Accepted Users')
|
||||
'user_ids': fields.many2many('res.users', 'res_company_users_rel', 'cid', 'user_id', 'Accepted Users'),
|
||||
'account_no':fields.char('Account No.', size=64),
|
||||
}
|
||||
|
||||
def search(self, cr, uid, args, offset=0, limit=None, order=None,
|
||||
@@ -179,6 +179,12 @@ class res_company(osv.osv):
|
||||
level -= 1
|
||||
return True
|
||||
|
||||
def _get_logo(self, cr, uid, ids):
|
||||
return open(os.path.join(
|
||||
tools.config['root_path'], '..', 'pixmaps', 'openerp-header.png'),
|
||||
'rb') .read().encode('base64')
|
||||
|
||||
|
||||
def _get_header2(self,cr,uid,ids):
|
||||
return """
|
||||
<header>
|
||||
@@ -240,7 +246,8 @@ class res_company(osv.osv):
|
||||
_defaults = {
|
||||
'currency_id': _get_euro,
|
||||
'rml_header':_get_header,
|
||||
'rml_header2': _get_header2
|
||||
'rml_header2': _get_header2,
|
||||
'logo':_get_logo
|
||||
}
|
||||
|
||||
_constraints = [
|
||||
|
||||
@@ -60,11 +60,11 @@ class lang(osv.osv):
|
||||
('name_uniq', 'unique (name)', 'The name of the language must be unique !'),
|
||||
('code_uniq', 'unique (code)', 'The code of the language must be unique !'),
|
||||
]
|
||||
|
||||
|
||||
@tools.cache(skiparg=3)
|
||||
def _lang_data_get(self, cr, uid, lang_id, monetary=False):
|
||||
conv = localeconv()
|
||||
lang_obj=self.browse(cr,uid,lang_id)
|
||||
lang_obj = self.browse(cr, uid, lang_id)
|
||||
thousands_sep = lang_obj.thousands_sep or conv[monetary and 'mon_thousands_sep' or 'thousands_sep']
|
||||
decimal_point = lang_obj.decimal_point
|
||||
grouping = lang_obj.grouping
|
||||
@@ -80,6 +80,7 @@ class lang(osv.osv):
|
||||
|
||||
if not grouping:
|
||||
return (s, 0)
|
||||
|
||||
result = ""
|
||||
seps = 0
|
||||
spaces = ""
|
||||
@@ -129,7 +130,7 @@ class lang(osv.osv):
|
||||
parts = formatted.split('.')
|
||||
|
||||
if grouping:
|
||||
parts[0], seps = self._group(cr,uid,ids,parts[0], monetary=monetary, grouping=lang_grouping, thousands_sep=thousands_sep)
|
||||
parts[0], seps = self._group(cr,uid,ids,parts[0], monetary=monetary, grouping=lang_grouping, thousands_sep=thousands_sep)
|
||||
|
||||
formatted = decimal_point.join(parts)
|
||||
while seps:
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
# -*- 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/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from osv import fields, osv
|
||||
|
||||
class res_log(osv.osv_memory):
|
||||
_name = 'res.log'
|
||||
_columns = {
|
||||
'name': fields.char('Message', size=128, help='The logging message.', required=True),
|
||||
'user_id': fields.many2one('res.users','User', required=True),
|
||||
'res_model': fields.char('Object', size=128),
|
||||
'res_id': fields.integer('Object ID'),
|
||||
'secondary': fields.boolean('Secondary Log', help='Do not display this log if it belongs to the same object the user is working on')
|
||||
}
|
||||
_defaults = {
|
||||
'user_id': lambda self,cr,uid,ctx: uid
|
||||
}
|
||||
_order='id desc'
|
||||
# TODO: do not return secondary log if same object than in the model (but unlink it)
|
||||
def get(self, cr, uid, context={}):
|
||||
ids = self.search(cr, uid, [('user_id','=',uid)], context=context)
|
||||
result = self.read(cr, uid, ids, ['name','res_model','res_id'], context=context)
|
||||
self.unlink(cr, uid, ids, context=context)
|
||||
return result
|
||||
res_log()
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record id="res_log_search" model="ir.ui.view">
|
||||
<field name="name">res.log.search</field>
|
||||
<field name="model">res.log</field>
|
||||
<field name="type">search</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Logs">
|
||||
<filter icon="terp-sale" string="My Logs" domain="[('user_id','=',uid)]" name="my"/>
|
||||
<separator orientation="vertical"/>
|
||||
<field name="name"/>
|
||||
<field name="user_id"/>
|
||||
<field name="res_model"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<record id="res_log_tree" model="ir.ui.view">
|
||||
<field name="name">res.log.tree</field>
|
||||
<field name="model">res.log</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Logs">
|
||||
<field name="name"/>
|
||||
<field name="user_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record id="res_log_form" model="ir.ui.view">
|
||||
<field name="name">res.log.form</field>
|
||||
<field name="model">res.log</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Logs">
|
||||
<group colspan="4" col="6">
|
||||
<field name="name" colspan="4"/>
|
||||
<field name="user_id" />
|
||||
<field name="res_model"/>
|
||||
<field name="res_id"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="res_log_act_window" model="ir.actions.act_window">
|
||||
<field name="name">Logs</field>
|
||||
<field name="res_model">res.log</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="context">{'search_default_my': 1}</field>
|
||||
</record>
|
||||
<menuitem
|
||||
action="res_log_act_window"
|
||||
id="menu_res_log_act_window"
|
||||
parent="base.next_id_4"/>
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -6,9 +6,13 @@
|
||||
<field name="name">Partner Manager</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.menu" id="menu_base_config">
|
||||
<field eval="[(6,0,[ref('group_system'), ref('group_partner_manager')])]" name="groups_id"/>
|
||||
</record>
|
||||
<record model="ir.ui.menu" id="menu_base_partner">
|
||||
<field eval="[(6,0,[ref('group_system'), ref('group_partner_manager')])]" name="groups_id"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.menu" id="menu_base_config">
|
||||
<field eval="[(6,0,[ref('group_system'), ref('group_partner_manager')])]" name="groups_id"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
from osv import fields,osv
|
||||
from osv.orm import except_orm, browse_record
|
||||
import tools
|
||||
import operator
|
||||
import pytz
|
||||
import pooler
|
||||
from tools.translate import _
|
||||
@@ -82,6 +83,11 @@ class groups(osv.osv):
|
||||
default['name'] = default['name'] + _(' (copy)')
|
||||
return super(groups, self).copy(cr, uid, id, default, context=context)
|
||||
|
||||
def get_extended_interface_group(self, cr, uid, context=None):
|
||||
data_obj = self.pool.get('ir.model.data')
|
||||
extended_group_data_id = data_obj._get_id(cr, uid, 'base', 'group_extended')
|
||||
return data_obj.browse(cr, uid, extended_group_data_id, context=context).res_id
|
||||
|
||||
groups()
|
||||
|
||||
class roles(osv.osv):
|
||||
@@ -106,10 +112,11 @@ roles()
|
||||
|
||||
def _lang_get(self, cr, uid, context={}):
|
||||
obj = self.pool.get('res.lang')
|
||||
ids = obj.search(cr, uid, [])
|
||||
ids = obj.search(cr, uid, [('translatable','=',True)])
|
||||
res = obj.read(cr, uid, ids, ['code', 'name'], context)
|
||||
res = [(r['code'], r['name']) for r in res]
|
||||
return res
|
||||
|
||||
def _tz_get(self,cr,uid, context={}):
|
||||
return [(x, x) for x in pytz.all_timezones]
|
||||
|
||||
@@ -166,6 +173,47 @@ class users(osv.osv):
|
||||
body=self.get_welcome_mail_body(
|
||||
cr, uid, context=context) % user)
|
||||
|
||||
def _set_interface_type(self, cr, uid, ids, name, value, arg, context=None):
|
||||
"""Implementation of 'view' function field setter, sets the type of interface of the users.
|
||||
@param name: Name of the field
|
||||
@param arg: User defined argument
|
||||
@param value: new value returned
|
||||
@return: True/False
|
||||
"""
|
||||
if not value or value not in ['simple','extended']:
|
||||
return False
|
||||
group_obj = self.pool.get('res.groups')
|
||||
extended_group_id = group_obj.get_extended_interface_group(cr, uid, context=context)
|
||||
self.write(cr, uid, ids, { 'groups_id': [((3 if value == 'simple' else 4), extended_group_id)]}, context=context)
|
||||
return True
|
||||
|
||||
|
||||
def _get_interface_type(self, cr, uid, ids, name, args, context=None):
|
||||
"""Implementation of 'view' function field getter, returns the type of interface of the users.
|
||||
@param field_name: Name of the field
|
||||
@param arg: User defined argument
|
||||
@return: Dictionary of values
|
||||
"""
|
||||
group_obj = self.pool.get('res.groups')
|
||||
extended_group_id = group_obj.get_extended_interface_group(cr, uid, context=context)
|
||||
extended_users = group_obj.read(cr, uid, extended_group_id, ['users'], context=context)['users']
|
||||
return dict(zip(ids, ['extended' if user in extended_users else 'simple' for user in ids]))
|
||||
|
||||
def _email_get(self, cr, uid, ids, name, arg, context=None):
|
||||
return dict(map(operator.attrgetter('id', 'address_id.email'), self.browse(cr, uid, ids, context=context)))
|
||||
|
||||
def _email_set(self, cr, uid, ids, name, value, arg, context=None):
|
||||
if not isinstance(ids,list):
|
||||
ids = [ids]
|
||||
address_obj = self.pool.get('res.partner.address')
|
||||
for user in self.browse(cr, uid, ids, context=context):
|
||||
if user.address_id:
|
||||
address_obj.write(cr, uid, user.address_id.id, {'email': value or None}, context=context)
|
||||
else:
|
||||
address_id = address_obj.create(cr, uid, {'name': user.name, 'email': value or None}, context=context)
|
||||
self.write(cr, uid, ids, {'address_id': address_id}, context)
|
||||
return True
|
||||
|
||||
_columns = {
|
||||
'name': fields.char('Name', size=64, required=True, select=True,
|
||||
help="The new user's real name, used for searching"
|
||||
@@ -194,7 +242,12 @@ class users(osv.osv):
|
||||
'context_tz': fields.selection(_tz_get, 'Timezone', size=64,
|
||||
help="The user's timezone, used to perform timezone conversions "
|
||||
"between the server and the client."),
|
||||
'view': fields.function(_get_interface_type, method=True, type='selection', fnct_inv=_set_interface_type,
|
||||
selection=[('simple','Simplified'),('extended','Extended')],
|
||||
string='Interface', help="Choose between the simplified interface and the extended one"),
|
||||
'user_email': fields.function(_email_get, method=True, fnct_inv=_email_set, string='Email', type="char"),
|
||||
}
|
||||
|
||||
def read(self,cr, uid, ids, fields=None, context=None, load='_classic_read'):
|
||||
def override_password(o):
|
||||
if 'password' in o and ( 'id' not in o or o['id'] != uid ):
|
||||
@@ -208,6 +261,15 @@ class users(osv.osv):
|
||||
result = override_password(result)
|
||||
else:
|
||||
result = map(override_password, result)
|
||||
|
||||
if isinstance(result, list):
|
||||
for rec in result:
|
||||
if not rec.get('action_id',True):
|
||||
rec['action_id'] = (self._get_menu(cr, uid),'Menu')
|
||||
else:
|
||||
if not result.get('action_id',True):
|
||||
result['action_id'] = (self._get_menu(cr, uid),'Menu')
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@@ -430,6 +492,13 @@ class groups2(osv.osv): ##FIXME: Is there a reason to inherit this object ?
|
||||
_columns = {
|
||||
'users': fields.many2many('res.users', 'res_groups_users_rel', 'gid', 'uid', 'Users'),
|
||||
}
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
for record in self.read(cr, uid, ids, ['users'], context=context):
|
||||
if record['users']:
|
||||
raise osv.except_osv(_('Warning !'), _('Make sure you have no users linked with the group(s)!'))
|
||||
return super(groups2, self).unlink(cr, uid, ids, context=context)
|
||||
|
||||
groups2()
|
||||
|
||||
class res_config_view(osv.osv_memory):
|
||||
@@ -446,15 +515,10 @@ class res_config_view(osv.osv_memory):
|
||||
}
|
||||
|
||||
def execute(self, cr, uid, ids, context=None):
|
||||
res=self.read(cr,uid,ids)[0]
|
||||
users_obj = self.pool.get('res.users')
|
||||
group_obj=self.pool.get('res.groups')
|
||||
if 'view' in res and res['view'] and res['view']=='extended':
|
||||
group_ids=group_obj.search(cr,uid,[('name','ilike','Extended')])
|
||||
if group_ids and len(group_ids):
|
||||
users_obj.write(cr, uid, [uid],{
|
||||
'groups_id':[(4,group_ids[0])]
|
||||
}, context=context)
|
||||
res = self.read(cr, uid, ids)[0]
|
||||
self.pool.get('res.users').write(cr, uid, [uid],
|
||||
{'view':res['view']}, context=context)
|
||||
|
||||
res_config_view()
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"access_ir_module_module_group_user","ir_module_module group_user","model_ir_module_module","group_system",1,1,1,1
|
||||
"access_ir_module_module_dependency_group_system","ir_module_module_dependency group_system","model_ir_module_module_dependency","group_system",1,1,1,1
|
||||
"access_ir_property_group_user","ir_property group_user","model_ir_property",,1,0,0,0
|
||||
"access_ir_property_group_user_manager","ir_property group_manager","model_ir_property","base.group_partner_manager",1,1,1,1
|
||||
"access_ir_property_group_user_manager","ir_property group_manager","model_ir_property","base.group_partner_manager",1,1,1,0
|
||||
"access_ir_report_custom_group_system","ir_report_custom group_system","model_ir_report_custom",,1,0,0,0
|
||||
"access_ir_report_custom_fields_group_system","ir_report_custom_fields group_system","model_ir_report_custom_fields",,1,0,0,0
|
||||
"access_ir_rule_group_user","ir_rule group_user","model_ir_rule",,1,0,0,0
|
||||
@@ -73,8 +73,6 @@
|
||||
"access_res_partner_event_group_partner_manager","res_partner_event group_partner_manager","model_res_partner_event","group_partner_manager",1,1,1,1
|
||||
"access_res_partner_event_type_group_partner_manager","res_partner_event_type group_partner_manager","model_res_partner_event_type","group_partner_manager",1,1,1,1
|
||||
"access_res_partner_event_type_group_user","res_partner_event_type group_user","model_res_partner_event_type","group_user",1,0,0,0
|
||||
"access_res_partner_function_group_user","res_partner_function group_user","model_res_partner_function","group_partner_manager",1,1,1,1
|
||||
"access_res_partner_function_group_partner_manager","res_partner_function group_partner_manager","model_res_partner_function","group_user",1,0,0,0
|
||||
"access_res_partner_som_group_user","res_partner_som group_user","model_res_partner_som","group_partner_manager",1,1,1,1
|
||||
"access_res_partner_som_group_partner_manager","res_partner_som group_partner_manager","model_res_partner_som","group_user",1,0,0,0
|
||||
"access_res_partner_title_group_user","res_partner_title group_user","model_res_partner_title","group_partner_manager",1,1,1,1
|
||||
|
||||
|
+4
-3
@@ -47,11 +47,12 @@
|
||||
<rng:define name="assert">
|
||||
<rng:element name="assert">
|
||||
<rng:attribute name="model" />
|
||||
<rng:optional><rng:attribute name="search" /> </rng:optional>
|
||||
<rng:optional><rng:attribute name="search" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="count" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="string" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="id" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="severity" /></rng:optional>
|
||||
<rng:oneOrMore>
|
||||
<rng:zeroOrMore>
|
||||
<rng:element name="test">
|
||||
<rng:attribute name="expr"/>
|
||||
<rng:choice>
|
||||
@@ -59,7 +60,7 @@
|
||||
<rng:empty />
|
||||
</rng:choice>
|
||||
</rng:element>
|
||||
</rng:oneOrMore>
|
||||
</rng:zeroOrMore>
|
||||
</rng:element>
|
||||
</rng:define>
|
||||
|
||||
|
||||
+19
-1
@@ -24,9 +24,11 @@
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import errno
|
||||
import logging
|
||||
import logging.handlers
|
||||
import os
|
||||
import socket
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
@@ -358,6 +360,21 @@ class Server:
|
||||
res.extend(srv.stats() for srv in cls.__servers)
|
||||
return '\n'.join(res)
|
||||
|
||||
def _close_socket(self):
|
||||
if os.name != 'nt':
|
||||
try:
|
||||
self.socket.shutdown(getattr(socket, 'SHUT_RDWR', 2))
|
||||
except socket.error, e:
|
||||
if e.errno != errno.ENOTCONN: raise
|
||||
# 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
|
||||
self.__logger.debug(
|
||||
'"%s" when shutting down server socket, '
|
||||
'this is normal under OS X', e)
|
||||
self.socket.close()
|
||||
|
||||
class OpenERPDispatcherException(Exception):
|
||||
def __init__(self, exception, traceback):
|
||||
self.exception = exception
|
||||
@@ -365,7 +382,8 @@ class OpenERPDispatcherException(Exception):
|
||||
|
||||
class OpenERPDispatcher:
|
||||
def log(self, title, msg):
|
||||
Logger().notifyChannel('%s' % title, LOG_DEBUG_RPC, pformat(msg))
|
||||
if tools.config['log_level'] == logging.DEBUG_RPC:
|
||||
Logger().notifyChannel('%s' % title, LOG_DEBUG_RPC, pformat(msg))
|
||||
|
||||
def dispatch(self, service_name, method, params):
|
||||
try:
|
||||
|
||||
@@ -280,8 +280,11 @@ class expression(object):
|
||||
c = context.copy()
|
||||
c['active_test'] = False
|
||||
res_ids = field_obj.name_search(cr, uid, right, [], operator, limit=None, context=c)
|
||||
right = map(lambda x: x[0], res_ids)
|
||||
self.__exp[i] = (left, 'in', right)
|
||||
if not res_ids:
|
||||
self.__exp[i] = ('id','=',0)
|
||||
else:
|
||||
right = map(lambda x: x[0], res_ids)
|
||||
self.__exp[i] = (left, 'in', right)
|
||||
else:
|
||||
# other field type
|
||||
# add the time part to datetime field when it's not there:
|
||||
|
||||
+2
-2
@@ -39,7 +39,7 @@ from psycopg2 import Binary
|
||||
import warnings
|
||||
|
||||
import tools
|
||||
|
||||
from tools.translate import _
|
||||
|
||||
def _symbol_set(symb):
|
||||
if symb == None or symb == False:
|
||||
@@ -748,7 +748,7 @@ class related(function):
|
||||
ids=[ids]
|
||||
objlst = obj.browse(cr, uid, ids)
|
||||
for data in objlst:
|
||||
t_id=None
|
||||
t_id = None
|
||||
t_data = data
|
||||
relation = obj._name
|
||||
for i in range(len(self.arg)):
|
||||
|
||||
+135
-62
@@ -49,6 +49,7 @@ import string
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
import datetime
|
||||
import types
|
||||
|
||||
import fields
|
||||
@@ -58,6 +59,7 @@ from tools.translate import _
|
||||
|
||||
import copy
|
||||
import sys
|
||||
import operator
|
||||
|
||||
try:
|
||||
from lxml import etree
|
||||
@@ -86,7 +88,6 @@ def last_day_of_current_month():
|
||||
def intersect(la, lb):
|
||||
return filter(lambda x: x in lb, la)
|
||||
|
||||
|
||||
class except_orm(Exception):
|
||||
def __init__(self, name, value):
|
||||
self.name = name
|
||||
@@ -276,7 +277,7 @@ class browse_record(object):
|
||||
else:
|
||||
new_data[n] = data[n]
|
||||
self._data[data['id']].update(new_data)
|
||||
|
||||
|
||||
if not name in self._data[self._id]:
|
||||
#how did this happen?
|
||||
self.logger.notifyChannel("browse_record", netsvc.LOG_ERROR,
|
||||
@@ -398,8 +399,17 @@ class orm_template(object):
|
||||
_inherits = {}
|
||||
_table = None
|
||||
_invalids = set()
|
||||
_log_create = False
|
||||
|
||||
CONCURRENCY_CHECK_FIELD = '__last_update'
|
||||
def log(self, cr, uid, id, message, secondary=False, context=None):
|
||||
return self.pool.get('res.log').create(cr, uid, {
|
||||
'name': message,
|
||||
'res_model': self._name,
|
||||
'secondary': secondary,
|
||||
'res_id': id},
|
||||
context=context
|
||||
)
|
||||
|
||||
def view_init(self, cr , uid , fields_list, context=None):
|
||||
"""Override this method to do specific things when a view on the object is opened."""
|
||||
@@ -438,7 +448,7 @@ class orm_template(object):
|
||||
'name': k,
|
||||
'field_description': f.string.replace("'", " "),
|
||||
'ttype': f._type,
|
||||
'relation': f._obj or 'NULL',
|
||||
'relation': f._obj or '',
|
||||
'view_load': (f.view_load and 1) or 0,
|
||||
'select_level': tools.ustr(f.select or 0),
|
||||
'readonly':(f.readonly and 1) or 0,
|
||||
@@ -535,6 +545,8 @@ class orm_template(object):
|
||||
return browse_null()
|
||||
|
||||
def __export_row(self, cr, uid, row, fields, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
def check_type(field_type):
|
||||
if field_type == 'float':
|
||||
@@ -588,9 +600,9 @@ class orm_template(object):
|
||||
cols = selection_field(self._inherits)
|
||||
if cols and cols._type == 'selection':
|
||||
sel_list = cols.selection
|
||||
if type(sel_list) == type([]):
|
||||
r = [x[1] for x in sel_list if r==x[0]][0]
|
||||
|
||||
if r and type(sel_list) == type([]):
|
||||
r = [x[1] for x in sel_list if r==x[0]]
|
||||
r = r and r[0] or False
|
||||
if not r:
|
||||
if f[i] in self._columns:
|
||||
r = check_type(self._columns[f[i]]._type)
|
||||
@@ -617,7 +629,7 @@ class orm_template(object):
|
||||
for rr in r :
|
||||
if isinstance(rr.name, browse_record):
|
||||
rr = rr.name
|
||||
rr_name = self.pool.get(rr._table_name).name_get(cr, uid, [rr.id])
|
||||
rr_name = self.pool.get(rr._table_name).name_get(cr, uid, [rr.id], context=context)
|
||||
rr_name = rr_name and rr_name[0] and rr_name[0][1] or ''
|
||||
dt += tools.ustr(rr_name or '') + ','
|
||||
data[fpos] = dt[:-1]
|
||||
@@ -630,7 +642,7 @@ class orm_template(object):
|
||||
i += 1
|
||||
if i == len(f):
|
||||
if isinstance(r, browse_record):
|
||||
r = self.pool.get(r._table_name).name_get(cr, uid, [r.id])
|
||||
r = self.pool.get(r._table_name).name_get(cr, uid, [r.id], context=context)
|
||||
r = r and r[0] and r[0][1] or ''
|
||||
data[fpos] = tools.ustr(r or '')
|
||||
return [data] + lines
|
||||
@@ -786,12 +798,12 @@ class orm_template(object):
|
||||
module, xml_id = line[i].rsplit('.', 1)
|
||||
else:
|
||||
module, xml_id = current_module, line[i]
|
||||
id = ir_model_data_obj._get_id(cr, uid, module, xml_id)
|
||||
|
||||
res_res_id = ir_model_data_obj.read(cr, uid, [id],
|
||||
['res_id'])
|
||||
if res_res_id:
|
||||
res_id = res_res_id[0]['res_id']
|
||||
record_id = ir_model_data_obj._get_id(cr, uid, module, xml_id)
|
||||
ir_model_data = ir_model_data_obj.read(cr, uid, [record_id], ['res_id'])
|
||||
if ir_model_data:
|
||||
res_id = ir_model_data[0]['res_id']
|
||||
else:
|
||||
raise ValueError('No references to %s.%s' % (module, xml_id))
|
||||
row[field[-1][:-3]] = res_id or False
|
||||
continue
|
||||
if (len(field) == len(prefix)+1) and \
|
||||
@@ -985,7 +997,8 @@ class orm_template(object):
|
||||
msg = _('Insertion Failed! ' + e[1])
|
||||
return (-1, res, 'Line ' + str(counter) +' : ' + msg, '' )
|
||||
#Raising Uncaught exception
|
||||
raise
|
||||
return (-1, res, 'Line ' + str(counter) +' : ' + str(e), '' )
|
||||
|
||||
for lang in translate:
|
||||
context2 = context.copy()
|
||||
context2['lang'] = lang
|
||||
@@ -994,6 +1007,8 @@ class orm_template(object):
|
||||
data = pickle.load(file(config.get('import_partial')))
|
||||
data[filename] = initial_size - len(datas) + original_value
|
||||
pickle.dump(data, file(config.get('import_partial'),'wb'))
|
||||
if context.get('defer_parent_store_computation'):
|
||||
self._parent_store_compute(cr)
|
||||
cr.commit()
|
||||
|
||||
#except Exception, e:
|
||||
@@ -1007,6 +1022,8 @@ class orm_template(object):
|
||||
#
|
||||
# TODO: Send a request with the result and multi-thread !
|
||||
#
|
||||
if context.get('defer_parent_store_computation'):
|
||||
self._parent_store_compute(cr)
|
||||
return (done, 0, 0, 0)
|
||||
|
||||
def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'):
|
||||
@@ -1023,7 +1040,14 @@ class orm_template(object):
|
||||
for constraint in self._constraints:
|
||||
fun, msg, fields = constraint
|
||||
if not fun(self, cr, uid, ids):
|
||||
translated_msg = trans._get_source(cr, uid, self._name, 'constraint', lng, source=msg) or msg
|
||||
# Check presence of __call__ directly instead of using
|
||||
# callable() because it will be deprecated as of Python 3.0
|
||||
if hasattr(msg, '__call__'):
|
||||
txt_msg, params = msg(self, cr, uid, ids)
|
||||
tmp_msg = trans._get_source(cr, uid, self._name, 'constraint', lng, source=txt_msg) or txt_msg
|
||||
translated_msg = tmp_msg % params
|
||||
else:
|
||||
translated_msg = trans._get_source(cr, uid, self._name, 'constraint', lng, source=msg) or msg
|
||||
error_msgs.append(
|
||||
_("Error occurred while validating the field(s) %s: %s") % (','.join(fields), translated_msg)
|
||||
)
|
||||
@@ -1092,8 +1116,7 @@ class orm_template(object):
|
||||
if getattr(self._columns[f], arg, None):
|
||||
res[f][arg] = getattr(self._columns[f], arg)
|
||||
|
||||
#TODO: optimize
|
||||
res_trans = translation_obj._get_source(cr, user, self._name + ',' + f, 'field', context.get('lang', False) or 'en_US')
|
||||
res_trans = translation_obj._get_source(cr, user, self._name + ',' + f, 'field', context.get('lang', False) or 'en_US', self._columns[f].string)
|
||||
if res_trans:
|
||||
res[f]['string'] = res_trans
|
||||
help_trans = translation_obj._get_source(cr, user, self._name + ',' + f, 'help', context.get('lang', False) or 'en_US')
|
||||
@@ -1708,7 +1731,8 @@ class orm_template(object):
|
||||
for lang in langs:
|
||||
for field in vals:
|
||||
if field in self._columns:
|
||||
self.pool.get('ir.translation')._set_ids(cr, uid, self._name+','+field, 'field', lang, [0], vals[field])
|
||||
src = self._columns[field].string
|
||||
self.pool.get('ir.translation')._set_ids(cr, uid, self._name+','+field, 'field', lang, [0], vals[field], src)
|
||||
for table in self._inherits:
|
||||
cols = intersect(self._inherit_fields.keys(), vals)
|
||||
if cols:
|
||||
@@ -1824,6 +1848,12 @@ class orm_memory(orm_template):
|
||||
for field in upd_todo:
|
||||
self._columns[field].set_memory(cr, self, id_new, field, vals[field], user, context)
|
||||
self._validate(cr, user, [id_new], context)
|
||||
if self._log_create and not (context and context.get('no_store_function', False)):
|
||||
message = self._description + \
|
||||
" '" + \
|
||||
self.name_get(cr, user, [id_new], context=context)[0][1] + \
|
||||
"' "+ _("created.")
|
||||
self.log(cr, user, id_new, message, True, context=context)
|
||||
wf_service = netsvc.LocalService("workflow")
|
||||
wf_service.trg_create(user, self._name, id_new, cr)
|
||||
return id_new
|
||||
@@ -1911,7 +1941,6 @@ class orm_memory(orm_template):
|
||||
res=e.__dict__['_expression__exp']
|
||||
return res or []
|
||||
|
||||
|
||||
def search(self, cr, user, args, offset=0, limit=None, order=None,
|
||||
context=None, count=False):
|
||||
if not context:
|
||||
@@ -2116,6 +2145,8 @@ class orm(orm_template):
|
||||
return (tables, where_clause)
|
||||
|
||||
def _parent_store_compute(self, cr):
|
||||
if not self._parent_store:
|
||||
return
|
||||
logger = netsvc.Logger()
|
||||
logger.notifyChannel('orm', netsvc.LOG_INFO, 'Computing parent left and right for table %s...' % (self._table, ))
|
||||
def browse_rec(root, pos=0):
|
||||
@@ -2204,8 +2235,8 @@ class orm(orm_template):
|
||||
logger.notifyChannel('orm', netsvc.LOG_ERROR, 'create a column parent_left on object %s: fields.integer(\'Left Parent\', select=1)' % (self._table, ))
|
||||
if 'parent_right' not in self._columns:
|
||||
logger.notifyChannel('orm', netsvc.LOG_ERROR, 'create a column parent_right on object %s: fields.integer(\'Right Parent\', select=1)' % (self._table, ))
|
||||
if self._columns[self._parent_name].ondelete<>'cascade':
|
||||
logger.notifyChannel('orm', netsvc.LOG_ERROR, "the columns %s on object must be set as ondelete='cascasde'" % (self._name, self._parent_name))
|
||||
if self._columns[self._parent_name].ondelete != 'cascade':
|
||||
logger.notifyChannel('orm', netsvc.LOG_ERROR, "The column %s on object %s must be set as ondelete='cascade'" % (self._parent_name, self._name))
|
||||
cr.execute('ALTER TABLE "%s" ADD COLUMN "parent_left" INTEGER' % (self._table,))
|
||||
cr.execute('ALTER TABLE "%s" ADD COLUMN "parent_right" INTEGER' % (self._table,))
|
||||
cr.commit()
|
||||
@@ -2451,6 +2482,8 @@ class orm(orm_template):
|
||||
cr.execute("SELECT relname FROM pg_class WHERE relkind in ('r','v') AND relname=%s", (self._table,))
|
||||
create = not bool(cr.fetchone())
|
||||
|
||||
cr.commit() # start a new transaction
|
||||
|
||||
for (key, con, _) in self._sql_constraints:
|
||||
conname = '%s_%s' % (self._table, key)
|
||||
cr.execute("SELECT conname FROM pg_constraint where conname=%s", (conname,))
|
||||
@@ -2460,6 +2493,7 @@ class orm(orm_template):
|
||||
cr.commit()
|
||||
except:
|
||||
logger.notifyChannel('orm', netsvc.LOG_WARNING, 'unable to add \'%s\' constraint on table %s !\n If you want to have it, you should update the records and execute manually:\nALTER table %s ADD CONSTRAINT %s_%s %s' % (con, self._table, self._table, self._table, key, con,))
|
||||
cr.rollback()
|
||||
|
||||
if create:
|
||||
if hasattr(self, "_sql"):
|
||||
@@ -2470,6 +2504,7 @@ class orm(orm_template):
|
||||
cr.commit()
|
||||
if store_compute:
|
||||
self._parent_store_compute(cr)
|
||||
cr.commit()
|
||||
return todo_end
|
||||
|
||||
def __init__(self, cr):
|
||||
@@ -2625,11 +2660,16 @@ class orm(orm_template):
|
||||
for i in range(len(field_value)):
|
||||
field_value2.append({})
|
||||
for field2 in field_value[i]:
|
||||
if obj._columns[field2]._type in ('many2one', 'one2one'):
|
||||
if field2 in obj._columns.keys() and obj._columns[field2]._type in ('many2one', 'one2one'):
|
||||
obj2 = self.pool.get(obj._columns[field2]._obj)
|
||||
if not obj2.search(cr, uid,
|
||||
[('id', '=', field_value[i][field2])]):
|
||||
continue
|
||||
elif field2 in obj._inherit_fields.keys() and obj._inherit_fields[field2][2]._type in ('many2one', 'one2one'):
|
||||
obj2 = self.pool.get(obj._inherit_fields[field2][2]._obj)
|
||||
if not obj2.search(cr, uid,
|
||||
[('id', '=', field_value[i][field2])]):
|
||||
continue
|
||||
# TODO add test for many2many and one2many
|
||||
field_value2[i][field2] = field_value[i][field2]
|
||||
field_value = field_value2
|
||||
@@ -3086,6 +3126,24 @@ class orm(orm_template):
|
||||
|
||||
self.pool.get('ir.model.access').check(cr, user, self._name, 'write', context=context)
|
||||
|
||||
# No direct update of parent_left/right
|
||||
vals.pop('parent_left', None)
|
||||
vals.pop('parent_right', None)
|
||||
|
||||
parents_changed = []
|
||||
if self._parent_store and (self._parent_name in vals):
|
||||
# The parent_left/right computation may take up to
|
||||
# 5 seconds. No need to recompute the values if the
|
||||
# parent is the same. Get the current value of the parent
|
||||
base_query = 'SELECT id FROM %s WHERE id IN %%s AND %s' % \
|
||||
(self._table, self._parent_name)
|
||||
params = (tuple(ids),)
|
||||
parent_val = vals[self._parent_name]
|
||||
if parent_val:
|
||||
cr.execute(base_query + " != %s", params + (parent_val,))
|
||||
else:
|
||||
cr.execute(base_query + " IS NULL", params)
|
||||
parents_changed = map(operator.itemgetter(0), cr.fetchall())
|
||||
|
||||
upd0 = []
|
||||
upd1 = []
|
||||
@@ -3175,41 +3233,48 @@ class orm(orm_template):
|
||||
self.pool.get(table).write(cr, user, nids, v, context)
|
||||
|
||||
self._validate(cr, user, ids, context)
|
||||
# TODO: use _order to set dest at the right position and not first node of parent
|
||||
if self._parent_store and (self._parent_name in vals):
|
||||
|
||||
# TODO: use _order to set dest at the right position and not first node of parent
|
||||
# We can't defer parent_store computation because the stored function
|
||||
# fields that are computer may refer (directly or indirectly) to
|
||||
# parent_left/right (via a child_of domain)
|
||||
if parents_changed:
|
||||
if self.pool._init:
|
||||
self.pool._init_parent[self._name]=True
|
||||
else:
|
||||
for id in ids:
|
||||
# Find Position of the element
|
||||
if vals[self._parent_name]:
|
||||
cr.execute('select parent_left,parent_right,id from '+self._table+' where '+self._parent_name+'=%s order by '+(self._parent_order or self._order), (vals[self._parent_name],))
|
||||
else:
|
||||
cr.execute('select parent_left,parent_right,id from '+self._table+' where '+self._parent_name+' is null order by '+(self._parent_order or self._order))
|
||||
result_p = cr.fetchall()
|
||||
position = None
|
||||
for (pleft,pright,pid) in result_p:
|
||||
if pid == id:
|
||||
break
|
||||
position = pright+1
|
||||
order = self._parent_order or self._order
|
||||
parent_val = vals[self._parent_name]
|
||||
if parent_val:
|
||||
clause, params = '%s=%%s' % (self._parent_name,), (parent_val,)
|
||||
else:
|
||||
clause, params = '%s IS NULL' % (self._parent_name,), ()
|
||||
cr.execute('SELECT parent_right, id FROM %s WHERE %s ORDER BY %s' % (self._table, clause, order), params)
|
||||
parents = cr.fetchall()
|
||||
|
||||
# It's the first node of the parent: position = parent_left+1
|
||||
if not position:
|
||||
if not vals[self._parent_name]:
|
||||
position = 1
|
||||
else:
|
||||
cr.execute('select parent_left from '+self._table+' where id=%s', (vals[self._parent_name],))
|
||||
position = cr.fetchone()[0]+1
|
||||
|
||||
# We have the new position !
|
||||
cr.execute('select parent_left,parent_right from '+self._table+' where id=%s', (id,))
|
||||
pleft,pright = cr.fetchone()
|
||||
for id in parents_changed:
|
||||
cr.execute('SELECT parent_left, parent_right FROM %s WHERE id=%%s' % (self._table,), (id,))
|
||||
pleft, pright = cr.fetchone()
|
||||
distance = pright - pleft + 1
|
||||
|
||||
if position>pleft and position<=pright:
|
||||
# Find Position of the element
|
||||
position = None
|
||||
for (parent_pright, parent_id) in parents:
|
||||
if parent_id == id:
|
||||
break
|
||||
position = parent_pright+1
|
||||
|
||||
# It's the first node of the parent
|
||||
if not position:
|
||||
if not parent_val:
|
||||
position = 1
|
||||
else:
|
||||
cr.execute('select parent_left from '+self._table+' where id=%s', (parent_val,))
|
||||
position = cr.fetchone()[0]+1
|
||||
|
||||
if pleft < position <= pright:
|
||||
raise except_orm(_('UserError'), _('Recursivity Detected.'))
|
||||
|
||||
if pleft<position:
|
||||
if pleft < position:
|
||||
cr.execute('update '+self._table+' set parent_left=parent_left+%s where parent_left>=%s', (distance, position))
|
||||
cr.execute('update '+self._table+' set parent_right=parent_right+%s where parent_right>=%s', (distance, position))
|
||||
cr.execute('update '+self._table+' set parent_left=parent_left+%s, parent_right=parent_right+%s where parent_left>=%s and parent_left<%s', (position-pleft,position-pleft, pleft, pright))
|
||||
@@ -3390,7 +3455,7 @@ class orm(orm_template):
|
||||
self.check_access_rule(cr, user, [id_new], 'create', context=context)
|
||||
upd_todo.sort(lambda x, y: self._columns[x].priority-self._columns[y].priority)
|
||||
|
||||
if self._parent_store:
|
||||
if self._parent_store and not context.get('defer_parent_store_computation'):
|
||||
if self.pool._init:
|
||||
self.pool._init_parent[self._name]=True
|
||||
else:
|
||||
@@ -3434,6 +3499,12 @@ class orm(orm_template):
|
||||
self.pool.get(object)._store_set_values(cr, user, ids, fields2, context)
|
||||
done.append((object, ids, fields2))
|
||||
|
||||
if self._log_create and not (context and context.get('no_store_function', False)):
|
||||
message = self._description + \
|
||||
" '" + \
|
||||
self.name_get(cr, user, [id_new], context=context)[0][1] + \
|
||||
"' "+ _("created.")
|
||||
self.log(cr, user, id_new, message, True, context=context)
|
||||
wf_service = netsvc.LocalService("workflow")
|
||||
wf_service.trg_create(user, self._name, id_new, cr)
|
||||
return id_new
|
||||
@@ -3612,8 +3683,9 @@ class orm(orm_template):
|
||||
* '&' (default), '|', '!'
|
||||
|
||||
"""
|
||||
if not context:
|
||||
if context is None:
|
||||
context = {}
|
||||
self.pool.get('ir.model.access').check(cr, user, self._name, 'read', context=context)
|
||||
# compute the where, order by, limit and offset clauses
|
||||
(qu1, qu2, tables) = self._where_calc(cr, user, args, context=context)
|
||||
dom = self.pool.get('ir.rule').domain_get(cr, user, self._name, 'read', context=context)
|
||||
@@ -3723,9 +3795,9 @@ class orm(orm_template):
|
||||
:return: dictionary containing all the field values
|
||||
"""
|
||||
|
||||
if not context:
|
||||
if context is None:
|
||||
context = {}
|
||||
if not default:
|
||||
if default is None:
|
||||
default = {}
|
||||
if 'state' not in default:
|
||||
if 'state' in self._defaults:
|
||||
@@ -3734,7 +3806,11 @@ class orm(orm_template):
|
||||
else:
|
||||
default['state'] = self._defaults['state']
|
||||
|
||||
data = self.read(cr, uid, [id], context=context)[0]
|
||||
context_wo_lang = context
|
||||
if 'lang' in context:
|
||||
del context_wo_lang['lang']
|
||||
data = self.read(cr, uid, [id], context=context_wo_lang)[0]
|
||||
|
||||
fields = self.fields_get(cr, uid, context=context)
|
||||
trans_data=[]
|
||||
for f in fields:
|
||||
@@ -3769,17 +3845,14 @@ class orm(orm_template):
|
||||
|
||||
trans_obj = self.pool.get('ir.translation')
|
||||
#TODO: optimize translations
|
||||
trans_name=''
|
||||
for f in fields:
|
||||
trans_flag=True
|
||||
trans_name = ''
|
||||
if f in self._columns and self._columns[f].translate:
|
||||
trans_name=self._name+","+f
|
||||
trans_name = self._name+","+f
|
||||
elif f in self._inherit_fields and self._inherit_fields[f][2].translate:
|
||||
trans_name=self._inherit_fields[f][0]+","+f
|
||||
else:
|
||||
trans_flag=False
|
||||
trans_name = self._inherit_fields[f][0] + "," + f
|
||||
|
||||
if trans_flag:
|
||||
if trans_name:
|
||||
trans_ids = trans_obj.search(cr, uid, [
|
||||
('name', '=', trans_name),
|
||||
('res_id','=',data['id'])
|
||||
|
||||
+19
-3
@@ -30,7 +30,7 @@ import copy
|
||||
import sys
|
||||
import traceback
|
||||
import logging
|
||||
from psycopg2 import IntegrityError
|
||||
from psycopg2 import IntegrityError, errorcodes
|
||||
from tools.func import wraps
|
||||
|
||||
|
||||
@@ -65,7 +65,22 @@ class osv_pool(netsvc.Service):
|
||||
for key in self._sql_error.keys():
|
||||
if key in inst[0]:
|
||||
self.abortResponse(1, 'Constraint Error', 'warning', self._sql_error[key])
|
||||
self.abortResponse(1, 'Integrity Error', 'warning', inst[0])
|
||||
if inst.pgcode == errorcodes.NOT_NULL_VIOLATION:
|
||||
msg = 'Sorry, this record cannot be deleted at the moment because other records still reference it.'
|
||||
self.logger.debug("IntegrityError", exc_info=True)
|
||||
try:
|
||||
context = inst.pgerror.split('"public".')[1]
|
||||
model_name = table = context.split('"')[1]
|
||||
model = table.replace("_",".")
|
||||
model_obj = self.get(model)
|
||||
if model_obj:
|
||||
model_name = model_obj._description or model_obj._name
|
||||
msg += '\n\n[object with reference: %s - %s]' % (model_name, model)
|
||||
except Exception:
|
||||
pass
|
||||
self.abortResponse(1, 'Integrity Error', 'warning', msg)
|
||||
else:
|
||||
self.abortResponse(1, 'Integrity Error', 'warning', inst[0])
|
||||
except Exception, e:
|
||||
self.logger.exception("Uncaught exception")
|
||||
raise
|
||||
@@ -253,7 +268,8 @@ class osv(osv_base, orm.orm):
|
||||
for c in cls.__dict__.get(s, []):
|
||||
exist = False
|
||||
for c2 in range(len(new)):
|
||||
if new[c2][2]==c[2]:
|
||||
#For _constraints, we should check field and methods as well
|
||||
if new[c2][2]==c[2] and new[c2][0]==c[0]:
|
||||
new[c2] = c
|
||||
exist = True
|
||||
break
|
||||
|
||||
+2
-2
@@ -21,8 +21,8 @@
|
||||
##############################################################################
|
||||
|
||||
name = 'openerp-server'
|
||||
version = '5.2dev'
|
||||
major_version = '5.2'
|
||||
version = '6.0dev'
|
||||
major_version = '6.0'
|
||||
description = 'OpenERP Server'
|
||||
long_desc = '''\
|
||||
OpenERP is a complete ERP and CRM. The main features are accounting (analytic
|
||||
|
||||
@@ -36,6 +36,7 @@ from pychart import *
|
||||
import misc
|
||||
import cStringIO
|
||||
from lxml import etree
|
||||
from tools.translate import _
|
||||
|
||||
class external_pdf(render.render):
|
||||
def __init__(self, pdf):
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
@@ -15,7 +15,7 @@
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
@@ -35,7 +35,7 @@ class report(object):
|
||||
for node in root_node:
|
||||
if node.tag == etree.Comment:
|
||||
continue
|
||||
if node.text:
|
||||
if node.text or node.tail:
|
||||
def _sub3(txt):
|
||||
n = node
|
||||
while n.tag != txt.group(2):
|
||||
@@ -71,9 +71,9 @@ class report(object):
|
||||
n = n.getparent()
|
||||
n.set('rml_loop', txt.group(2))
|
||||
return '[['+txt.group(1)+"''"+txt.group(4)+']]'
|
||||
t = _regex1.sub(_sub1, node.text)
|
||||
t = _regex1.sub(_sub1, node.text or node.tail)
|
||||
if t == " ":
|
||||
t = _regex11.sub(_sub1, node.text)
|
||||
t = _regex11.sub(_sub1, node.text or node.tail)
|
||||
t = _regex3.sub(_sub3, t)
|
||||
node.text = _regex2.sub(_sub2, t)
|
||||
self.preprocess_rml(node,type)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
@@ -15,7 +15,7 @@
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
@@ -52,13 +52,20 @@ class _rml_styles(object,):
|
||||
def __init__(self, nodes, localcontext):
|
||||
self.localcontext = localcontext
|
||||
self.styles = {}
|
||||
self.styles_obj = {}
|
||||
self.names = {}
|
||||
self.table_styles = {}
|
||||
self.default_style = reportlab.lib.styles.getSampleStyleSheet()
|
||||
|
||||
for node in nodes:
|
||||
for style in node.findall('blockTableStyle'):
|
||||
self.table_styles[style.get('id')] = self._table_style_get(style)
|
||||
for style in node.findall('paraStyle'):
|
||||
self.styles[style.get('name')] = self._para_style_update(style)
|
||||
sname = style.get('name')
|
||||
self.styles[sname] = self._para_style_update(style)
|
||||
|
||||
self.styles_obj[sname] = reportlab.lib.styles.ParagraphStyle(sname, self.default_style["Normal"], **self.styles[sname])
|
||||
|
||||
for variable in node.findall('initialize'):
|
||||
for name in variable.findall('name'):
|
||||
self.names[ name.get('id')] = name.get('value')
|
||||
@@ -126,17 +133,19 @@ class _rml_styles(object,):
|
||||
|
||||
def para_style_get(self, node):
|
||||
style = False
|
||||
if node.get('style'):
|
||||
if node.get('style') in self.styles:
|
||||
styles = reportlab.lib.styles.getSampleStyleSheet()
|
||||
sname = node.get('style')
|
||||
style = reportlab.lib.styles.ParagraphStyle(sname, styles["Normal"], **self.styles[sname])
|
||||
sname = node.get('style')
|
||||
if sname:
|
||||
if sname in self.styles_obj:
|
||||
style = self.styles_obj[sname]
|
||||
else:
|
||||
sys.stderr.write('Warning: style not found, %s - setting default!\n' % (node.get('style'),) )
|
||||
if not style:
|
||||
styles = reportlab.lib.styles.getSampleStyleSheet()
|
||||
style = styles['Normal']
|
||||
style.__dict__.update(self._para_style_update(node))
|
||||
style = self.default_style['Normal']
|
||||
para_update = self._para_style_update(node)
|
||||
if para_update:
|
||||
# update style only is necessary
|
||||
style = copy.deepcopy(style)
|
||||
style.__dict__.update(para_update)
|
||||
return style
|
||||
|
||||
class _rml_doc(object):
|
||||
@@ -167,7 +176,7 @@ class _rml_doc(object):
|
||||
from reportlab.lib.fonts import addMapping
|
||||
from reportlab.pdfbase import pdfmetrics
|
||||
from reportlab.pdfbase.ttfonts import TTFont
|
||||
|
||||
|
||||
pdfmetrics.registerFont(TTFont(fontname, filename ))
|
||||
if (mode == 'all'):
|
||||
addMapping(face, 0, 0, fontname) #normal
|
||||
@@ -417,11 +426,11 @@ class _rml_canvas(object):
|
||||
self.canvas.drawPath(self.path, **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'}))
|
||||
|
||||
def setFont(self, node):
|
||||
from reportlab.pdfbase import pdfmetrics
|
||||
from reportlab.pdfbase import pdfmetrics
|
||||
fname = node.get('name')
|
||||
#TODO : other fonts should be supported
|
||||
#TODO : other fonts should be supported
|
||||
if fname not in pdfmetrics.standardFonts:
|
||||
fname = self.canvas._fontname
|
||||
fname = self.canvas._fontname
|
||||
return self.canvas.setFont(fname, utils.unit_get(node.get('size')))
|
||||
|
||||
def render(self, node):
|
||||
@@ -565,7 +574,7 @@ class _rml_flowable(object):
|
||||
def _illustration(self, node):
|
||||
class Illustration(platypus.flowables.Flowable):
|
||||
def __init__(self, node, localcontext, styles, self2):
|
||||
self.localcontext = localcontext
|
||||
self.localcontext = localcontext.copy()
|
||||
self.node = node
|
||||
self.styles = styles
|
||||
self.width = utils.unit_get(node.get('width'))
|
||||
|
||||
@@ -441,8 +441,8 @@ class report_sxw(report_rml, preprocess.report):
|
||||
rml_parser.set_context(objs, data, ids, report_xml.report_type)
|
||||
processed_rml = etree.XML(rml)
|
||||
if report_xml.header:
|
||||
rml_parser._add_header(processed_rml)
|
||||
processed_rml = self.preprocess_rml(processed_rml,report_xml.report_type)
|
||||
rml_parser._add_header(processed_rml, self.header)
|
||||
processed_rml = self.preprocess_rml(processed_rml,report_xml.report_type)
|
||||
if rml_parser.logo:
|
||||
logo = base64.decodestring(rml_parser.logo)
|
||||
create_doc = self.generators[report_xml.report_type]
|
||||
|
||||
+12
-16
@@ -31,9 +31,11 @@
|
||||
"""
|
||||
from websrv_lib import *
|
||||
import netsvc
|
||||
import errno
|
||||
import threading
|
||||
import tools
|
||||
import os
|
||||
import select
|
||||
import socket
|
||||
import xmlrpclib
|
||||
|
||||
@@ -128,31 +130,25 @@ class BaseHttpDaemon(threading.Thread, netsvc.Server):
|
||||
"Error occur when starting the server daemon: %s" % (e,))
|
||||
raise
|
||||
|
||||
@property
|
||||
def socket(self):
|
||||
return self.server.socket
|
||||
|
||||
def attach(self, path, gw):
|
||||
pass
|
||||
|
||||
def stop(self):
|
||||
self.running = False
|
||||
if os.name != 'nt':
|
||||
try:
|
||||
self.server.socket.shutdown(
|
||||
getattr(socket, 'SHUT_RDWR', 2))
|
||||
except socket.error, e:
|
||||
if e.errno != 57: raise
|
||||
# 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
|
||||
netsvc.Logger().notifyChannel(
|
||||
'server', netsvc.LOG_DEBUG,
|
||||
'"%s" when shutting down server socket, '
|
||||
'this is normal under OS X'%e)
|
||||
self.server.socket.close()
|
||||
self._close_socket()
|
||||
|
||||
def run(self):
|
||||
self.running = True
|
||||
while self.running:
|
||||
self.server.handle_request()
|
||||
try:
|
||||
self.server.handle_request()
|
||||
except (socket.error, select.error), e:
|
||||
if self.running or e.args[0] != errno.EBADF:
|
||||
raise
|
||||
return True
|
||||
|
||||
class HttpDaemon(BaseHttpDaemon):
|
||||
|
||||
@@ -27,6 +27,7 @@ import netsvc
|
||||
import threading
|
||||
import tools
|
||||
import os
|
||||
import select
|
||||
import socket
|
||||
|
||||
import tiny_socket
|
||||
@@ -50,7 +51,6 @@ class TinySocketClientThread(threading.Thread, netsvc.OpenERPDispatcher):
|
||||
self.sock = None
|
||||
|
||||
def run(self):
|
||||
# import select
|
||||
self.running = True
|
||||
try:
|
||||
ts = tiny_socket.mysocket(self.sock)
|
||||
@@ -103,10 +103,13 @@ class TinySocketServerThread(threading.Thread,netsvc.Server):
|
||||
"starting NET-RPC service at %s port %d" % (interface or '0.0.0.0', port,))
|
||||
|
||||
def run(self):
|
||||
# import select
|
||||
try:
|
||||
self.running = True
|
||||
while self.running:
|
||||
timeout = self.socket.gettimeout()
|
||||
fd_sets = select.select([self.socket], [], [], timeout)
|
||||
if not fd_sets[0]:
|
||||
continue
|
||||
(clientsocket, address) = self.socket.accept()
|
||||
ct = TinySocketClientThread(clientsocket, self.threads)
|
||||
clientsocket = None
|
||||
@@ -129,12 +132,7 @@ class TinySocketServerThread(threading.Thread,netsvc.Server):
|
||||
self.running = False
|
||||
for t in self.threads:
|
||||
t.stop()
|
||||
try:
|
||||
self.socket.shutdown(
|
||||
getattr(socket, 'SHUT_RDWR', 2))
|
||||
self.socket.close()
|
||||
except:
|
||||
return False
|
||||
self._close_socket()
|
||||
|
||||
def stats(self):
|
||||
res = "Net-RPC: " + ( (self.running and "running") or "stopped")
|
||||
|
||||
@@ -283,8 +283,8 @@ class db(netsvc.ExportService):
|
||||
## Not True: in fact, check if connection to database is possible. The database may exists
|
||||
return bool(sql_db.db_connect(db_name))
|
||||
|
||||
def exp_list(self):
|
||||
if not tools.config['list_db']:
|
||||
def exp_list(self, document=False):
|
||||
if not tools.config['list_db'] and not document:
|
||||
raise Exception('AccessDenied')
|
||||
|
||||
db = sql_db.db_connect('template1')
|
||||
|
||||
+40
-22
@@ -100,7 +100,7 @@ class Cursor(object):
|
||||
msg = "Cursor not closed explicitly\n" \
|
||||
"Cursor was created at %s:%s"
|
||||
self.__logger.warn(msg, *self.__caller)
|
||||
self.close()
|
||||
self._close(True)
|
||||
|
||||
@check
|
||||
def execute(self, query, params=None):
|
||||
@@ -118,7 +118,7 @@ class Cursor(object):
|
||||
params = params or None
|
||||
res = self._obj.execute(query, params)
|
||||
except psycopg2.ProgrammingError, pe:
|
||||
self.__logger.error("Programming error: %s, in query %s" % (pe, query))
|
||||
self.__logger.error("Programming error: %s, in query %s", pe, query)
|
||||
raise
|
||||
except Exception:
|
||||
self.__logger.exception("bad query: %s", self._obj.query)
|
||||
@@ -177,6 +177,9 @@ class Cursor(object):
|
||||
|
||||
@check
|
||||
def close(self):
|
||||
return self._close(False)
|
||||
|
||||
def _close(self, leak=False):
|
||||
if not self._obj:
|
||||
return
|
||||
|
||||
@@ -194,7 +197,12 @@ class Cursor(object):
|
||||
# part because browse records keep a reference to the cursor.
|
||||
del self._obj
|
||||
self.__closed = True
|
||||
self._pool.give_back(self._cnx)
|
||||
|
||||
if leak:
|
||||
self._cnx.leaked = True
|
||||
else:
|
||||
keep_in_pool = self.dbname not in ('template1', 'template0', 'postgres')
|
||||
self._pool.give_back(self._cnx, keep_in_pool=keep_in_pool)
|
||||
|
||||
@check
|
||||
def autocommit(self, on):
|
||||
@@ -214,6 +222,9 @@ class Cursor(object):
|
||||
return getattr(self._obj, name)
|
||||
|
||||
|
||||
class PsycoConnection(psycopg2.extensions.connection):
|
||||
pass
|
||||
|
||||
class ConnectionPool(object):
|
||||
|
||||
__logger = logging.getLogger('db.connection_pool')
|
||||
@@ -239,57 +250,64 @@ class ConnectionPool(object):
|
||||
count = len(self._connections)
|
||||
return "ConnectionPool(used=%d/count=%d/max=%d)" % (used, count, self._maxconn)
|
||||
|
||||
def _debug(self, msg):
|
||||
self.__logger.debug(repr(self))
|
||||
self.__logger.debug(msg)
|
||||
def _debug(self, msg, *args):
|
||||
msg = '%r ' + msg
|
||||
self.__logger.debug(msg, self, *args)
|
||||
|
||||
@locked
|
||||
def borrow(self, dsn):
|
||||
self._debug('Borrow connection to %s' % (dsn,))
|
||||
self._debug('Borrow connection to %r', dsn)
|
||||
|
||||
# free leaked connections
|
||||
for i, (cnx, _) in tools.reverse_enumerate(self._connections):
|
||||
if getattr(cnx, 'leaked', False):
|
||||
delattr(cnx, 'leaked')
|
||||
self._connections.pop(i)
|
||||
self._connections.append((cnx, False))
|
||||
self._debug('Free leaked connection to %r', cnx.dsn)
|
||||
|
||||
result = None
|
||||
for i, (cnx, used) in enumerate(self._connections):
|
||||
if not used and dsn_are_equals(cnx.dsn, dsn):
|
||||
self._debug('Existing connection found at index %d' % i)
|
||||
|
||||
self._connections.pop(i)
|
||||
self._connections.append((cnx, True))
|
||||
self._debug('Existing connection found at index %d', i)
|
||||
|
||||
result = cnx
|
||||
break
|
||||
if result:
|
||||
return result
|
||||
return cnx
|
||||
|
||||
if len(self._connections) >= self._maxconn:
|
||||
# try to remove the oldest connection not used
|
||||
for i, (cnx, used) in enumerate(self._connections):
|
||||
if not used:
|
||||
self._debug('Removing old connection at index %d: %s' % (i, cnx.dsn))
|
||||
self._connections.pop(i)
|
||||
self._debug('Removing old connection at index %d: %r', i, cnx.dsn)
|
||||
break
|
||||
else:
|
||||
# note: this code is called only if the for loop has completed (no break)
|
||||
raise PoolError('The Connection Pool Is Full')
|
||||
|
||||
self._debug('Create new connection')
|
||||
result = psycopg2.connect(dsn=dsn)
|
||||
result = psycopg2.connect(dsn=dsn, connection_factory=PsycoConnection)
|
||||
self._connections.append((result, True))
|
||||
self._debug('Create new connection')
|
||||
return result
|
||||
|
||||
@locked
|
||||
def give_back(self, connection):
|
||||
self._debug('Give back connection to %s' % (connection.dsn,))
|
||||
def give_back(self, connection, keep_in_pool=True):
|
||||
self._debug('Give back connection to %r', connection.dsn)
|
||||
for i, (cnx, used) in enumerate(self._connections):
|
||||
if cnx is connection:
|
||||
self._connections.pop(i)
|
||||
self._connections.append((cnx, False))
|
||||
if keep_in_pool:
|
||||
self._connections.append((cnx, False))
|
||||
self._debug('Put connection to %r in pool', cnx.dsn)
|
||||
else:
|
||||
self._debug('Forgot connection to %r', cnx.dsn)
|
||||
break
|
||||
else:
|
||||
raise PoolError('This connection does not below to the pool')
|
||||
|
||||
@locked
|
||||
def close_all(self, dsn):
|
||||
self._debug('Close all connections to %s' % (dsn,))
|
||||
self._debug('Close all connections to %r', dsn)
|
||||
for i, (cnx, used) in tools.reverse_enumerate(self._connections):
|
||||
if dsn_are_equals(cnx.dsn, dsn):
|
||||
cnx.close()
|
||||
@@ -305,7 +323,7 @@ class Connection(object):
|
||||
|
||||
def cursor(self, serialized=False):
|
||||
cursor_type = serialized and 'serialized ' or ''
|
||||
self.__logger.debug('create %scursor to "%s"' % (cursor_type, self.dbname,))
|
||||
self.__logger.debug('create %scursor to %r', cursor_type, self.dbname)
|
||||
return Cursor(self._pool, self.dbname, serialized=serialized)
|
||||
|
||||
def serialized_cursor(self):
|
||||
|
||||
+18
-16
@@ -83,8 +83,7 @@ class configmanager(object):
|
||||
'list_db' : True,
|
||||
'timezone' : False, # to override the default TZ
|
||||
'test-disable' : False,
|
||||
'test-rollback' : True,
|
||||
'test-continue' : False
|
||||
'test-commit' : False,
|
||||
}
|
||||
|
||||
self.misc = {}
|
||||
@@ -124,7 +123,6 @@ class configmanager(object):
|
||||
parser.add_option("--assert-exit-level", dest='assert_exit_level', type="choice", choices=self._LOGLEVELS.keys(),
|
||||
help="specify the level at which a failed assertion will stop the server. Accepted values: %s" % (self._LOGLEVELS.keys(),))
|
||||
parser.add_option('--price_accuracy', dest='price_accuracy', default='2', help='deprecated since v6.0, replaced by module decimal_precision')
|
||||
parser.add_option('--no-database-list', action="store_false", dest='list_db', default=True, help="disable the ability to return the list of databases")
|
||||
|
||||
if self.has_ssl:
|
||||
group = optparse.OptionGroup(parser, "SSL Configuration")
|
||||
@@ -137,17 +135,15 @@ class configmanager(object):
|
||||
default="server.pkey",
|
||||
help="specify the private key file for the SSL connection")
|
||||
parser.add_option_group(group)
|
||||
|
||||
|
||||
# Testing Group
|
||||
group = optparse.OptionGroup(parser, "Testing Configuration")
|
||||
group.add_option("--test-disable", action="store_true", dest="test_disable",
|
||||
default=False, help="Disable loading test files.")
|
||||
group.add_option("--test-no-rollback", action="store_false", dest="test_rollback",
|
||||
default=True, help="Don't rollback after running test.")
|
||||
group.add_option("--test-continue", action="store_true", dest="test_continue",
|
||||
default=False, help="Display exception but then test should continue.")
|
||||
group.add_option("--test-commit", action="store_true", dest="test_commit",
|
||||
default=False, help="Commit database changes performed by tests.")
|
||||
parser.add_option_group(group)
|
||||
|
||||
|
||||
# Logging Group
|
||||
group = optparse.OptionGroup(parser, "Logging Configuration")
|
||||
group.add_option("--logfile", dest="logfile", help="file where the server log will be stored")
|
||||
@@ -164,8 +160,7 @@ class configmanager(object):
|
||||
group.add_option('--email-from', dest='email_from', default='', help='specify the SMTP email address for sending email')
|
||||
group.add_option('--smtp', dest='smtp_server', default='', help='specify the SMTP server for sending email')
|
||||
group.add_option('--smtp-port', dest='smtp_port', default='25', help='specify the SMTP port', type="int")
|
||||
if self.has_ssl:
|
||||
group.add_option('--smtp-ssl', dest='smtp_ssl', default='', help='specify the SMTP server support SSL or not')
|
||||
group.add_option('--smtp-ssl', dest='smtp_ssl', default='', help='specify the SMTP server support SSL or not')
|
||||
group.add_option('--smtp-user', dest='smtp_user', default='', help='specify the SMTP username for sending email')
|
||||
group.add_option('--smtp-password', dest='smtp_password', default='', help='specify the SMTP password for sending email')
|
||||
parser.add_option_group(group)
|
||||
@@ -202,6 +197,13 @@ class configmanager(object):
|
||||
action="callback", callback=self._check_addons_path, nargs=1, type="string")
|
||||
parser.add_option_group(group)
|
||||
|
||||
security = optparse.OptionGroup(parser, 'Security-related options')
|
||||
security.add_option('--no-database-list', action="store_false", dest='list_db', default=True, help="disable the ability to return the list of databases")
|
||||
security.add_option('--enable-code-actions', action='store_true',
|
||||
dest='server_actions_allow_code', default=False,
|
||||
help='Enables server actions of state "code". Warning, this is a security risk.')
|
||||
parser.add_option_group(security)
|
||||
|
||||
def parse_config(self):
|
||||
(opt, args) = self.parser.parse_args()
|
||||
|
||||
@@ -247,13 +249,13 @@ class configmanager(object):
|
||||
self.options['pidfile'] = False
|
||||
|
||||
keys = ['interface', 'port', 'db_name', 'db_user', 'db_password', 'db_host',
|
||||
'db_port', 'list_db', 'logfile', 'pidfile', 'smtp_port', 'cache_timeout',
|
||||
'db_port', 'list_db', 'logfile', 'pidfile', 'smtp_port', 'cache_timeout','smtp_ssl',
|
||||
'email_from', 'smtp_server', 'smtp_user', 'smtp_password', 'price_accuracy',
|
||||
'netinterface', 'netport', 'db_maxconn', 'import_partial', 'addons_path',
|
||||
'netrpc', 'xmlrpc', 'syslog', 'without_demo', 'timezone',]
|
||||
|
||||
if self.has_ssl:
|
||||
keys.extend(['smtp_ssl', 'secure_cert_file', 'secure_pkey_file'])
|
||||
keys.extend(['secure_cert_file', 'secure_pkey_file'])
|
||||
keys.append('secure')
|
||||
|
||||
for arg in keys:
|
||||
@@ -261,7 +263,8 @@ class configmanager(object):
|
||||
self.options[arg] = getattr(opt, arg)
|
||||
|
||||
keys = ['language', 'translate_out', 'translate_in', 'debug_mode',
|
||||
'stop_after_init', 'logrotate', 'without_demo', 'netrpc', 'xmlrpc', 'syslog', 'list_db']
|
||||
'stop_after_init', 'logrotate', 'without_demo', 'netrpc', 'xmlrpc', 'syslog',
|
||||
'list_db', 'server_actions_allow_code']
|
||||
|
||||
if self.has_ssl and not self.options['secure']:
|
||||
keys.append('secure')
|
||||
@@ -288,8 +291,7 @@ class configmanager(object):
|
||||
self.options['init'] = opt.init and dict.fromkeys(opt.init.split(','), 1) or {}
|
||||
self.options["demo"] = not opt.without_demo and self.options['init'] or {}
|
||||
self.options["test-disable"] = opt.test_disable
|
||||
self.options["test-rollback"] = opt.test_rollback
|
||||
self.options["test-continue"] = opt.test_continue
|
||||
self.options["test-commit"] = opt.test_commit
|
||||
self.options['update'] = opt.update and dict.fromkeys(opt.update.split(','), 1) or {}
|
||||
|
||||
self.options['translate_modules'] = opt.translate_modules and map(lambda m: m.strip(), opt.translate_modules.split(',')) or ['all']
|
||||
|
||||
+49
-19
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
@@ -15,7 +15,7 @@
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
@@ -26,11 +26,9 @@ import os.path
|
||||
import pickle
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
|
||||
from datetime import datetime
|
||||
from lxml import etree
|
||||
|
||||
import ir
|
||||
import misc
|
||||
import netsvc
|
||||
@@ -79,6 +77,7 @@ def _eval_xml(self,node, pool, cr, uid, idref, context=None):
|
||||
return f_val
|
||||
a_eval = node.get('eval','')
|
||||
if a_eval:
|
||||
import time
|
||||
idref2 = idref.copy()
|
||||
idref2['time'] = time
|
||||
idref2['DateTime'] = datetime
|
||||
@@ -253,6 +252,14 @@ form: module.record_id""" % (xml_id,)
|
||||
self.pool.get(d_model).unlink(cr, self.uid, ids)
|
||||
self.pool.get('ir.model.data')._unlink(cr, self.uid, d_model, ids)
|
||||
|
||||
def _remove_ir_values(self, cr, name, value, model):
|
||||
ir_value_ids = self.pool.get('ir.values').search(cr, self.uid, [('name','=',name),('value','=',value),('model','=',model)])
|
||||
if ir_value_ids:
|
||||
self.pool.get('ir.values').unlink(cr, self.uid, ir_value_ids)
|
||||
self.pool.get('ir.model.data')._unlink(cr, self.uid, 'ir.values', ir_value_ids)
|
||||
|
||||
return True
|
||||
|
||||
def _tag_report(self, cr, rec, data_node=None):
|
||||
res = {}
|
||||
for dest,f in (('name','string'),('model','model'),('report_name','name')):
|
||||
@@ -262,16 +269,17 @@ form: module.record_id""" % (xml_id,)
|
||||
if rec.get(field):
|
||||
res[dest] = rec.get(field).encode('utf8')
|
||||
if rec.get('auto'):
|
||||
res['auto'] = eval(rec.get('auto'))
|
||||
res['auto'] = eval(rec.get('auto','False'))
|
||||
if rec.get('sxw'):
|
||||
sxw_content = misc.file_open(rec.get('sxw')).read()
|
||||
res['report_sxw_content'] = sxw_content
|
||||
if rec.get('header'):
|
||||
res['header'] = eval(rec.get('header'))
|
||||
res['header'] = eval(rec.get('header','False'))
|
||||
if rec.get('report_type'):
|
||||
res['report_type'] = rec.get('report_type')
|
||||
|
||||
res['multi'] = rec.get('multi') and eval(rec.get('multi'))
|
||||
res['multi'] = rec.get('multi') and eval(rec.get('multi','False'))
|
||||
|
||||
xml_id = rec.get('id','').encode('utf8')
|
||||
self._test_xml_id(xml_id)
|
||||
|
||||
@@ -291,12 +299,16 @@ form: module.record_id""" % (xml_id,)
|
||||
id = self.pool.get('ir.model.data')._update(cr, self.uid, "ir.actions.report.xml", self.module, res, xml_id, noupdate=self.isnoupdate(data_node), mode=self.mode)
|
||||
self.idref[xml_id] = int(id)
|
||||
|
||||
if not rec.get('menu') or eval(rec.get('menu','')):
|
||||
if not rec.get('menu') or eval(rec.get('menu','False')):
|
||||
keyword = str(rec.get('keyword', 'client_print_multi'))
|
||||
keys = [('action',keyword),('res_model',res['model'])]
|
||||
value = 'ir.actions.report.xml,'+str(id)
|
||||
replace = rec.get('replace', True)
|
||||
self.pool.get('ir.model.data').ir_set(cr, self.uid, 'action', keyword, res['name'], [res['model']], value, replace=replace, isobject=True, xml_id=xml_id)
|
||||
elif self.mode=='update' and eval(rec.get('menu','False'))==False:
|
||||
# Special check for report having attribute menu=False on update
|
||||
value = 'ir.actions.report.xml,'+str(id)
|
||||
self._remove_ir_values(cr, res['name'], value, res['model'])
|
||||
return False
|
||||
|
||||
def _tag_function(self, cr, rec, data_node=None):
|
||||
@@ -313,7 +325,7 @@ form: module.record_id""" % (xml_id,)
|
||||
name = rec.get("name",'').encode('utf8')
|
||||
xml_id = rec.get('id','').encode('utf8')
|
||||
self._test_xml_id(xml_id)
|
||||
multi = rec.get('multi','') and eval(rec.get('multi',''))
|
||||
multi = rec.get('multi','') and eval(rec.get('multi','False'))
|
||||
res = {'name': string, 'wiz_name': name, 'multi': multi, 'model': model}
|
||||
|
||||
if rec.get('groups'):
|
||||
@@ -332,12 +344,16 @@ form: module.record_id""" % (xml_id,)
|
||||
id = self.pool.get('ir.model.data')._update(cr, self.uid, "ir.actions.wizard", self.module, res, xml_id, noupdate=self.isnoupdate(data_node), mode=self.mode)
|
||||
self.idref[xml_id] = int(id)
|
||||
# ir_set
|
||||
if (not rec.get('menu') or eval(rec.get('menu',''))) and id:
|
||||
if (not rec.get('menu') or eval(rec.get('menu','False'))) and id:
|
||||
keyword = str(rec.get('keyword','') or 'client_action_multi')
|
||||
keys = [('action',keyword),('res_model',model)]
|
||||
value = 'ir.actions.wizard,'+str(id)
|
||||
replace = rec.get("replace",'') or True
|
||||
self.pool.get('ir.model.data').ir_set(cr, self.uid, 'action', keyword, string, [model], value, replace=replace, isobject=True, xml_id=xml_id)
|
||||
elif self.mode=='update' and (rec.get('menu') and eval(rec.get('menu','False'))==False):
|
||||
# Special check for wizard having attribute menu=False on update
|
||||
value = 'ir.actions.wizard,'+str(id)
|
||||
self._remove_ir_values(cr, string, value, model)
|
||||
|
||||
def _tag_url(self, cr, rec, data_node=None):
|
||||
url = rec.get("string",'').encode('utf8')
|
||||
@@ -351,12 +367,16 @@ form: module.record_id""" % (xml_id,)
|
||||
id = self.pool.get('ir.model.data')._update(cr, self.uid, "ir.actions.url", self.module, res, xml_id, noupdate=self.isnoupdate(data_node), mode=self.mode)
|
||||
self.idref[xml_id] = int(id)
|
||||
# ir_set
|
||||
if (not rec.get('menu') or eval(rec.get('menu',''))) and id:
|
||||
if (not rec.get('menu') or eval(rec.get('menu','False'))) and id:
|
||||
keyword = str(rec.get('keyword','') or 'client_action_multi')
|
||||
keys = [('action',keyword)]
|
||||
value = 'ir.actions.url,'+str(id)
|
||||
replace = rec.get("replace",'') or True
|
||||
self.pool.get('ir.model.data').ir_set(cr, self.uid, 'action', keyword, url, ["ir.actions.url"], value, replace=replace, isobject=True, xml_id=xml_id)
|
||||
elif self.mode=='update' and (rec.get('menu') and eval(rec.get('menu','False'))==False):
|
||||
# Special check for URL having attribute menu=False on update
|
||||
value = 'ir.actions.url,'+str(id)
|
||||
self._remove_ir_values(cr, url, value, "ir.actions.url")
|
||||
|
||||
def _tag_act_window(self, cr, rec, data_node=None):
|
||||
name = rec.get('name','').encode('utf-8')
|
||||
@@ -369,21 +389,22 @@ form: module.record_id""" % (xml_id,)
|
||||
domain = rec.get('domain','').encode('utf-8') or '{}'
|
||||
context = rec.get('context','').encode('utf-8') or '{}'
|
||||
res_model = rec.get('res_model','').encode('utf-8')
|
||||
src_model = rec.get('src_model','').encode('utf-8')
|
||||
src_model = rec.get('src_model','').encode('utf-8')
|
||||
view_type = rec.get('view_type','').encode('utf-8') or 'form'
|
||||
view_mode = rec.get('view_mode','').encode('utf-8') or 'tree,form'
|
||||
|
||||
|
||||
usage = rec.get('usage','').encode('utf-8')
|
||||
limit = rec.get('limit','').encode('utf-8')
|
||||
auto_refresh = rec.get('auto_refresh','').encode('utf-8')
|
||||
uid = self.uid
|
||||
# def ref() added because , if context has ref('id') eval wil use this ref
|
||||
|
||||
active_id=str("active_id") # for further reference in client/bin/tools/__init__.py
|
||||
active_id = str("active_id") # for further reference in client/bin/tools/__init__.py
|
||||
|
||||
def ref(str_id):
|
||||
return self.id_get(cr, None, str_id)
|
||||
context=eval(context)
|
||||
context = eval(context)
|
||||
# domain = eval(domain) # XXX need to test this line -> uid, active_id, active_ids, ...
|
||||
|
||||
res = {
|
||||
'name': name,
|
||||
@@ -720,7 +741,12 @@ form: module.record_id""" % (xml_id,)
|
||||
if f_ref=="null":
|
||||
f_val = False
|
||||
else:
|
||||
f_val = self.id_get(cr, f_model, f_ref)
|
||||
if f_name in model._columns \
|
||||
and model._columns[f_name]._type == 'reference':
|
||||
val = self.model_id_get(cr, f_model, f_ref)
|
||||
f_val = val[0] + ',' + str(val[1])
|
||||
else:
|
||||
f_val = self.id_get(cr, f_model, f_ref)
|
||||
else:
|
||||
f_val = _eval_xml(self,field, self.pool, cr, self.uid, self.idref)
|
||||
if model._columns.has_key(f_name):
|
||||
@@ -738,13 +764,17 @@ form: module.record_id""" % (xml_id,)
|
||||
def id_get(self, cr, model, id_str):
|
||||
if id_str in self.idref:
|
||||
return self.idref[id_str]
|
||||
return self.model_id_get(cr, model, id_str)[1]
|
||||
|
||||
def model_id_get(self, cr, model, id_str):
|
||||
model_data_obj = self.pool.get('ir.model.data')
|
||||
mod = self.module
|
||||
if '.' in id_str:
|
||||
mod,id_str = id_str.split('.')
|
||||
result = self.pool.get('ir.model.data')._get_id(cr, self.uid, mod, id_str)
|
||||
res = self.pool.get('ir.model.data').read(cr, self.uid, [result], ['res_id'])
|
||||
result = model_data_obj._get_id(cr, self.uid, mod, id_str)
|
||||
res = model_data_obj.read(cr, self.uid, [result], ['model', 'res_id'])
|
||||
if res and res[0] and res[0]['res_id']:
|
||||
return int(res[0]['res_id'])
|
||||
return res[0]['model'], int(res[0]['res_id'])
|
||||
return False
|
||||
|
||||
def parse(self, de):
|
||||
|
||||
+30
-18
@@ -101,7 +101,7 @@ def init_db(cr):
|
||||
values (%s, %s, %s, %s, %s, %s, %s, %s, %s)', (
|
||||
id, info.get('author', ''),
|
||||
info.get('website', ''), i, info.get('name', False),
|
||||
info.get('description', ''), p_id, state, info.get('certificate')))
|
||||
info.get('description', ''), p_id, state, info.get('certificate') or None))
|
||||
cr.execute('insert into ir_model_data \
|
||||
(name,model,module, res_id, noupdate) values (%s,%s,%s,%s,%s)', (
|
||||
'module_meta_information', 'ir.module.module', i, id, True))
|
||||
@@ -820,6 +820,22 @@ class cache(object):
|
||||
def to_xml(s):
|
||||
return s.replace('&','&').replace('<','<').replace('>','>')
|
||||
|
||||
def get_encodings():
|
||||
yield 'utf8'
|
||||
from locale import getpreferredencoding
|
||||
prefenc = getpreferredencoding()
|
||||
if prefenc:
|
||||
yield prefenc
|
||||
|
||||
prefenc = {
|
||||
'latin1': 'latin9',
|
||||
'iso-8859-1': 'iso8859-15',
|
||||
'cp1252': '1252',
|
||||
}.get(prefenc.lower())
|
||||
if prefenc:
|
||||
yield prefenc
|
||||
|
||||
|
||||
def ustr(value):
|
||||
"""This method is similar to the builtin `str` method, except
|
||||
it will return Unicode string.
|
||||
@@ -829,29 +845,25 @@ def ustr(value):
|
||||
@rtype: unicode
|
||||
@return: unicode string
|
||||
"""
|
||||
orig = value
|
||||
if isinstance(value, Exception):
|
||||
return exception_to_unicode(value)
|
||||
|
||||
if isinstance(value, unicode):
|
||||
return value
|
||||
|
||||
if hasattr(value, '__unicode__'):
|
||||
try:
|
||||
return unicode(value)
|
||||
|
||||
if not isinstance(value, str):
|
||||
value = str(value)
|
||||
|
||||
try: # first try utf-8
|
||||
return unicode(value, 'utf-8')
|
||||
except:
|
||||
pass
|
||||
|
||||
try: # then extened iso-8858
|
||||
return unicode(value, 'iso-8859-15')
|
||||
except:
|
||||
pass
|
||||
for ln in get_encodings():
|
||||
try:
|
||||
return unicode(value, ln)
|
||||
except:
|
||||
pass
|
||||
raise UnicodeError('unable de to convert %r' % (orig,))
|
||||
|
||||
# else use default system locale
|
||||
from locale import getlocale
|
||||
return unicode(value, getlocale()[1])
|
||||
|
||||
def exception_to_unicode(e):
|
||||
if (sys.version_info[:2] < (2,6)) and hasattr(e, 'message'):
|
||||
@@ -914,7 +926,7 @@ def get_languages():
|
||||
'fr_BE': u'French (BE) / Français (BE)',
|
||||
'fr_CH': u'French (CH) / Français (CH)',
|
||||
'fr_FR': u'French / Français',
|
||||
'gl_ES': u'Galician / Spain',
|
||||
'gl_ES': u'Galician / Galego',
|
||||
'gu_IN': u'Gujarati / India',
|
||||
'hi_IN': u'Hindi / India',
|
||||
'hr_HR': u'Croatian / hrvatski jezik',
|
||||
@@ -939,8 +951,8 @@ def get_languages():
|
||||
'ro_RO': u'Romanian / limba română',
|
||||
'ru_RU': u'Russian / русский язык',
|
||||
'si_LK': u'Sinhalese / Sri Lanka',
|
||||
'sk_SK': u'Slovak / Slovakia',
|
||||
'sl_SL': u'Slovenian / slovenščina',
|
||||
'sl_SI': u'Slovenian / slovenščina',
|
||||
'sk_SK': u'Slovak / Slovenský jazyk',
|
||||
'sq_AL': u'Albanian / Shqipëri',
|
||||
'sr_RS': u'Serbian / Serbia',
|
||||
'sv_SE': u'Swedish / svenska',
|
||||
|
||||
+35
-22
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
@@ -15,7 +15,7 @@
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
@@ -596,23 +596,30 @@ def trans_generate(lang, modules, dbname=None):
|
||||
push_translation(module, 'model', name, xml_name, encode(trad))
|
||||
|
||||
# parse source code for _() calls
|
||||
def get_module_from_path(path,mod_paths=None):
|
||||
if not mod_paths:
|
||||
# First, construct a list of possible paths
|
||||
def_path = os.path.abspath(os.path.join(tools.config['root_path'], 'addons')) # default addons path (base)
|
||||
ad_paths= map(lambda m: os.path.abspath(m.strip()),tools.config['addons_path'].split(','))
|
||||
mod_paths=[def_path]
|
||||
for adp in ad_paths:
|
||||
mod_paths.append(adp)
|
||||
if not adp.startswith('/'):
|
||||
mod_paths.append(os.path.join(def_path,adp))
|
||||
elif adp.startswith(def_path):
|
||||
mod_paths.append(adp[len(def_path)+1:])
|
||||
|
||||
for mp in mod_paths:
|
||||
if path.startswith(mp) and (os.path.dirname(path) != mp):
|
||||
path = path[len(mp)+1:]
|
||||
return path.split(os.path.sep)[0]
|
||||
def get_module_from_path(path, mod_paths=None):
|
||||
# if not mod_paths:
|
||||
## First, construct a list of possible paths
|
||||
# def_path = os.path.abspath(os.path.join(tools.config['root_path'], 'addons')) # default addons path (base)
|
||||
# ad_paths= map(lambda m: os.path.abspath(m.strip()),tools.config['addons_path'].split(','))
|
||||
# mod_paths=[def_path]
|
||||
# for adp in ad_paths:
|
||||
# mod_paths.append(adp)
|
||||
# if not adp.startswith('/'):
|
||||
# mod_paths.append(os.path.join(def_path,adp))
|
||||
# elif adp.startswith(def_path):
|
||||
# mod_paths.append(adp[len(def_path)+1:])
|
||||
# for mp in mod_paths:
|
||||
# if path.startswith(mp) and (os.path.dirname(path) != mp):
|
||||
# path = path[len(mp)+1:]
|
||||
# return path.split(os.path.sep)[0]
|
||||
path_dir = os.path.dirname(path[1:])
|
||||
if path_dir:
|
||||
if os.path.exists(os.path.join(tools.config['addons_path'],path[1:])):
|
||||
return path.split(os.path.sep)[1]
|
||||
else:
|
||||
root_addons = os.path.join(tools.config['root_path'], 'addons')
|
||||
if os.path.exists(os.path.join(root_addons,path[1:])):
|
||||
return path.split(os.path.sep)[1]
|
||||
return 'base' # files that are not in a module are considered as being in 'base' module
|
||||
|
||||
modobj = pool.get('ir.module.module')
|
||||
@@ -699,6 +706,11 @@ def trans_load_data(db_name, fileobj, fileformat, lang, strict=False, lang_name=
|
||||
if not lang_name:
|
||||
lang_name = tools.get_languages().get(lang, lang)
|
||||
|
||||
def fix_xa0(s):
|
||||
if s == '\xa0':
|
||||
return '\xc2\xa0'
|
||||
return s
|
||||
|
||||
lang_info = {
|
||||
'code': lang,
|
||||
'iso_code': iso_lang,
|
||||
@@ -706,9 +718,10 @@ def trans_load_data(db_name, fileobj, fileformat, lang, strict=False, lang_name=
|
||||
'translatable': 1,
|
||||
'date_format' : str(locale.nl_langinfo(locale.D_FMT).replace('%y', '%Y')),
|
||||
'time_format' : str(locale.nl_langinfo(locale.T_FMT)),
|
||||
'decimal_point' : str(locale.localeconv()['decimal_point']).replace('\xa0', '\xc2\xa0'),
|
||||
'thousands_sep' : str(locale.localeconv()['thousands_sep']).replace('\xa0', '\xc2\xa0'),
|
||||
'decimal_point' : fix_xa0(str(locale.localeconv()['decimal_point'])),
|
||||
'thousands_sep' : fix_xa0(str(locale.localeconv()['thousands_sep'])),
|
||||
}
|
||||
|
||||
try:
|
||||
lang_obj.create(cr, uid, lang_info)
|
||||
finally:
|
||||
@@ -767,7 +780,7 @@ def trans_load_data(db_name, fileobj, fileformat, lang, strict=False, lang_name=
|
||||
# the same source
|
||||
obj = pool.get(model)
|
||||
if obj:
|
||||
if not field in obj._columns:
|
||||
if field not in obj.fields_get_keys(cr, uid):
|
||||
continue
|
||||
ids = obj.search(cr, uid, [(field, '=', dic['src'])])
|
||||
|
||||
|
||||
@@ -682,7 +682,7 @@ class YamlInterpreter(object):
|
||||
self.logger.exception(e)
|
||||
except Exception, e:
|
||||
self.logger.exception(e)
|
||||
raise e
|
||||
raise
|
||||
|
||||
def _process_node(self, node):
|
||||
if is_comment(node):
|
||||
|
||||
Vendored
+40
@@ -0,0 +1,40 @@
|
||||
# Copyright (C) 2009 Deng Xiyue <manphiz-guest@users.alioth.debian.org>
|
||||
# This file is distributed under the same license as the openerp-server package.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: openerp-server\n"
|
||||
"Report-Msgid-Bugs-To: openerp-server@packages.debian.org\n"
|
||||
"POT-Creation-Date: 2009-08-24 22:41+0300\n"
|
||||
"PO-Revision-Date: 2010-05-21 04:19+0000\n"
|
||||
"Last-Translator: OpenERP Administrators <Unknown>\n"
|
||||
"Language-Team: 桃園小白 <hasrhgni@msn.com>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2010-05-22 03:59+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
"X-Poedit-Country: TAIWAN\n"
|
||||
"X-Poedit-Language: Chinese\n"
|
||||
"X-Poedit-SourceCharset: utf-8\n"
|
||||
|
||||
#. Type: string
|
||||
#. Description
|
||||
#: ../openerp-server.templates:1001
|
||||
msgid "Dedicated system account for the Open ERP server:"
|
||||
msgstr "Open ERP 專屬系統管理員帳號:"
|
||||
|
||||
#. Type: string
|
||||
#. Description
|
||||
#: ../openerp-server.templates:1001
|
||||
msgid ""
|
||||
"The Open ERP server must use a dedicated account for its operation so that "
|
||||
"the system's security is not compromised by running it with superuser "
|
||||
"privileges."
|
||||
msgstr "Open ERP 必須指定一個專屬系統管理員帳號來進行操作,如此才不會因最高權限管理者在運行時損毀了系統的安全性。"
|
||||
|
||||
#. Type: string
|
||||
#. Description
|
||||
#: ../openerp-server.templates:1001
|
||||
msgid "Please choose that account's username."
|
||||
msgstr "請選擇帳號的用戶名稱。"
|
||||
+931
-177
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,10 @@
|
||||
2010-07-XX: 6.0.0
|
||||
=================
|
||||
|
||||
Improvements (server)
|
||||
---------------------
|
||||
|
||||
* support of 'ref' attribute for importing 'reference' field values, as for many2one fields.
|
||||
* experimental xml2yml script in /tools for conversion of XML data/test files to the new YAML format
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ formats=rpm
|
||||
|
||||
[bdist_rpm]
|
||||
# release must exactly match 'release' as set in bin/release.py
|
||||
release=3
|
||||
release=10
|
||||
|
||||
requires=python >= 2.4
|
||||
#build-requires=python-devel >= 2.3
|
||||
|
||||
@@ -44,7 +44,7 @@ sys.path.append(join(os.path.abspath(os.path.dirname(__file__)), "bin"))
|
||||
|
||||
execfile(join('bin', 'release.py'))
|
||||
|
||||
if sys.argv[1] == 'bdist_rpm':
|
||||
if 'bdist_rpm' in sys.argv:
|
||||
version = version.split('-')[0]
|
||||
|
||||
# get python short version
|
||||
@@ -60,6 +60,7 @@ required_modules = [
|
||||
('pytz', 'Timezone handling library for Python'),
|
||||
('reportlab', 'reportlab module'),
|
||||
('yaml', 'YAML parser and emitter for Python'),
|
||||
('pywebdav', 'PyWebDAV is a standards compliant WebDAV server and library written in Python'),
|
||||
]
|
||||
|
||||
def check_modules():
|
||||
@@ -176,11 +177,14 @@ options = {
|
||||
"compressed": 1,
|
||||
"optimize": 2,
|
||||
"dist_dir": 'dist',
|
||||
"packages": ["lxml", "lxml.builder", "lxml._elementpath", "lxml.etree",
|
||||
"lxml.objectify", "decimal", "xml", "encodings",
|
||||
"dateutil", "wizard", "pychart", "PIL", "pyparsing",
|
||||
"pydot", "asyncore","asynchat", "reportlab", "vobject",
|
||||
"HTMLParser", "select", "yaml"],
|
||||
"packages": [
|
||||
"lxml", "lxml.builder", "lxml._elementpath", "lxml.etree",
|
||||
"lxml.objectify", "decimal", "xml", "xml", "xml.dom", "xml.xpath",
|
||||
"encodings", "dateutil", "wizard", "pychart", "PIL", "pyparsing",
|
||||
"pydot", "asyncore","asynchat", "reportlab", "vobject",
|
||||
"HTMLParser", "select", "mako", "poplib",
|
||||
"imaplib", "smtplib", "email", "yaml","pywebdav",
|
||||
],
|
||||
"excludes" : ["Tkconstants","Tkinter","tcl"],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
# TODO handle the case of zip modules
|
||||
|
||||
import os
|
||||
import optparse
|
||||
import sys
|
||||
import glob
|
||||
|
||||
@@ -37,29 +38,48 @@ def load_information_from_description_file(module):
|
||||
|
||||
return {}
|
||||
|
||||
if len(sys.argv) == 2 and (sys.argv[1] in ['-h', '--help']):
|
||||
print >>sys.stderr, 'Usage: module_graph.py [module1 module2 module3]\n\tWhen no module is specified, all modules in current directory are used'
|
||||
sys.exit(1)
|
||||
def get_valid_path(paths, module):
|
||||
for path in paths:
|
||||
full = os.path.join(path, module)
|
||||
if os.path.exists(full):
|
||||
return full
|
||||
return None
|
||||
|
||||
modules = sys.argv[1:]
|
||||
if not len(modules):
|
||||
modules = map(os.path.dirname, glob.glob(os.path.join('*', '__openerp__.py')))
|
||||
modules += map(os.path.dirname, glob.glob(os.path.join('*', '__terp__.py')))
|
||||
parser = optparse.OptionParser(usage="%prog [options] [module1 [module2 ...]]")
|
||||
parser.add_option("-p", "--addons-path", dest="path", help="addons directory", action="append")
|
||||
(opt, args) = parser.parse_args()
|
||||
|
||||
done = []
|
||||
modules = []
|
||||
if not opt.path:
|
||||
opt.path = ["."]
|
||||
|
||||
if not args:
|
||||
for path in opt.path:
|
||||
modules += map(os.path.dirname, glob.glob(os.path.join(path, '*', '__openerp__.py')))
|
||||
modules += map(os.path.dirname, glob.glob(os.path.join(path, '*', '__terp__.py')))
|
||||
else:
|
||||
for module in args:
|
||||
valid_path = get_valid_path(opt.path, module)
|
||||
if valid_path:
|
||||
modules.append(valid_path)
|
||||
|
||||
all_modules = set(map(os.path.basename, modules))
|
||||
print 'digraph G {'
|
||||
while len(modules):
|
||||
f = modules.pop(0)
|
||||
done.append(f)
|
||||
module_name = os.path.basename(f)
|
||||
all_modules.add(module_name)
|
||||
info = load_information_from_description_file(f)
|
||||
if info.get('installable', True):
|
||||
for name in info['depends']:
|
||||
if name not in done+modules:
|
||||
modules.append(name)
|
||||
if not os.path.exists(name):
|
||||
print '\t%s [color=red]' % (name,)
|
||||
print '\t%s -> %s;' % (f, name)
|
||||
for name in info.get('depends',[]):
|
||||
valid_path = get_valid_path(opt.path, name)
|
||||
if name not in all_modules:
|
||||
if valid_path:
|
||||
modules.append(valid_path)
|
||||
else:
|
||||
all_modules.add(name)
|
||||
print '\t%s [color=red]' % (name,)
|
||||
print '\t%s -> %s;' % (module_name, name)
|
||||
print '}'
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
Executable
+345
@@ -0,0 +1,345 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2010 OpenERP SA (<http://openerp.com>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# 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/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
"""
|
||||
Experimental script for conversion between OpenERP's XML serialization format
|
||||
and the new YAML serialization format introduced in OpenERP 6.0.
|
||||
Intended to be used as a quick preprocessor for converting data/test files, then
|
||||
to be fine-tuned manually.
|
||||
"""
|
||||
|
||||
import yaml
|
||||
import logging
|
||||
from lxml import etree
|
||||
|
||||
__VERSION__ = '0.0.2'
|
||||
|
||||
def toString(value):
|
||||
value='"' + value + '"'
|
||||
return value
|
||||
|
||||
class XmlTag(etree.ElementBase):
|
||||
def _to_yaml(self):
|
||||
child_tags = []
|
||||
for node in self:
|
||||
if hasattr(node, '_to_yaml'):
|
||||
child_tags.append(node._to_yaml())
|
||||
return self.tag(attrib=self.attrib, child_tags=child_tags)
|
||||
|
||||
class YamlTag(object):
|
||||
"""
|
||||
Superclass for constructors of custom tags defined in yaml file.
|
||||
"""
|
||||
def __init__(self, **kwargs):
|
||||
self.__dict__.update(kwargs)
|
||||
self.attrib = self.__dict__.get('attrib', {})
|
||||
self.child_tags = self.__dict__.get('child_tags', '')
|
||||
def __getitem__(self, key):
|
||||
return getattr(self, key)
|
||||
def __getattr__(self, attr):
|
||||
return None
|
||||
def __repr__(self):
|
||||
for k,v in self.attrib.iteritems():
|
||||
if str(v) and str(v)[0] in ['[', '{', '#', '*', '(']:
|
||||
self.attrib[k] = toString(self.attrib[k]).replace("'", '')
|
||||
st = self.yaml_tag + ' ' + str(self.attrib)
|
||||
return st
|
||||
|
||||
# attrib tags
|
||||
class ref(YamlTag):
|
||||
yaml_tag = u'!ref'
|
||||
def __init__(self, expr="False"):
|
||||
self.expr = expr
|
||||
def __repr__(self):
|
||||
return "'%s'"%str(self.expr)
|
||||
|
||||
class Eval(YamlTag):
|
||||
yaml_tag = u'!eval'
|
||||
def __init__(self, expr="False"):
|
||||
self.expr = expr
|
||||
def __repr__(self):
|
||||
value=str(self.expr)
|
||||
if value.find("6,") != -1:
|
||||
value = eval(str(eval(value)))
|
||||
value=value[0][2]
|
||||
value = [[value]]
|
||||
else:
|
||||
try:
|
||||
value=int(value)
|
||||
except:
|
||||
if value and value[0] in ['[', '{', '#', '*', '(']:
|
||||
value = value.replace('"', r'\"')
|
||||
value = toString(value)
|
||||
else:
|
||||
try:
|
||||
value = eval(value)
|
||||
except Exception:
|
||||
pass
|
||||
return value
|
||||
|
||||
class Search(YamlTag):
|
||||
yaml_tag = u'!ref'
|
||||
|
||||
# test tag
|
||||
class xml_test(XmlTag):
|
||||
def _to_yaml(self):
|
||||
expr = self.attrib.get('expr')
|
||||
text = self.text
|
||||
if text:
|
||||
expr = expr + ' == ' + '"%s"'%text
|
||||
return [[expr]]
|
||||
|
||||
class xml_data(etree.ElementBase):
|
||||
def _to_yaml(self):
|
||||
value = self.attrib.get('noupdate', "0")
|
||||
return data(value)
|
||||
|
||||
# field tag:
|
||||
class xml_field(etree.ElementBase):
|
||||
def _to_yaml(self):
|
||||
field = ' ' + self.attrib.pop('name','unknown')
|
||||
|
||||
if self.attrib.get('search', None):
|
||||
value = Search(attrib=self.attrib, child_tags='').__repr__()
|
||||
else:
|
||||
attr = (self.attrib.get('ref', None) and 'ref') or (self.attrib.get('eval', None) and 'eval') or 'None'
|
||||
value = Eval(self.attrib.get(attr, self.text)).__repr__() or ''
|
||||
return {field: value}
|
||||
|
||||
# value tag
|
||||
class xml_value(etree.ElementBase):
|
||||
def _to_yaml(self):
|
||||
|
||||
if self.attrib.get('eval', None):
|
||||
key, val = 'eval', '"'+self.attrib.get('eval')+'"'
|
||||
elif self.attrib.get('model', None):
|
||||
key, val = 'model', self.attrib.get('model')
|
||||
val=val.replace("'",'""')
|
||||
self.attrib.pop(key)
|
||||
d={}
|
||||
for k,v in self.attrib.iteritems():
|
||||
if k == 'search':
|
||||
v = '"' + v + '"'
|
||||
k='--' + k
|
||||
v=v.replace("'",'""')
|
||||
d[k] = v
|
||||
if d:
|
||||
ls=[[{key:val},dict(d)]]
|
||||
else:
|
||||
ls=[[{key:val}]]
|
||||
return ls
|
||||
|
||||
# data tag
|
||||
class data(YamlTag):
|
||||
yaml_tag = u'!context'
|
||||
def __init__(self, noupdate="0"):
|
||||
self.child_tags = {' noupdate':noupdate}
|
||||
def __repr__(self):
|
||||
return "!!context"
|
||||
|
||||
# Record tag
|
||||
class Record(YamlTag):
|
||||
yaml_tag = u'!record'
|
||||
class xml_record(XmlTag):
|
||||
tag=Record
|
||||
def _to_yaml(self):
|
||||
child_tags = {}
|
||||
for node in self:
|
||||
if hasattr(node, '_to_yaml'):
|
||||
child_tags.update(node._to_yaml())
|
||||
return Record(attrib=self.attrib, child_tags=child_tags)
|
||||
|
||||
# ir_set tag
|
||||
class Ir_Set(YamlTag):
|
||||
yaml_tag = u'!ir_set'
|
||||
def __repr__(self):
|
||||
st = self.yaml_tag
|
||||
return st
|
||||
class xml_ir_set(XmlTag):
|
||||
tag=Ir_Set
|
||||
def _to_yaml(self):
|
||||
child_tags = {}
|
||||
for node in self:
|
||||
if hasattr(node, '_to_yaml'):
|
||||
child_tags.update(node._to_yaml())
|
||||
return Ir_Set(attrib=self.attrib, child_tags=child_tags)
|
||||
|
||||
# workflow tag
|
||||
class Workflow(YamlTag):
|
||||
yaml_tag = u'!workflow'
|
||||
class xml_workflow(XmlTag):
|
||||
tag=Workflow
|
||||
|
||||
# function tag
|
||||
class Function(YamlTag):
|
||||
yaml_tag = u'!function'
|
||||
class xml_function(XmlTag):
|
||||
tag=Function
|
||||
|
||||
# function tag
|
||||
class Assert(YamlTag):
|
||||
yaml_tag = u'!assert'
|
||||
class xml_assert(XmlTag):
|
||||
tag=Assert
|
||||
|
||||
# menuitem tagresult.append(yaml.safe_dump(obj, default_flow_style=False, allow_unicode=True).replace("'",''))
|
||||
class MenuItem(YamlTag):
|
||||
yaml_tag = u'!menuitem'
|
||||
class xml_menuitem(XmlTag):
|
||||
tag=MenuItem
|
||||
|
||||
# act_window tag
|
||||
class ActWindow(YamlTag):
|
||||
yaml_tag = u'!act_window'
|
||||
class xml_act_window(XmlTag):
|
||||
tag=ActWindow
|
||||
|
||||
# report tag
|
||||
class Report(YamlTag):
|
||||
yaml_tag = u'!report'
|
||||
class xml_report(XmlTag):
|
||||
tag=Report
|
||||
|
||||
# deletes tag
|
||||
class Delete(YamlTag):
|
||||
yaml_tag = u'!delete'
|
||||
class xml_delete(XmlTag):
|
||||
tag=Delete
|
||||
|
||||
# python tag
|
||||
class Python(YamlTag):
|
||||
yaml_tag = u'!python'
|
||||
class xml_python(XmlTag):
|
||||
tag=Python
|
||||
|
||||
# context tag
|
||||
class Context(YamlTag):
|
||||
yaml_tag = u'!context'
|
||||
class xml_context(XmlTag):
|
||||
tag=Context
|
||||
|
||||
# url tag
|
||||
class Url(YamlTag):
|
||||
yaml_tag = u'!url'
|
||||
class xml_url(XmlTag):
|
||||
tag=Url
|
||||
|
||||
# delete tag
|
||||
class Delete(YamlTag):
|
||||
yaml_tag = u'!delete'
|
||||
class xml_delete(XmlTag):
|
||||
tag=Delete
|
||||
|
||||
def represent_data(dumper, data):
|
||||
return dumper.represent_mapping(u'tag:yaml.org,2002:map', [('!'+str(data), data.child_tags)])
|
||||
|
||||
yaml.SafeDumper.add_representer(Record, represent_data)
|
||||
yaml.SafeDumper.add_representer(data, represent_data)
|
||||
yaml.SafeDumper.add_representer(Workflow, represent_data)
|
||||
yaml.SafeDumper.add_representer(Function, represent_data)
|
||||
yaml.SafeDumper.add_representer(Assert, represent_data)
|
||||
yaml.SafeDumper.add_representer(MenuItem, represent_data)
|
||||
yaml.SafeDumper.add_representer(Ir_Set, represent_data)
|
||||
yaml.SafeDumper.add_representer(Python, represent_data)
|
||||
yaml.SafeDumper.add_representer(Context, represent_data)
|
||||
|
||||
class MyLookup(etree.CustomElementClassLookup):
|
||||
def lookup(self, node_type, document, namespace, name):
|
||||
if node_type=='element':
|
||||
return {
|
||||
'data': xml_data,
|
||||
'record': xml_record,
|
||||
'field': xml_field,
|
||||
'workflow': xml_workflow,
|
||||
'function': xml_function,
|
||||
'value': xml_value,
|
||||
'assert': xml_assert,
|
||||
'test': xml_test,
|
||||
'menuitem': xml_menuitem,
|
||||
'act_window': xml_act_window,
|
||||
'report': xml_report,
|
||||
'delete': xml_delete,
|
||||
'python': xml_python,
|
||||
'context': xml_context,
|
||||
'url': xml_url,
|
||||
'ir_set': xml_ir_set,
|
||||
}.get(name, None)
|
||||
elif node_type=='comment':
|
||||
return None#xml_comment
|
||||
return None
|
||||
|
||||
class xml_parse(object):
|
||||
def __init__(self):
|
||||
self.context = {}
|
||||
def parse(self, fname):
|
||||
parser = etree.XMLParser()
|
||||
parser.setElementClassLookup(MyLookup())
|
||||
result = []
|
||||
self.root = etree.XML(file(fname).read(), parser)
|
||||
for data in self.root:
|
||||
if hasattr(data, '_to_yaml'):
|
||||
obj = data._to_yaml()
|
||||
if obj.yaml_tag == '!context':
|
||||
result.append(yaml.dump(str(obj)).replace("'",'').split('\n')[0])
|
||||
result.append(yaml.dump(obj.child_tags, default_flow_style=False).replace("'",''))
|
||||
else:
|
||||
result.append(yaml.safe_dump(obj, default_flow_style=False, allow_unicode=True).replace("'",''))
|
||||
self.context.update(data.attrib)
|
||||
for tag in data:
|
||||
if tag.tag == etree.Comment:
|
||||
result.append(tag)
|
||||
else:
|
||||
if hasattr(tag, '_to_yaml'):
|
||||
obj = tag._to_yaml()
|
||||
if not obj.child_tags:
|
||||
result.append(yaml.dump('!'+str(obj), default_flow_style=False, allow_unicode=True, width=999).replace("'",''))
|
||||
else:
|
||||
result.append(yaml.safe_dump(obj, default_flow_style=False, allow_unicode=True, width=999).replace('\n:', ':\n').replace("'",''))
|
||||
print "# Experimental OpenERP xml-to-yml conversion! (v%s)"%__VERSION__
|
||||
print "# Please use this as a first conversion/preprocessing step,"
|
||||
print "# not as a production-ready tool!"
|
||||
for record in result:
|
||||
if type(record) != type(''):
|
||||
record=str(record)
|
||||
l= record.split("\n")
|
||||
for line in l:
|
||||
print '#' + str(line)
|
||||
continue
|
||||
record=str(record)
|
||||
record = record.replace('- --',' ') #for value tag
|
||||
record = record.replace('!!', '- \n !') #for all parent tags
|
||||
record = record.replace('- - -', ' -') #for many2many fields
|
||||
record = record.replace('? ', '') #for long expressions
|
||||
record = record.replace('""', "'") #for string-value under value tag
|
||||
print record
|
||||
|
||||
if __name__=='__main__':
|
||||
import optparse
|
||||
import sys
|
||||
parser = optparse.OptionParser(
|
||||
usage = '%s file.xml' % sys.argv[0])
|
||||
(opt, args) = parser.parse_args()
|
||||
if len(args) != 1:
|
||||
parser.error("incorrect number of arguments")
|
||||
fname = sys.argv[1]
|
||||
p = xml_parse()
|
||||
p.parse(fname)
|
||||
|
||||
Reference in New Issue
Block a user