From a13827eb221bbe04d76fd23fb0fe66987be1fe20 Mon Sep 17 00:00:00 2001 From: moylop260 Date: Wed, 20 Apr 2011 15:05:54 +0300 Subject: [PATCH 01/16] base_vat: Fix wrong validation for MX [Bug 749958] I tested the last patch https://launchpadlibrarian.net/68224790/base_vat.py.withregexp.patch With 242,665 Mexican valids RFC (cherry picked from commit 7ecb9aebfef4f2f131df4cd76372c455c06a8762) bzr revid: p_christ@hol.gr-20110420120554-itxrqntpgc05hksw --- addons/base_vat/base_vat.py | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/addons/base_vat/base_vat.py b/addons/base_vat/base_vat.py index 102619e876d..2fea37ddf0f 100644 --- a/addons/base_vat/base_vat.py +++ b/addons/base_vat/base_vat.py @@ -39,7 +39,7 @@ _ref_vat = { 'pt': 'PT123456789', 'ro': 'RO1234567897', 'se': 'SE123456789701', 'si': 'SI12345679', 'sk': 'SK0012345675', 'el': 'EL12345670', - 'mx': 'MXABC123456T1B' + 'mx': 'MXABCD831230T1B', } @@ -1066,17 +1066,39 @@ class res_partner(osv.osv): return False return True - def check_vat_mx(self, vat): + def check_vat_mx(vat): ''' - Verificar RFC méxico + Verificar RFC México ''' - if not 12 <= len(vat) <= 13: + #[IMP] base_vat: check_vat_mx by moylop260@hotmail.com, tested with 242,665 real RFC's + import time + import re + map_initials = "[A-Z|&]"*4 + map_date = "[0-9][0-9][0-1][1-9][0-3][0-9]" + map_code = "[A-Z|&|0-9]"*3 + mapping = map_initials + map_date + map_code + + vat = vat.upper().replace('ñ', 'Ñ').replace('\xd1', 'Ñ').replace('\xf1', 'Ñ')#upper ascii + vat = vat.replace('Ñ', 'X')#Replace ascii valid char, these is problems with match in regexp + vat = vat.replace(' ', '').replace('-', '')#Replace some char valid, but no required + if len(vat)==12: + vat = "X" + vat#Add a valid char, for pass validation with case with cad of len = 12 + if len(vat) <> 13: return False - elif len(vat)==12 and not vat[:3].isalpha() | vat[3:9].isdigit() | vat[-3:].isalnum(): + regex = re.compile(mapping) + if not regex.match(vat): + #No valid format return False - elif len(vat)==13 and not vat[:4].isalpha() | vat[4:10].isdigit() | vat[-3:].isalnum(): + date_match = re.search(map_date, vat) + date_format = '%y%m%d' + try: + time.strptime(date_match.group(), date_format) + except: + #Valid format, but date wrong return False + #Valid format and valid date return True + res_partner() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: From 3432fe6d651b569db5dc0c4c88586942fac1ae2e Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Wed, 20 Apr 2011 15:06:58 +0300 Subject: [PATCH 02/16] base_vat: cleaner version of Mexico validator bzr revid: p_christ@hol.gr-20110420120658-u5ycapiy20uxx5wn --- addons/base_vat/base_vat.py | 38 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/addons/base_vat/base_vat.py b/addons/base_vat/base_vat.py index 2fea37ddf0f..190541bbcfb 100644 --- a/addons/base_vat/base_vat.py +++ b/addons/base_vat/base_vat.py @@ -23,6 +23,8 @@ import string from osv import osv, fields from tools.translate import _ +import re +import datetime _ref_vat = { 'be': 'BE0477472701', 'at': 'ATU12345675', @@ -1066,36 +1068,26 @@ class res_partner(osv.osv): return False return True + __check_vat_mx_re = re.compile(r"(?P[A-Z&ñÑ]{3,4})" \ + r"[ \-_]?" \ + r"(?P[0-9]{2})(?P[01][1-9])(?P[0-3][0-9])" \ + r"[ \-_]?" \ + r"(?P[A-Z0-9&ñÑ\xd1\xf1]{3})$") + def check_vat_mx(vat): - ''' + ''' Mexican VAT verification + Verificar RFC México ''' - #[IMP] base_vat: check_vat_mx by moylop260@hotmail.com, tested with 242,665 real RFC's - import time - import re - map_initials = "[A-Z|&]"*4 - map_date = "[0-9][0-9][0-1][1-9][0-3][0-9]" - map_code = "[A-Z|&|0-9]"*3 - mapping = map_initials + map_date + map_code - - vat = vat.upper().replace('ñ', 'Ñ').replace('\xd1', 'Ñ').replace('\xf1', 'Ñ')#upper ascii - vat = vat.replace('Ñ', 'X')#Replace ascii valid char, these is problems with match in regexp - vat = vat.replace(' ', '').replace('-', '')#Replace some char valid, but no required - if len(vat)==12: - vat = "X" + vat#Add a valid char, for pass validation with case with cad of len = 12 - if len(vat) <> 13: - return False - regex = re.compile(mapping) - if not regex.match(vat): + m = self.__check_vat_mx_re.match(vat) + if not m: #No valid format return False - date_match = re.search(map_date, vat) - date_format = '%y%m%d' try: - time.strptime(date_match.group(), date_format) - except: - #Valid format, but date wrong + datetime.date(int(m.group('ano')), int(m.group('mes')), int(m.group('dia'))) + except ValueError: return False + #Valid format and valid date return True From 8d984744debb9c538b3d549ea627b82b95086252 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Fri, 29 Apr 2011 11:35:46 +0300 Subject: [PATCH 03/16] base_vat: fix the silly regression that broke check_vat_mx bzr revid: p_christ@hol.gr-20110429083546-vm5t0t1g5bibs8vh --- addons/base_vat/base_vat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/base_vat/base_vat.py b/addons/base_vat/base_vat.py index 190541bbcfb..bcfbfec3436 100644 --- a/addons/base_vat/base_vat.py +++ b/addons/base_vat/base_vat.py @@ -1074,7 +1074,7 @@ class res_partner(osv.osv): r"[ \-_]?" \ r"(?P[A-Z0-9&ñÑ\xd1\xf1]{3})$") - def check_vat_mx(vat): + def check_vat_mx(self, vat): ''' Mexican VAT verification Verificar RFC México From b6dce5a11b4c5e0bf5d7c50dca7d6f2f14392d3a Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Fri, 29 Apr 2011 11:36:25 +0300 Subject: [PATCH 04/16] base_vat: fix check_vat_mx, wrt. encoding, small letters and year range As of this commit, tests pass all the 242662 numbers provided by moylop260 bzr revid: p_christ@hol.gr-20110429083625-80dd16bs4dlbvt1w --- addons/base_vat/base_vat.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/addons/base_vat/base_vat.py b/addons/base_vat/base_vat.py index bcfbfec3436..7d0f61df82c 100644 --- a/addons/base_vat/base_vat.py +++ b/addons/base_vat/base_vat.py @@ -23,6 +23,7 @@ import string from osv import osv, fields from tools.translate import _ +from tools.misc import ustr import re import datetime @@ -1068,23 +1069,30 @@ class res_partner(osv.osv): return False return True - __check_vat_mx_re = re.compile(r"(?P[A-Z&ñÑ]{3,4})" \ + __check_vat_mx_re = re.compile(r"(?P[A-Za-z\xd1\xf1&]{3,4})" \ r"[ \-_]?" \ - r"(?P[0-9]{2})(?P[01][1-9])(?P[0-3][0-9])" \ + r"(?P[0-9]{2})(?P[01][0-9])(?P[0-3][0-9])" \ r"[ \-_]?" \ - r"(?P[A-Z0-9&ñÑ\xd1\xf1]{3})$") + r"(?P[A-Za-z0-9&\xd1\xf1]{3})$") def check_vat_mx(self, vat): ''' Mexican VAT verification Verificar RFC México ''' + # we convert to 8-bit encoding, to help the regex parse only bytes + vat = ustr(vat).encode('iso8859-1') m = self.__check_vat_mx_re.match(vat) if not m: #No valid format return False try: - datetime.date(int(m.group('ano')), int(m.group('mes')), int(m.group('dia'))) + ano = int(m.group('ano')) + if ano > 30: + ano = 1900 + ano + else: + ano = 2000 + ano + datetime.date(ano, int(m.group('mes')), int(m.group('dia'))) except ValueError: return False From 6c57b9ddc165c97239bd37ce45338898e827e46e Mon Sep 17 00:00:00 2001 From: Launchpad Translations on behalf of openerp <> Date: Wed, 1 Jun 2011 04:38:09 +0000 Subject: [PATCH 05/16] Launchpad automatic translations update. bzr revid: launchpad_translations_on_behalf_of_openerp-20110601043809-ocpn3eaostvtf4mx --- addons/base_action_rule/i18n/tr.po | 535 +++++++++++++++++++++++++++++ 1 file changed, 535 insertions(+) create mode 100644 addons/base_action_rule/i18n/tr.po diff --git a/addons/base_action_rule/i18n/tr.po b/addons/base_action_rule/i18n/tr.po new file mode 100644 index 00000000000..c7120a9b549 --- /dev/null +++ b/addons/base_action_rule/i18n/tr.po @@ -0,0 +1,535 @@ +# Turkish translation for openobject-addons +# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2011. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2011-01-11 11:14+0000\n" +"PO-Revision-Date: 2011-05-31 13:20+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Turkish \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2011-06-01 04:38+0000\n" +"X-Generator: Launchpad (build 12959)\n" + +#. module: base_action_rule +#: help:base.action.rule,act_mail_to_user:0 +msgid "" +"Check this if you want the rule to send an email to the responsible person." +msgstr "" +"Sorumlu kişiye eposta gönderme kuralını kullanmak istiyorsanız bunu " +"işaretleyin." + +#. module: base_action_rule +#: field:base.action.rule,act_remind_partner:0 +msgid "Remind Partner" +msgstr "Paydaşa Anımsat" + +#. module: base_action_rule +#: field:base.action.rule,trg_partner_categ_id:0 +msgid "Partner Category" +msgstr "Paydaş Kategorisi" + +#. module: base_action_rule +#: field:base.action.rule,act_mail_to_watchers:0 +msgid "Mail to Watchers (CC)" +msgstr "İzleyicilere Postala (CC)" + +#. module: base_action_rule +#: field:base.action.rule,trg_state_to:0 +msgid "Button Pressed" +msgstr "Düğmeye Basıldı" + +#. module: base_action_rule +#: field:base.action.rule,model_id:0 +msgid "Object" +msgstr "Nesne" + +#. module: base_action_rule +#: field:base.action.rule,act_mail_to_email:0 +msgid "Mail to these Emails" +msgstr "Bu Epostalara yolla" + +#. module: base_action_rule +#: field:base.action.rule,act_state:0 +msgid "Set State to" +msgstr "Durumu şuna Ayarla" + +#. module: base_action_rule +#: field:base.action.rule,act_email_from:0 +msgid "Email From" +msgstr "Gelen Eposta" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "Email Body" +msgstr "Eposta Gövdesi" + +#. module: base_action_rule +#: selection:base.action.rule,trg_date_range_type:0 +msgid "Days" +msgstr "Günler" + +#. module: base_action_rule +#: field:base.action.rule,last_run:0 +msgid "Last Run" +msgstr "Son Çalıştırma" + +#. module: base_action_rule +#: code:addons/base_action_rule/base_action_rule.py:313 +#, python-format +msgid "Error!" +msgstr "Hata!" + +#. module: base_action_rule +#: field:base.action.rule,act_reply_to:0 +msgid "Reply-To" +msgstr "Yanıtla" + +#. module: base_action_rule +#: help:base.action.rule,act_email_cc:0 +msgid "" +"These people will receive a copy of the future communication between partner " +"and users by email" +msgstr "" +"Bu kişiler paydaşla kullanıcılar arasındaki yapılacak iletişimin bir " +"kopyasını eposta yoluyla alacaklardır." + +#. module: base_action_rule +#: selection:base.action.rule,trg_date_range_type:0 +msgid "Minutes" +msgstr "Dakikalar" + +#. module: base_action_rule +#: field:base.action.rule,name:0 +msgid "Rule Name" +msgstr "Kural Adı" + +#. module: base_action_rule +#: help:base.action.rule,act_remind_partner:0 +msgid "" +"Check this if you want the rule to send a reminder by email to the partner." +msgstr "" +"Kuralın ortağa hatırlatma E-Postası göndermesini istiyorsanız bunu seçiniz." + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "Conditions on Model Partner" +msgstr "Model Paydaş için Koşullar" + +#. module: base_action_rule +#: selection:base.action.rule,trg_date_type:0 +msgid "Deadline" +msgstr "Bitiş tarihi" + +#. module: base_action_rule +#: field:base.action.rule,trg_partner_id:0 +msgid "Partner" +msgstr "Paydaş" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "%(object_subject)s = Object subject" +msgstr "%(nesne_konu)lar = Nesne konu" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "Email Reminders" +msgstr "Eposta Anımsatıcıları" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "Special Keywords to Be Used in The Body" +msgstr "Gövdede Kullanılacak Özel Anahtar Kelimeler" + +#. module: base_action_rule +#: field:base.action.rule,trg_state_from:0 +msgid "State" +msgstr "Durum" + +#. module: base_action_rule +#: model:ir.actions.act_window,help:base_action_rule.base_action_rule_act +msgid "" +"Use automated actions to automatically trigger actions for various screens. " +"Example: a lead created by a specific user may be automatically set to a " +"specific sales team, or an opportunity which still has status pending after " +"14 days might trigger an automatic reminder email." +msgstr "" +"Değişik ekranlar için kullanılacak otomatik tetikleme eylemleri için " +"otomatize eylemleri kullan. Örnek: belirli bir kullanıcı tarafından " +"oluşturulan paydaş otomatik olarak bir satış takımına atanabilir ya da halen " +"14 günlük bekleme durumunda olan bir fırsat otomatik anımsatma epostası ile " +"tetiklenebilir." + +#. module: base_action_rule +#: help:base.action.rule,act_mail_to_email:0 +msgid "Email-id of the persons whom mail is to be sent" +msgstr "Eposta gönderilecek kişinin Eposta kimliği" + +#. module: base_action_rule +#: view:base.action.rule:0 +#: model:ir.module.module,shortdesc:base_action_rule.module_meta_information +msgid "Action Rule" +msgstr "Eylem Kuralı" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "Fields to Change" +msgstr "Değiştirilecek Alanlar" + +#. module: base_action_rule +#: selection:base.action.rule,trg_date_type:0 +msgid "Creation Date" +msgstr "Oluşturulma Tarihi" + +#. module: base_action_rule +#: selection:base.action.rule,trg_date_type:0 +msgid "Last Action Date" +msgstr "Son Eylem Tarihi" + +#. module: base_action_rule +#: selection:base.action.rule,trg_date_range_type:0 +msgid "Hours" +msgstr "Saatler" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "%(object_id)s = Object ID" +msgstr "%(nesne_no)s = Nesne NO" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "Delay After Trigger Date" +msgstr "Tetikleme Tarihi sonrası Gecikme" + +#. module: base_action_rule +#: field:base.action.rule,act_remind_attach:0 +msgid "Remind with Attachment" +msgstr "Ek ile Anımsat" + +#. module: base_action_rule +#: constraint:ir.cron:0 +msgid "Invalid arguments" +msgstr "Geçersiz parametreler" + +#. module: base_action_rule +#: field:base.action.rule,act_user_id:0 +msgid "Set Responsible to" +msgstr "Sorumluyu şuna Ayarla" + +#. module: base_action_rule +#: selection:base.action.rule,trg_date_type:0 +msgid "None" +msgstr "Hiçbiri" + +#. module: base_action_rule +#: help:base.action.rule,act_email_to:0 +msgid "" +"Use a python expression to specify the right field on which one than we will " +"use for the 'To' field of the header" +msgstr "" +"Başlıkta 'Kime' için kullanılacak doğru alanı belirlemek için bir python " +"ifadesi kullanın" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "%(object_user_phone)s = Responsible phone" +msgstr "%(nesne_kullanıcı_telefon)lar = Sorumlu telefonu" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "" +"The rule uses the AND operator. The model must match all non-empty fields so " +"that the rule executes the action described in the 'Actions' tab." +msgstr "" +"Bu kural VE operatörünü kullanır. Model tüm boş olmayan alanlara uymalıdır " +"ki; kural 'Eylemler' sekmesinde belirtilen eylemi uygulasın." + +#. module: base_action_rule +#: field:base.action.rule,trg_date_range_type:0 +msgid "Delay type" +msgstr "Gecikme türü" + +#. module: base_action_rule +#: help:base.action.rule,regex_name:0 +msgid "" +"Regular expression for matching name of the resource\n" +"e.g.: 'urgent.*' will search for records having name starting with the " +"string 'urgent'\n" +"Note: This is case sensitive search." +msgstr "" +"Kaynak adına uygun kural ifadesi\n" +"ör.: 'acil.*' 'acil' dizesi ile başlayan bütün kayıtları arıyacaktır\n" +"Not: Harf durumu duyarlı bir aramadır." + +#. module: base_action_rule +#: field:base.action.rule,act_method:0 +msgid "Call Object Method" +msgstr "Nesne Arama Yöntemi" + +#. module: base_action_rule +#: field:base.action.rule,act_email_to:0 +msgid "Email To" +msgstr "Şuna Eposta Gönder" + +#. module: base_action_rule +#: help:base.action.rule,act_mail_to_watchers:0 +msgid "" +"Check this if you want the rule to mark CC(mail to any other person defined " +"in actions)." +msgstr "" +"Kuralın CC yi işaretlemesini isterseniz bunu işaretleyin (eylemlerde " +"tanımlanan herhangi bir kişiye postalama)" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "%(partner)s = Partner name" +msgstr "%(paydaş)lar = Paydaş Adı" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "Note" +msgstr "Not" + +#. module: base_action_rule +#: help:base.action.rule,act_email_from:0 +msgid "" +"Use a python expression to specify the right field on which one than we will " +"use for the 'From' field of the header" +msgstr "" +"Başlıkta 'Kime' için kullanılacak doğru alanı belirlemek için bir python " +"ifadesi kullanın" + +#. module: base_action_rule +#: field:base.action.rule,trg_date_range:0 +msgid "Delay after trigger date" +msgstr "Tetikleme Tarihi sonrası Gecikme" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "Conditions" +msgstr "Koşullar" + +#. module: base_action_rule +#: help:base.action.rule,trg_date_range:0 +msgid "" +"Delay After Trigger Date,specifies you can put a negative number. If you " +"need a delay before the trigger date, like sending a reminder 15 minutes " +"before a meeting." +msgstr "" +"Tetikleme Sonrası Gecikme, burada negatif sayı kullanabilirsiniz. Eğer " +"tetikleme tarihinden önce, örneğin toplantıdan 15 dakika önce anımsatma " +"gönder gibi, bir gecikmeye gerek duyarsanız." + +#. module: base_action_rule +#: field:base.action.rule,active:0 +msgid "Active" +msgstr "Etkin" + +#. module: base_action_rule +#: code:addons/base_action_rule/base_action_rule.py:314 +#, python-format +msgid "No E-Mail ID Found for your Company address!" +msgstr "Firma adresinizde bir Eposta Kimliği yok!" + +#. module: base_action_rule +#: field:base.action.rule,act_remind_user:0 +msgid "Remind Responsible" +msgstr "Sorumluya Hatırlat" + +#. module: base_action_rule +#: model:ir.module.module,description:base_action_rule.module_meta_information +msgid "This module allows to implement action rules for any object." +msgstr "" +"Bu modül herhangi bir nesne için eylem kurallaru uygulamanızı sağlar." + +#. module: base_action_rule +#: help:base.action.rule,sequence:0 +msgid "Gives the sequence order when displaying a list of rules." +msgstr "Kural listesini görüntülerken diziliş sırasını verir." + +#. module: base_action_rule +#: selection:base.action.rule,trg_date_range_type:0 +msgid "Months" +msgstr "Aylar" + +#. module: base_action_rule +#: field:base.action.rule,filter_id:0 +msgid "Filter" +msgstr "Süzgeç" + +#. module: base_action_rule +#: selection:base.action.rule,trg_date_type:0 +msgid "Date" +msgstr "Tarih" + +#. module: base_action_rule +#: help:base.action.rule,server_action_id:0 +msgid "" +"Describes the action name.\n" +"eg:on which object which action to be taken on basis of which condition" +msgstr "" +"Eylem adını belirtir.\n" +"ör:koşula uygun olarak hangi nesne için hangi eylem ele alınır" + +#. module: base_action_rule +#: model:ir.model,name:base_action_rule.model_ir_cron +msgid "ir.cron" +msgstr "ir.cron" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "%(object_description)s = Object description" +msgstr "%(nesne_tanım)lar = Nesne açıklama" + +#. module: base_action_rule +#: constraint:base.action.rule:0 +msgid "Error: The mail is not well formated" +msgstr "Hata: Mail düzgün bir formatta değil" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "Email Actions" +msgstr "Epsota Eylemleri" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "Email Information" +msgstr "Eposta Bilgisi" + +#. module: base_action_rule +#: model:ir.model,name:base_action_rule.model_base_action_rule +msgid "Action Rules" +msgstr "Eylem Kuralları" + +#. module: base_action_rule +#: help:base.action.rule,act_mail_body:0 +msgid "Content of mail" +msgstr "Posta içeriği" + +#. module: base_action_rule +#: field:base.action.rule,trg_user_id:0 +msgid "Responsible" +msgstr "Sorumlu" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "%(partner_email)s = Partner Email" +msgstr "%(paydas_eposta)lar = Paydaş Eposta" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "%(object_date)s = Creation date" +msgstr "%(nesne_tarih)ler = Oluşturma tarihi" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "%(object_user_email)s = Responsible Email" +msgstr "%(nesne_kullanıcı_eposta)lar = Sorumlu Eposta" + +#. module: base_action_rule +#: field:base.action.rule,act_mail_body:0 +msgid "Mail body" +msgstr "Posta gövdesi" + +#. module: base_action_rule +#: help:base.action.rule,act_remind_user:0 +msgid "" +"Check this if you want the rule to send a reminder by email to the user." +msgstr "" +"Kullanıcıya eposta ile anımsatma gönderme kuralını kullanmak için bunu " +"işaretleyin." + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "Server Action to be Triggered" +msgstr "Tetiklenecek Sunucu Eylemi" + +#. module: base_action_rule +#: field:base.action.rule,act_mail_to_user:0 +msgid "Mail to Responsible" +msgstr "Sorumluya Posta" + +#. module: base_action_rule +#: field:base.action.rule,act_email_cc:0 +msgid "Add Watchers (Cc)" +msgstr "İzleyici Ekle (Cc)" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "Conditions on Model Fields" +msgstr "Model Alanları Koşulları" + +#. module: base_action_rule +#: model:ir.actions.act_window,name:base_action_rule.base_action_rule_act +#: model:ir.ui.menu,name:base_action_rule.menu_base_action_rule_form +msgid "Automated Actions" +msgstr "Otomatik Eylemler" + +#. module: base_action_rule +#: field:base.action.rule,server_action_id:0 +msgid "Server Action" +msgstr "Sunucu Eylemi" + +#. module: base_action_rule +#: field:base.action.rule,regex_name:0 +msgid "Regex on Resource Name" +msgstr "Kaynak Adı için Kurallı İfade" + +#. module: base_action_rule +#: help:base.action.rule,act_remind_attach:0 +msgid "" +"Check this if you want that all documents attached to the object be attached " +"to the reminder email sent." +msgstr "" +"Nesneye iliştirilmiş bütün belgelerin gönderilen anımsatma epostasına " +"eklenmesini isterseniz bunu işaretleyin." + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "Conditions on Timing" +msgstr "Zamanlama Koşulları" + +#. module: base_action_rule +#: field:base.action.rule,sequence:0 +msgid "Sequence" +msgstr "Diziliş" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "Actions" +msgstr "Eylemler" + +#. module: base_action_rule +#: help:base.action.rule,active:0 +msgid "" +"If the active field is set to False, it will allow you to hide the rule " +"without removing it." +msgstr "" +"Eğer etkin alanı Yanlış olarak işaretli ise, kuralı silmeden gizlemenizi " +"sağlar." + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "%(object_user)s = Responsible name" +msgstr "%(nesne_kullanıcı)lar = Sorumlu adı" + +#. module: base_action_rule +#: field:base.action.rule,create_date:0 +msgid "Create Date" +msgstr "Tarih Oluştur" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "Conditions on States" +msgstr "Durum Koşulları" + +#. module: base_action_rule +#: field:base.action.rule,trg_date_type:0 +msgid "Trigger Date" +msgstr "Tetikleme Tarihi" From 6d1753a7342a6a64f1bac8340fae21530ed2d241 Mon Sep 17 00:00:00 2001 From: Vo Minh Thu Date: Wed, 1 Jun 2011 09:44:00 +0200 Subject: [PATCH 06/16] [FIX] tools/config: properly cast config file values. bzr revid: vmt@openerp.com-20110601074400-hv1jrgoqw2ji0yah --- openerp/tools/config.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/openerp/tools/config.py b/openerp/tools/config.py index 7a10a774cf8..65f6726febc 100644 --- a/openerp/tools/config.py +++ b/openerp/tools/config.py @@ -72,6 +72,9 @@ class configmanager(object): ['publisher_warranty_url', 'load_language', 'root_path', 'init', 'save', 'config', 'update']) + # dictionary mapping option destination (keys in self.options) to MyOptions. + self.casts = {} + self.misc = {} self.config_file = fname self.has_ssl = check_ssl() @@ -251,6 +254,7 @@ class configmanager(object): for group in parser.option_groups: for option in group.option_list: self.options[option.dest] = option.my_default + self.casts[option.dest] = option self.parse_config() @@ -314,8 +318,12 @@ class configmanager(object): ] for arg in keys: + # Copy the command-line argument... if getattr(opt, arg): self.options[arg] = getattr(opt, arg) + # ... or keep, but cast, the config file value. + elif isinstance(self.options[arg], basestring) and self.casts[arg].type in optparse.Option.TYPE_CHECKER: + self.options[arg] = optparse.Option.TYPE_CHECKER[self.casts[arg].type](self.casts[arg], arg, self.options[arg]) keys = [ 'language', 'translate_out', 'translate_in', 'overwrite_existing_translations', @@ -327,8 +335,12 @@ class configmanager(object): ] for arg in keys: + # Copy the command-line argument... if getattr(opt, arg) is not None: self.options[arg] = getattr(opt, arg) + # ... or keep, but cast, the config file value. + elif isinstance(self.options[arg], basestring) and self.casts[arg].type in optparse.Option.TYPE_CHECKER: + self.options[arg] = optparse.Option.TYPE_CHECKER[self.casts[arg].type](self.casts[arg], arg, self.options[arg]) if opt.assert_exit_level: self.options['assert_exit_level'] = self._LOGLEVELS[opt.assert_exit_level] From 26d85afab623f00481ed1f5181f78ee03b4eeed0 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Wed, 1 Jun 2011 11:11:16 +0200 Subject: [PATCH 07/16] [IMP] account: added access rights for model_account_treasury_report bzr revid: qdp-launchpad@openerp.com-20110601091116-iysfehqoj7r2hlue --- addons/account/security/ir.model.access.csv | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addons/account/security/ir.model.access.csv b/addons/account/security/ir.model.access.csv index 1ab94806b63..50e8dced7e6 100644 --- a/addons/account/security/ir.model.access.csv +++ b/addons/account/security/ir.model.access.csv @@ -125,3 +125,5 @@ "access_account_invoice_tax_sale_manager","account.invoice.tax sale manager","model_account_invoice_tax","base.group_sale_salesman",1,0,0,0 "access_account_sequence_fiscal_year_sale_user","account.sequence.fiscalyear.sale.user","model_account_sequence_fiscalyear","base.group_sale_salesman",1,1,1,0 "access_account_sequence_fiscal_year_sale_manager","account.sequence.fiscalyear.sale.manager","model_account_sequence_fiscalyear","base.group_sale_manager",1,1,1,1 +"access_account_treasury_report_manager","account.treasury.report.manager","model_account_treasury_report","account.group_account_manager",1,0,0,0 + From 7d60eddba8f89c00c5f3cad205c478f84f72e769 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Wed, 1 Jun 2011 12:20:21 +0200 Subject: [PATCH 08/16] [IMP] account, analytic accounting: added group by state on analytic accounts bzr revid: qdp-launchpad@openerp.com-20110601102021-pe3ixdhuwd410g0j --- addons/account/project/project_view.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addons/account/project/project_view.xml b/addons/account/project/project_view.xml index 1e3f4d5d373..e1f383f4792 100644 --- a/addons/account/project/project_view.xml +++ b/addons/account/project/project_view.xml @@ -17,6 +17,7 @@ + @@ -44,6 +45,7 @@ + From c491593b57ecf2b5528cb9093e97b11be4d5039b Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 1 Jun 2011 12:36:31 +0200 Subject: [PATCH 09/16] [IMP] project_mrp: improved project description bzr revid: odo@openerp.com-20110601103631-6ei1ugahbwe1ibpq --- addons/project_mrp/__openerp__.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/addons/project_mrp/__openerp__.py b/addons/project_mrp/__openerp__.py index 09d2c48fa08..ed723dda1f6 100644 --- a/addons/project_mrp/__openerp__.py +++ b/addons/project_mrp/__openerp__.py @@ -21,29 +21,30 @@ { - 'name': 'Procurement and Project Management integration', + 'name': 'Automatic task creation from procurement lines', 'version': '1.0', 'category': 'Project Management', 'description': """ -This module creates a link between procurement orders containing "service" lines and project management tasks. -============================================================================================================== +Automatically creates project tasks from procurement lines +========================================================== -When installed, this module will automatically create a new task -for each procurement order line, when the corresponding product +This module will automatically create a new task for each procurement +order line (e.g. for sale order lines), if the corresponding product meets the following characteristics: * Type = Service * Procurement method (Order fulfillment) = MTO (make to order) * Supply/Procurement method = Produce -The new task is created outside of any existing project, but -can be added to a project manually. +If on top of that a projet is specified on the product form (in the Procurement +tab), then the new task will be created in that specific project. +Otherwise, the new task will not belong to any project, and may be added to a +project manually later. When the project task is completed or cancelled, the workflow of the corresponding -procurement line is updated accordingly. - -This module is useful to be able to invoice services based on tasks -automatically created via sale orders. +procurement line is updated accordingly. For example if this procurement corresponds +to a sale order line, the sale order line will be considered delivered when the +task is completed. """, 'author': 'OpenERP SA', From c5f4eab14a7f4d513415fd94b0f12e47d45328ad Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 1 Jun 2011 12:38:02 +0200 Subject: [PATCH 10/16] [IMP] board: board.fields_view_get now returns the "custom_view_id" if there was one This can be used by client side to display special features regarding customization, like "Undo" buttons, etc. bzr revid: odo@openerp.com-20110601103802-swxqe17bts7rqn2k --- addons/board/board.py | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/board/board.py b/addons/board/board.py index 7de35fcc5f8..85f2cce7ef7 100644 --- a/addons/board/board.py +++ b/addons/board/board.py @@ -129,6 +129,7 @@ class board_board(osv.osv): if vids: view_id = vids[0] arch = self.pool.get('ir.ui.view.custom').browse(cr, user, view_id, context=context) + res['custom_view_id'] = view_id res['arch'] = arch.arch res['arch'] = self._arch_preprocessing(cr, user, res['arch'], context=context) res['toolbar'] = {'print': [], 'action': [], 'relate': []} From a270d19d81624a72ee69f23f5619894d7f64836e Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 1 Jun 2011 12:52:09 +0200 Subject: [PATCH 11/16] [IMP] ir.ui.view.custom: order by create_date descending This way search(limit=1) will give you the latest customization for a given user. This can be used by client side to allow multiple customizations with multi-level undo. bzr revid: odo@openerp.com-20110601105209-vtgf73reh90uipct --- openerp/addons/base/ir/ir_ui_view.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openerp/addons/base/ir/ir_ui_view.py b/openerp/addons/base/ir/ir_ui_view.py index 1600fa62d90..8aa4c0882bd 100644 --- a/openerp/addons/base/ir/ir_ui_view.py +++ b/openerp/addons/base/ir/ir_ui_view.py @@ -46,6 +46,7 @@ def _check_xml(self, cr, uid, ids, context=None): class view_custom(osv.osv): _name = 'ir.ui.view.custom' + _order = 'create_date desc' # search(limit=1) should return the last customization _columns = { 'ref_id': fields.many2one('ir.ui.view', 'Original View', select=True), 'user_id': fields.many2one('res.users', 'User', select=True), From 778b43b2dea2d5c43a7cfe4dda9c1365556232ff Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Wed, 1 Jun 2011 12:56:52 +0200 Subject: [PATCH 12/16] [FIX] Do not call success_callback if none given bzr revid: fme@openerp.com-20110601105652-wmtapmthal3yi9h6 --- addons/base/static/src/js/chrome.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/base/static/src/js/chrome.js b/addons/base/static/src/js/chrome.js index 4c1e1962d88..86549eca001 100644 --- a/addons/base/static/src/js/chrome.js +++ b/addons/base/static/src/js/chrome.js @@ -371,7 +371,7 @@ openerp.base.Session = openerp.base.BasicController.extend( /** @lends openerp.b } else { error_callback(response.error); } - } else { + } else if (success_callback) { success_callback(response["result"], textStatus, jqXHR); } }, From 4b3aa9c7641c0dfc07953e137a466b1478674b8a Mon Sep 17 00:00:00 2001 From: Vo Minh Thu Date: Wed, 1 Jun 2011 13:33:53 +0200 Subject: [PATCH 13/16] [FIX] modules/loading: revert a change where db.initialize was removed: - it should be removed - but some code will test if the ir_module_module table exists - and will force the demo data. bzr revid: vmt@openerp.com-20110601113353-hdmyl4is9poss8hh --- openerp/service/web_services.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openerp/service/web_services.py b/openerp/service/web_services.py index 10ab738c15b..39457807868 100644 --- a/openerp/service/web_services.py +++ b/openerp/service/web_services.py @@ -90,7 +90,12 @@ class db(netsvc.ExportService): cr = None try: serv.actions[id]['progress'] = 0 + cr = sql_db.db_connect(db_name).cursor() + openerp.modules.db.initialize(cr) # TODO this should be removed as it is done by pooler.restart_pool. tools.config['lang'] = lang + cr.commit() + cr.close() + pool = pooler.restart_pool(db_name, demo, serv.actions[id], update_module=True)[1] From b681b0ec75cf2ba6b6f8ef219c32f019358696e6 Mon Sep 17 00:00:00 2001 From: Vo Minh Thu Date: Wed, 1 Jun 2011 13:50:55 +0200 Subject: [PATCH 14/16] [ADD] tools/config: started tests. bzr revid: vmt@openerp.com-20110601115055-idoid8p0pnkfci38 --- openerp/tools/config.py | 6 ++-- openerp/tools/test-config-values | 25 +++++++++++++ openerp/tools/test-config-values-00.conf | 3 ++ openerp/tools/test_config.py | 46 ++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 2 deletions(-) create mode 100755 openerp/tools/test-config-values create mode 100644 openerp/tools/test-config-values-00.conf create mode 100644 openerp/tools/test_config.py diff --git a/openerp/tools/config.py b/openerp/tools/config.py index 65f6726febc..9a59235773a 100644 --- a/openerp/tools/config.py +++ b/openerp/tools/config.py @@ -258,8 +258,10 @@ class configmanager(object): self.parse_config() - def parse_config(self, args=[]): - opt, args = self.parser.parse_args() + def parse_config(self, args=None): + if args is None: + args = [] + opt, args = self.parser.parse_args(args) def die(cond, msg): if cond: diff --git a/openerp/tools/test-config-values b/openerp/tools/test-config-values new file mode 100755 index 00000000000..43991e8a21d --- /dev/null +++ b/openerp/tools/test-config-values @@ -0,0 +1,25 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import logging +import os +import signal +import sys +import threading +import traceback +import time + +import openerp + +openerp.tools.config.parse_config(sys.argv[1:]) +config = openerp.tools.config + +for name in [ + 'db_name', + 'addons_path', + 'demo', + 'osv_memory_count_limit', + 'osv_memory_age_limit', + ]: + print "%s: %s - %s" % (name, config[name], type(config[name])) + diff --git a/openerp/tools/test-config-values-00.conf b/openerp/tools/test-config-values-00.conf new file mode 100644 index 00000000000..1d0bd7419fe --- /dev/null +++ b/openerp/tools/test-config-values-00.conf @@ -0,0 +1,3 @@ +[options] +osv_memory_count_limit = 5 +osv_memory_age_limit = 3.4 diff --git a/openerp/tools/test_config.py b/openerp/tools/test_config.py new file mode 100644 index 00000000000..256e7ba4c32 --- /dev/null +++ b/openerp/tools/test_config.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- + +""" Tests for the configuration file/command-line arguments. """ + +# This test should be run from its directory. + +# TODO A configmanager object cannot parse multiple times a config file +# and/or the command line, preventing to 'reload' a configuration. + +import os + +import config + +config_file_00 = os.path.join(os.path.dirname(__file__),'test-config-values-00.conf') + +# 1. No config file, no command-line arguments (a.k.a. default values) + +conf = config.configmanager() +conf.parse_config() + +assert conf['osv_memory_age_limit'] == 1.0 +assert os.path.join(conf['root_path'], 'addons') == conf['addons_path'] + +# 2. No config file, some command-line arguments + +conf = config.configmanager() +# mess with the optparse.Option definition to allow an invalid path +conf.casts['addons_path'].action = 'store' +conf.parse_config(['--addons-path=/xyz/dont-exist', '--osv-memory-age-limit=2.3']) + +assert conf['osv_memory_age_limit'] == 2.3 +assert conf['addons_path'] == '/xyz/dont-exist' + +# 3. Config file, no command-line arguments + +conf = config.configmanager() +conf.parse_config(['-c', config_file_00]) + +assert conf['osv_memory_age_limit'] == 3.4 + +# 4. Config file, and command-line arguments + +conf = config.configmanager() +conf.parse_config(['-c', config_file_00, '--osv-memory-age-limit=2.3']) + +assert conf['osv_memory_age_limit'] == 2.3 From dac344cf3115e6e872809c5c03d3ff07968df6e5 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Wed, 1 Jun 2011 15:45:14 +0200 Subject: [PATCH 15/16] [ADD] Add custom views management (add, undo) in View controller bzr revid: fme@openerp.com-20110601134514-dv91qpjl50ptf161 --- addons/base/controllers/main.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/addons/base/controllers/main.py b/addons/base/controllers/main.py index 35289e487af..94eb05f7d6c 100644 --- a/addons/base/controllers/main.py +++ b/addons/base/controllers/main.py @@ -456,6 +456,8 @@ class DataGroup(openerpweb.Controller): dict(request.context, group_by=group_by_fields)) class View(openerpweb.Controller): + _cp_path = "/base/view" + def fields_view_get(self, request, model, view_id, view_type, transform=True, toolbar=False, submenu=False): Model = request.session.model(model) @@ -471,6 +473,25 @@ class View(openerpweb.Controller): fvg['arch'] = Xml2Json.convert_element(xml) return fvg + @openerpweb.jsonrequest + def add_custom(self, request, view_id, arch): + CustomView = request.session.model('ir.ui.view.custom') + CustomView.create({ + 'user_id': request.session._uid, + 'ref_id': view_id, + 'arch': arch + }) + return {'result': True} + + @openerpweb.jsonrequest + def undo_custom(self, request, view_id): + CustomView = request.session.model('ir.ui.view.custom') + vcustom = CustomView.search([('user_id', '=', request.session._uid), ('ref_id' ,'=', view_id)]) + if vcustom: + CustomView.unlink([vcustom[0]]) + return {'result': True} + return {'result': False} + def normalize_attrs(self, elem, context): """ Normalize @attrs, @invisible, @required, @readonly and @states, so the client only has to deal with @attrs. From 82d7cefef253239ec2a4fcac4ac870e23e31ceba Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Wed, 1 Jun 2011 15:48:00 +0200 Subject: [PATCH 16/16] [ADD] Save Dashboard's custom views with "Undo" button support bzr revid: fme@openerp.com-20110601134800-d9j48t4f287pokr6 --- .../static/src/css/dashboard.css | 9 ++++ .../base_dashboard/static/src/js/dashboard.js | 43 ++++++++++++++----- .../static/src/xml/base_dashboard.xml | 15 ++++--- 3 files changed, 51 insertions(+), 16 deletions(-) diff --git a/addons/base_dashboard/static/src/css/dashboard.css b/addons/base_dashboard/static/src/css/dashboard.css index 071a8cb8228..57f1ceb8f98 100644 --- a/addons/base_dashboard/static/src/css/dashboard.css +++ b/addons/base_dashboard/static/src/css/dashboard.css @@ -18,6 +18,15 @@ .openerp .oe-dashboard-action .oe-dashboard-action-header:hover { cursor: move; } +.openerp .oe-dashboard-action .ui-icon { + cursor: pointer; +} +.openerp .oe-dashboard-action .ui-icon:hover { + background-color: #ccc; + border-radius: 4px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; +} .openerp .oe-dashboard-action .oe-dashboard-action-header .ui-icon { float: right; diff --git a/addons/base_dashboard/static/src/js/dashboard.js b/addons/base_dashboard/static/src/js/dashboard.js index 3e253194d57..8497ffc2ce5 100644 --- a/addons/base_dashboard/static/src/js/dashboard.js +++ b/addons/base_dashboard/static/src/js/dashboard.js @@ -10,22 +10,18 @@ openerp.base.form.DashBoard = openerp.base.form.Widget.extend({ start: function() { var self = this; this._super.apply(this, arguments); + this.$element.find(".oe-dashboard-column").sortable({ connectWith: ".oe-dashboard-column", scroll: false }).disableSelection().bind('sortstop', self.do_save_dashboard); + + // Events + this.$element.find('.oe-dashboard-link-undo').click(this.on_undo); this.$element.find('.oe-dashboard-link-add_widget').click(this.on_add_widget); this.$element.find('.oe-dashboard-link-edit_layout').click(this.on_edit_layout); - - this.$element.find('.oe-dashboard-column .ui-icon-minusthick').click(function() { - $(this).toggleClass('ui-icon-minusthick').toggleClass('ui-icon-plusthick'); - $(this).parents('.oe-dashboard-action:first .oe-dashboard-action-content').toggle(); - }); - - $('.oe-dashboard-column .ui-icon-closethick').click(function() { - $(this).parents('.oe-dashboard-action:first').remove(); - self.do_save_dashboard(); - }); + this.$element.find('.oe-dashboard-column .ui-icon-minusthick').click(this.on_fold_action); + this.$element.find('.oe-dashboard-column .ui-icon-closethick').click(this.on_close_action); this.actions_attrs = {}; // Init actions @@ -41,13 +37,28 @@ openerp.base.form.DashBoard = openerp.base.form.Widget.extend({ }); }); }, + on_undo: function() { + this.rpc('/base/view/undo_custom', { + view_id: this.view.fields_view.view_id + }, this.do_reload); + }, on_add_widget: function() { }, on_edit_layout: function() { }, + on_fold_action: function(e) { + var $e = $(e.currentTarget); + $e.toggleClass('ui-icon-minusthick ui-icon-plusthick'); + $e.parents('.oe-dashboard-action:first').find('.oe-dashboard-action-content').toggle(); + }, + on_close_action: function(e) { + $(e.currentTarget).parents('.oe-dashboard-action:first').remove(); + this.do_save_dashboard(); + }, do_save_dashboard: function() { var self = this; var board = { + form_title : this.view.fields_view.arch.attrs.string, style : this.$element.find('.oe-dashboard').attr('data-layout'), columns : [] }; @@ -59,7 +70,13 @@ openerp.base.form.DashBoard = openerp.base.form.Widget.extend({ }); board.columns.push(actions); }); - var xml = QWeb.render('DashBoard.xml', board); + var arch = QWeb.render('DashBoard.xml', board); + this.rpc('/base/view/add_custom', { + view_id: this.view.fields_view.view_id, + arch: arch + }, function() { + self.$element.find('.oe-dashboard-link-undo').show(); + }); }, on_load_action: function(result) { var action = result.result; @@ -76,6 +93,10 @@ openerp.base.form.DashBoard = openerp.base.form.Widget.extend({ }, render: function() { return QWeb.render(this.template, this); + }, + do_reload: function() { + this.view.view_manager.stop(); + this.view.view_manager.start(); } }); openerp.base.form.DashBoardLegacy = openerp.base.form.DashBoard.extend({ diff --git a/addons/base_dashboard/static/src/xml/base_dashboard.xml b/addons/base_dashboard/static/src/xml/base_dashboard.xml index 349823847aa..80667dec557 100644 --- a/addons/base_dashboard/static/src/xml/base_dashboard.xml +++ b/addons/base_dashboard/static/src/xml/base_dashboard.xml @@ -1,6 +1,9 @@