diff --git a/bin/addons/base/i18n/base.pot b/bin/addons/base/i18n/base.pot index a42c62bb88a..9c64334abbc 100644 --- a/bin/addons/base/i18n/base.pot +++ b/bin/addons/base/i18n/base.pot @@ -11298,3 +11298,63 @@ msgstr "" msgid "Russian / русский язык" msgstr "" +#. module: base +#: sql_constraint:res.user:0 +msgid "You cannot have two users with the same login !" +msgstr "" + +#. module: base +#: sql_constraint:ir.model.data:0 +msgid "You cannot have multiple records with the same id for the same module" +msgstr "" + +#. module: base +#: sql_constraint:maintenance.contract:0 +msgid "Your maintenance contract is already subscribed in the system !" +msgstr "" + +#. module: base +#: sql_constraint:ir.module.module:0 +msgid "The name of the module must be unique !" +msgstr "" + +#. module: base +#: sql_constraint:res.partner.function:0 +msgid "The Code of the Partner Function must be unique !" +msgstr "" + +#. module: base +#: sql_constraint:res.partner:0 +msgid "The name of the Partner must be unique !" +msgstr "" + +#. module: base +#: sql_constraint:res.country:0 +msgid "The name of the country must be unique !" +msgstr "" + +#. module: base +#: sql_constraint:res.country:0 +msgid "The code of the country must be unique !" +msgstr "" + +#. module: base +#: sql_constraint:res.lang:0 +msgid "The name of the language must be unique !" +msgstr "" + +#. module: base +#: sql_constraint:res.lang:0 +msgid "The code of the language must be unique !" +msgstr "" + +#. module: base +#: code:addons/osv/osv.py:0 +#, python-format +msgid "Constraint Error" +msgstr "" + +#. module: base +#: selection:ir.translation,type:0 +msgid "SQL Constraint" +msgstr "" \ No newline at end of file diff --git a/bin/addons/base/ir/ir.xml b/bin/addons/base/ir/ir.xml index 1658be358d5..bba3d842260 100644 --- a/bin/addons/base/ir/ir.xml +++ b/bin/addons/base/ir/ir.xml @@ -1031,6 +1031,7 @@ + diff --git a/bin/addons/base/ir/ir_actions.py b/bin/addons/base/ir/ir_actions.py index 8bfc0740781..5f95f3bebdb 100644 --- a/bin/addons/base/ir/ir_actions.py +++ b/bin/addons/base/ir/ir_actions.py @@ -429,8 +429,8 @@ class actions_server(osv.osv): def _select_signals(self, cr, uid, context={}): cr.execute("select distinct t.signal as key, t.signal || ' - [ ' || w.osv || ' ] ' as val from wkf w, wkf_activity a, wkf_transition t "\ " where w.id = a.wkf_id " \ - " and t.act_from = a.wkf_id " \ - " or t.act_to = a.wkf_id and t.signal not in (null, NULL)") + " and t.act_from = a.id " \ + " or t.act_to = a.id and t.signal not in (null, NULL)") result = cr.fetchall() or [] res = [] for rs in result: diff --git a/bin/addons/base/ir/ir_cron.py b/bin/addons/base/ir/ir_cron.py index 18889d46a2a..3966a816f4f 100644 --- a/bin/addons/base/ir/ir_cron.py +++ b/bin/addons/base/ir/ir_cron.py @@ -28,7 +28,7 @@ import pooler from osv import fields, osv def str2tuple(s): - return eval('tuple(%s)' % s) + return eval('tuple(%s)' % (s or '')) _intervalTypes = { 'work_days': lambda interval: DateTime.RelativeDateTime(days=interval), diff --git a/bin/addons/base/ir/ir_model.py b/bin/addons/base/ir/ir_model.py index 1b204e476db..c5361fd952d 100644 --- a/bin/addons/base/ir/ir_model.py +++ b/bin/addons/base/ir/ir_model.py @@ -425,7 +425,7 @@ class ir_model_data(osv.osv): 'module': lambda *a: '' } _sql_constraints = [ - ('module_name_uniq', 'unique(name, module)', 'You can not have multiple records with the same id for the same module'), + ('module_name_uniq', 'unique(name, module)', 'You cannot have multiple records with the same id for the same module'), ] def __init__(self, pool, cr): diff --git a/bin/addons/base/ir/ir_translation.py b/bin/addons/base/ir/ir_translation.py index 8e2a6d98b10..e33365fcfca 100644 --- a/bin/addons/base/ir/ir_translation.py +++ b/bin/addons/base/ir/ir_translation.py @@ -35,6 +35,7 @@ TRANSLATION_TYPE = [ ('help', 'Help'), ('code', 'Code'), ('constraint', 'Constraint'), + ('sql_constraint', 'SQL Constraint') ] class ir_translation(osv.osv): @@ -131,14 +132,14 @@ class ir_translation(osv.osv): 'and type=%s ' \ 'and name=%s ' \ 'and src=%s', - (lang, tt, str(name), source)) + (lang, tt, tools.ustr(name), source)) else: cr.execute('select value ' \ 'from ir_translation ' \ 'where lang=%s ' \ 'and type=%s ' \ 'and name=%s', - (lang, tt, str(name))) + (lang, tt, tools.ustr(name))) res = cr.fetchone() trad = res and res[0] or '' return trad diff --git a/bin/osv/expression.py b/bin/osv/expression.py index 31a02e8f244..8fc7be28467 100644 --- a/bin/osv/expression.py +++ b/bin/osv/expression.py @@ -140,6 +140,12 @@ class expression(object): if field._type == 'many2one': right = field_obj.search(cr, uid, [(fargs[1], operator, right)], context=context) self.__exp[i] = (fargs[0], 'in', right) + # Making search easier when there is a left operand as field.o2m or field.m2m + if field._type in ['many2many','one2many']: + right = field_obj.search(cr, uid, [(fargs[1], operator, right)], context=context) + right1 = table.search(cr, uid, [(fargs[0],'in', right)], context=context) + self.__exp[i] = ('id', 'in', right1) + continue if field._properties: @@ -162,32 +168,45 @@ class expression(object): elif field._type == 'one2many': - call_null = True - - if right: + # Applying recursivity on field(one2many) + if operator == 'child_of': if isinstance(right, basestring): - ids2 = [x[0] for x in field_obj.name_search(cr, uid, right, [], operator, limit=None)] - operator = 'in' + ids2 = [x[0] for x in field_obj.name_search(cr, uid, right, [], 'like', limit=None)] else: - if not isinstance(right,list): - ids2 = [right] - else: - ids2 = right - if not ids2: - call_null = True - operator = 'in' # operator changed because ids are directly related to main object + ids2 = list(right) + if field._obj != working_table._name: + dom = _rec_get(ids2, field_obj, left=left, prefix=field._obj) else: - call_null = False - o2m_op = 'in' - if operator in ['not like','not ilike','not in','<>','!=']: - o2m_op = 'not in' - self.__exp[i] = ('id', o2m_op, self.__execute_recursive_in(cr, field._fields_id, field_obj._table, 'id', ids2, operator, field._type)) + dom = _rec_get(ids2, working_table, parent=left) + self.__exp = self.__exp[:i] + dom + self.__exp[i+1:] - if call_null: - o2m_op = 'not in' - if operator in ['not like','not ilike','not in','<>','!=']: - o2m_op = 'in' - self.__exp[i] = ('id', o2m_op, self.__execute_recursive_in(cr, field._fields_id, field_obj._table, 'id', [], operator, field._type) or [0]) + else: + call_null = True + + if right: + if isinstance(right, basestring): + ids2 = [x[0] for x in field_obj.name_search(cr, uid, right, [], operator, limit=None)] + operator = 'in' + else: + if not isinstance(right,list): + ids2 = [right] + else: + ids2 = right + if not ids2: + call_null = True + operator = 'in' # operator changed because ids are directly related to main object + else: + call_null = False + o2m_op = 'in' + if operator in ['not like','not ilike','not in','<>','!=']: + o2m_op = 'not in' + self.__exp[i] = ('id', o2m_op, self.__execute_recursive_in(cr, field._fields_id, field_obj._table, 'id', ids2, operator, field._type)) + + if call_null: + o2m_op = 'not in' + if operator in ['not like','not ilike','not in','<>','!=']: + o2m_op = 'in' + self.__exp[i] = ('id', o2m_op, self.__execute_recursive_in(cr, field._fields_id, field_obj._table, 'id', [], operator, field._type) or [0]) elif field._type == 'many2many': #FIXME diff --git a/bin/osv/fields.py b/bin/osv/fields.py index e40ef0e713f..55d78cd4852 100644 --- a/bin/osv/fields.py +++ b/bin/osv/fields.py @@ -527,6 +527,8 @@ class many2many(_column): return obj = obj.pool.get(self._obj) for act in values: + if not (isinstance(act, list) or isinstance(act, tuple)) or not act: + continue if act[0] == 0: idnew = obj.create(cr, user, act[2]) cr.execute('insert into '+self._rel+' ('+self._id1+','+self._id2+') values (%s,%s)', (id, idnew)) diff --git a/bin/osv/orm.py b/bin/osv/orm.py index a9b417dde84..6fbb29ee811 100644 --- a/bin/osv/orm.py +++ b/bin/osv/orm.py @@ -52,6 +52,7 @@ import fields import tools from tools.translate import _ +import copy import sys import copy @@ -62,6 +63,7 @@ except ImportError: sys.stderr.write("ERROR: Try to install the python-lxml package\n") sys.exit(2) + from tools.config import config regex_order = re.compile('^([a-z0-9_]+( *desc| *asc)?( *, *|))+$', re.I) @@ -534,7 +536,9 @@ class orm_template(object): for rr in r : if isinstance(rr.name, browse_record): rr = rr.name - dt += rr.name or '' + ',' + rr_name = self.pool.get(rr._table_name).name_get(cr, uid, [rr.id]) + rr_name = rr_name and rr_name[0] and rr_name[0][1] or '' + dt += tools.ustr(rr_name or '') + ',' data[fpos] = dt[:-1] break lines += lines2[1:] @@ -545,7 +549,8 @@ class orm_template(object): i += 1 if i == len(f): if isinstance(r, browse_record): - r = r.name + r = self.pool.get(r._table_name).name_get(cr, uid, [r.id]) + r = r and r[0] and r[0][1] or '' data[fpos] = tools.ustr(r or '') return [data] + lines @@ -854,14 +859,17 @@ class orm_template(object): noupdate=noupdate, res_id=res_id, context=context) except Exception, e: import psycopg2 + import osv if isinstance(e,psycopg2.IntegrityError): - msg= _('Insertion Failed!') + msg= _('Insertion Failed! ') for key in self.pool._sql_error.keys(): if key in e[0]: msg = self.pool._sql_error[key] break - return (-1, res,'Line ' + str(counter) +' : ' + msg,'' ) - + return (-1, res, 'Line ' + str(counter) +' : ' + msg, '' ) + if isinstance(e, osv.orm.except_orm ): + msg = _('Insertion Failed! ' + e[1]) + return (-1, res, 'Line ' + str(counter) +' : ' + msg, '' ) for lang in translate: context2 = context.copy() context2['lang'] = lang @@ -1041,10 +1049,9 @@ class orm_template(object): attrs = {'views': views} if node.get('widget') and node.get('widget') == 'selection': # We can not use the 'string' domain has it is defined according to the record ! - dom = [] + dom = None if column._domain and not isinstance(column._domain, (str, unicode)): dom = column._domain - attrs['selection'] = self.pool.get(relation).name_search(cr, user, '', dom, context=context) if (node.get('required') and not int(node.get('required'))) or not column.required: attrs['selection'].append((False,'')) @@ -1120,8 +1127,7 @@ class orm_template(object): # running -> done = signal_next (role Z) # running -> cancel = signal_cancel (role Z) - - # As we don't know the object state, in this scenario, + # As we don't know the object state, in this scenario, # the button "signal_cancel" will be always shown as there is no restriction to cancel in draft # the button "signal_next" will be show if the user has any of the roles (X Y or Z) # The verification will be made later in workflow process... @@ -1238,7 +1244,7 @@ class orm_template(object): while len(toparse): node2 = toparse.pop(0) if node2.tag == 'data': - toparse += node2.getchildren() + toparse += [ c for c in doc_dest ] continue node = _find(src, node2) if node is not None: @@ -1379,7 +1385,7 @@ class orm_template(object): result['name'] = 'default' result['field_parent'] = False result['view_id'] = 0 - + xarch, xfields = self.__view_look_dom_arch(cr, user, result['arch'], view_id, context=context) result['arch'] = xarch result['fields'] = xfields @@ -1997,12 +2003,23 @@ class orm(orm_template): cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO temp_change_size' % (self._table, k)) cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" VARCHAR(%d)' % (self._table, k, f.size)) cr.execute('UPDATE "%s" SET "%s"=temp_change_size::VARCHAR(%d)' % (self._table, k, f.size)) - cr.execute('ALTER TABLE "%s" DROP COLUMN temp_change_size' % (self._table,)) + cr.execute('ALTER TABLE "%s" DROP COLUMN temp_change_size CASCADE' % (self._table,)) cr.commit() for c in casts: if (f_pg_type==c[0]) and (f._type==c[1]): - if f_pg_type != f_obj_type: - logger.notifyChannel('orm', netsvc.LOG_INFO, "column '%s' in table '%s' changed type to %s." % (k, self._table, c[1])) + # Adding upcoming 6 lines to check whether only the size of the fields got changed or not.E.g. :(16,3) to (16,4) + field_size_change = False + if f_pg_type in ['int4','numeric','float8']: + if f.digits: + field_size = (65535 * f.digits[0]) + f.digits[0] + f.digits[1] + if field_size != f_pg_size: + field_size_change = True + + if f_pg_type != f_obj_type or field_size_change: + if f_pg_type != f_obj_type: + logger.notifyChannel('orm', netsvc.LOG_INFO, "column '%s' in table '%s' changed type to %s." % (k, self._table, c[1])) + if field_size_change: + logger.notifyChannel('orm', netsvc.LOG_INFO, "column '%s' in table '%s' changed in the size." % (k, self._table)) ok = True cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO temp_change_size' % (self._table, k)) cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, c[2])) @@ -2140,7 +2157,8 @@ class orm(orm_template): ok = True for x,y,z,e,f,l in self.pool._store_function[object]: if (x==self._name) and (y==store_field) and (e==fields2): - ok = False + if f==order: + ok = False if ok: self.pool._store_function[object].append( (self._name, store_field, fnct, fields2, order, length)) self.pool._store_function[object].sort(lambda x,y: cmp(x[4],y[4])) @@ -2306,7 +2324,21 @@ class orm(orm_template): for key, v in r.items(): if v == None: r[key] = False - + if key in self._columns.keys(): + type = self._columns[key]._type + elif key in self._inherit_fields.keys(): + type = self._inherit_fields[key][2]._type + else: + continue + if type == 'reference' and v: + model,ref_id = v.split(',') + table = self.pool.get(model)._table + cr.execute('select id from "%s" where id=%s' % (table,ref_id)) + id_exist = cr.fetchone() + if not id_exist: + cr.execute('update "'+self._table+'" set "'+key+'"=NULL where "%s"=%s' %(key,''.join("'"+str(v)+"'"))) + r[key] = '' + if isinstance(ids, (int, long, dict)): return result and result[0] or False return result @@ -3272,6 +3304,4 @@ class orm(orm_template): return False return True - # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: - diff --git a/bin/osv/osv.py b/bin/osv/osv.py index a8d4d901742..7d85ae363a9 100644 --- a/bin/osv/osv.py +++ b/bin/osv/osv.py @@ -33,6 +33,8 @@ from psycopg2 import IntegrityError from netsvc import Logger, LOG_ERROR from tools.misc import UpdateableDict +from tools.translate import _ + module_list = [] module_class_list = {} class_pool = {} @@ -63,7 +65,7 @@ class osv_pool(netsvc.Service): except IntegrityError, inst: 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, _('Constraint Error'), 'warning', _(self._sql_error[key])) self.abortResponse(1, 'Integrity Error', 'warning', inst[0]) except Exception, e: import traceback, sys diff --git a/bin/tools/amount_to_text.py b/bin/tools/amount_to_text.py index ab81084d39e..3612440a5ef 100644 --- a/bin/tools/amount_to_text.py +++ b/bin/tools/amount_to_text.py @@ -23,147 +23,128 @@ # French #------------------------------------------------------------- -unites = { - 0: '', 1:'un', 2:'deux', 3:'trois', 4:'quatre', 5:'cinq', 6:'six', 7:'sept', 8:'huit', 9:'neuf', - 10:'dix', 11:'onze', 12:'douze', 13:'treize', 14:'quatorze', 15:'quinze', 16:'seize', - 21:'vingt et un', 31:'trente et un', 41:'quarante et un', 51:'cinquante et un', 61:'soixante et un', - 71:'septante et un', 91:'nonante et un', 80:'quatre-vingts' -} -dizaine = { - 1: 'dix', 2:'vingt', 3:'trente',4:'quarante', 5:'cinquante', 6:'soixante', 7:'septante', 8:'quatre-vingt', 9:'nonante' -} +to_19_fr = ( 'zéro', 'un', 'deux', 'trois', 'quatre', 'cinq', 'six', + 'sept', 'huit', 'neuf', 'dix', 'onze', 'douze', 'treize', + 'quatorze', 'quinze', 'seize', 'dix-sept', 'dix-huit', 'dix-neuf' ) +tens_fr = ( 'vingt', 'trente', 'quarante', 'Cinquante', 'Soixante', 'Soixante-dix', 'Quatre-vingts', 'Quatre-vingt Dix') +denom_fr = ( '', + 'Mille', 'Millions', 'Milliards', 'Billions', 'Quadrillions', + 'Quintillion', 'Sextillion', 'Septillion', 'Octillion', 'Nonillion', + 'Décillion', 'Undecillion', 'Duodecillion', 'Tredecillion', 'Quattuordecillion', + 'Sexdecillion', 'Septendecillion', 'Octodecillion', 'Icosillion', 'Vigintillion' ) -centaine = { - 0:'', 1: 'cent', 2:'deux cent', 3:'trois cent',4:'quatre cent', 5:'cinq cent', 6:'six cent', 7:'sept cent', 8:'huit cent', 9:'neuf cent' -} +# convert a value < 100 to French. +def _convert_nn_fr(val): + if val < 20: + return to_19_fr[val] + for (dcap, dval) in ((k, 20 + (10 * v)) for (v, k) in enumerate(tens_fr)): + if dval + 10 > val: + if val % 10: + return dcap + '-' + to_19_fr[val % 10] + return dcap -mille = { - 0:'', 1:'mille' -} +# convert a value < 1000 to french, special cased because it is the level that kicks +# off the < 100 special case. The rest are more general. This also allows you to +# get strings in the form of 'forty-five hundred' if called directly. +def _convert_nnn_fr(val): + word = '' + (mod, rem) = (val % 100, val // 100) + if rem > 0: + word = to_19_fr[rem] + ' Cent' + if mod > 0: + word = word + ' ' + if mod > 0: + word = word + _convert_nn_fr(mod) + return word -def _100_to_text_fr(chiffre): - if chiffre in unites: - return unites[chiffre] - else: - if chiffre%10>0: - return dizaine[chiffre / 10]+'-'+unites[chiffre % 10] - else: - return dizaine[chiffre / 10] +def french_number(val): + if val < 100: + return _convert_nn_fr(val) + if val < 1000: + return _convert_nnn_fr(val) + for (didx, dval) in ((v - 1, 1000 ** v) for v in range(len(denom_fr))): + if dval > val: + mod = 1000 ** didx + l = val // mod + r = val - (l * mod) + ret = _convert_nnn_fr(l) + ' ' + denom_fr[didx] + if r > 0: + ret = ret + ', ' + french_number(r) + return ret -def _1000_to_text_fr(chiffre): - d = _100_to_text_fr(chiffre % 100) - d2 = chiffre/100 - if d2>0 and d: - return centaine[d2]+' '+d - elif d2>1 and not(d): - return centaine[d2]+'s' - else: - return centaine[d2] or d - -def _10000_to_text_fr(chiffre): - if chiffre==0: - return 'zero' - part1 = _1000_to_text_fr(chiffre % 1000) - part2 = mille.get(chiffre / 1000, _1000_to_text_fr(chiffre / 1000)+' mille') - if part2 and part1: - part1 = ' '+part1 - return part2+part1 - def amount_to_text_fr(number, currency): - units_number = int(number) units_name = currency - if units_number > 1: - units_name += 's' - units = _10000_to_text_fr(units_number) - units = units_number and '%s %s' % (units, units_name) or '' - - cents_number = int(number * 100) % 100 - cents_name = (cents_number > 1) and 'cents' or 'cent' - cents = _100_to_text_fr(cents_number) - cents = cents_number and '%s %s' % (cents, cents_name) or '' - - if units and cents: - cents = ' '+cents - - return units + cents + list = str(number).split('.') + start_word = french_number(abs(int(list[0]))) + end_word = french_number(int(list[1])) + cents_number = int(list[1]) + cents_name = (cents_number > 1) and ' Cents' or ' Cent' + final_result = start_word +' '+units_name+' '+ end_word +' '+cents_name + return final_result #------------------------------------------------------------- # Dutch #------------------------------------------------------------- -units_nl = { - 0:'', 1:'een', 2:'twee', 3:'drie', 4:'vier', 5:'vijf', 6:'zes', 7:'zeven', 8:'acht', 9:'negen', - 10:'tien', 11:'elf', 12:'twaalf', 13:'dertien', 14:'veertien' -} +to_19_nl = ( 'Nul', 'Een', 'Twee', 'Drie', 'Vier', 'Vijf', 'Zes', + 'Zeven', 'Acht', 'Negen', 'Tien', 'Elf', 'Twaalf', 'Dertien', + 'Veertien', 'Vijftien', 'Zestien', 'Zeventien', 'Achttien', 'Negentien' ) +tens_nl = ( 'Twintig', 'Dertig', 'Veertig', 'Vijftig', 'Zestig', 'Zeventig', 'Tachtig', 'Negentig') +denom_nl = ( '', + 'Duizend', 'Miljoen', 'Miljard', 'Triljoen', 'Quadriljoen', + 'Quintillion', 'Sextiljoen', 'Septillion', 'Octillion', 'Nonillion', + 'Decillion', 'Undecillion', 'Duodecillion', 'Tredecillion', 'Quattuordecillion', + 'Sexdecillion', 'Septendecillion', 'Octodecillion', 'Novemdecillion', 'Vigintillion' ) -tens_nl = { - 1: 'tien', 2:'twintig', 3:'dertig',4:'veertig', 5:'vijftig', 6:'zestig', 7:'zeventig', 8:'tachtig', 9:'negentig' -} +# convert a value < 100 to Dutch. +def _convert_nn_nl(val): + if val < 20: + return to_19_nl[val] + for (dcap, dval) in ((k, 20 + (10 * v)) for (v, k) in enumerate(tens_nl)): + if dval + 10 > val: + if val % 10: + return dcap + '-' + to_19_nl[val % 10] + return dcap -hundreds_nl = { - 0:'', 1: 'honderd', -} +# convert a value < 1000 to Dutch, special cased because it is the level that kicks +# off the < 100 special case. The rest are more general. This also allows you to +# get strings in the form of 'forty-five hundred' if called directly. +def _convert_nnn_nl(val): + word = '' + (mod, rem) = (val % 100, val // 100) + if rem > 0: + word = to_19_nl[rem] + ' Honderd' + if mod > 0: + word = word + ' ' + if mod > 0: + word = word + _convert_nn_nl(mod) + return word -thousands_nl = { - 0:'', 1:'duizend' -} +def dutch_number(val): + if val < 100: + return _convert_nn_nl(val) + if val < 1000: + return _convert_nnn_nl(val) + for (didx, dval) in ((v - 1, 1000 ** v) for v in range(len(denom_nl))): + if dval > val: + mod = 1000 ** didx + l = val // mod + r = val - (l * mod) + ret = _convert_nnn_nl(l) + ' ' + denom_nl[didx] + if r > 0: + ret = ret + ', ' + dutch_number(r) + return ret -def _100_to_text_nl(number): - if number in units_nl: - return units_nl[number] - else: - if number%10 > 0: - if number>10 and number<20: - return units_nl[number % 10]+tens_nl[number / 10] - else: - units = units_nl[number % 10] - if units[-1] == 'e': - joinword = 'ën' - else: - joinword = 'en' - return units+joinword+tens_nl[number / 10] - else: - return tens_nl[number / 10] - -def _1000_to_text_nl(number): - part1 = _100_to_text_nl(number % 100) - part2 = hundreds_nl.get(number / 100, units_nl[number/100] + hundreds_nl[1]) - if part2 and part1: - part1 = ' ' + part1 - return part2 + part1 - -def _10000_to_text_nl(number): - if number==0: - return 'nul' - part1 = _1000_to_text_nl(number % 1000) - if thousands_nl.has_key(number / 1000): - part2 = thousands_nl[number / 1000] - else: - if (number / 1000 % 100 > 0) and (number / 1000 > 100): - space = ' ' - else: - space = '' - part2 = _1000_to_text_nl(number / 1000) + space + thousands_nl[1] - if part2 and part1: - part1 = ' ' + part1 - return part2 + part1 - def amount_to_text_nl(number, currency): - units_number = int(number) units_name = currency - units = _10000_to_text_nl(units_number) - units = units_number and '%s %s' % (units, units_name) or '' - - cents_number = int(number * 100) % 100 - cents_name = 'cent' - cents = _100_to_text_nl(cents_number) - cents = cents_number and '%s %s' % (cents, cents_name) or '' - - if units and cents: - cents = ' ' + cents - - return units + cents + list = str(number).split('.') + start_word = dutch_number(int(list[0])) + end_word = dutch_number(int(list[1])) + cents_number = int(list[1]) + cents_name = (cents_number > 1) and 'cent' or 'cent' + final_result = start_word +' '+units_name+' '+ end_word +' '+cents_name + return final_result #------------------------------------------------------------- # Generic functions @@ -182,17 +163,17 @@ def amount_to_text(nbr, lang='fr', currency='euro'): Example: 1654: mille six cent cinquante-quatre. """ - if nbr > 1000000: -#TODO: use logger - print "WARNING: number too large '%d', can't translate it!" % (nbr,) - return str(nbr) +# if nbr > 1000000: +##TODO: use logger +# print "WARNING: number too large '%d', can't translate it!" % (nbr,) +# return str(nbr) if not _translate_funcs.has_key(lang): #TODO: use logger print "WARNING: no translation function found for lang: '%s'" % (lang,) #TODO: (default should be en) same as above lang = 'fr' - return _translate_funcs[lang](nbr, currency) + return _translate_funcs[lang](abs(nbr), currency) if __name__=='__main__': from sys import argv diff --git a/bin/tools/amount_to_text_en.py b/bin/tools/amount_to_text_en.py index 69778880738..1dbf1f95822 100644 --- a/bin/tools/amount_to_text_en.py +++ b/bin/tools/amount_to_text_en.py @@ -22,85 +22,66 @@ #------------------------------------------------------------- #ENGLISH #------------------------------------------------------------- +from tools.translate import _ -ones = { - 0: '', 1:'One', 2:'Two', 3:'Three', 4:'Four', 5:'Five', 6:'Six', 7:'Seven', 8:'Eight', 9:'Nine', - 10:'Ten', 11:'Eleven', 12:'Twelve', 13:'Thirteen', 14:'Forteen', 15:'Fifteen', 16:'Sixteen', 17:"Seventeen",18:"Eighteen",19:"Nineteen", -} +to_19 = ( 'Zero', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', + 'Seven', 'Eight', 'Nine', 'Ten', 'Eleven', 'Twelve', 'Thirteen', + 'Fourteen', 'Fifteen', 'Sixteen', 'Seventeen', 'Eighteen', 'Nineteen' ) +tens = ( 'Twenty', 'Thirty', 'Forty', 'Fifty', 'Sixty', 'Seventy', 'Eighty', 'Ninety') +denom = ( '', + 'Thousand', 'Million', 'Billion', 'Trillion', 'Quadrillion', + 'Quintillion', 'Sextillion', 'Septillion', 'Octillion', 'Nonillion', + 'Decillion', 'Undecillion', 'Duodecillion', 'Tredecillion', 'Quattuordecillion', + 'Sexdecillion', 'Septendecillion', 'Octodecillion', 'Novemdecillion', 'Vigintillion' ) -tens = { - 1: 'Ten', 2: 'Twenty ', 3:'Thirty', 4:'Forty', 5:'Fifty', 6: 'Sixty', 7 : 'Seventy', 8:'Eighty' ,9: 'Ninety'} +# convert a value < 100 to English. +def _convert_nn(val): + if val < 20: + return to_19[val] + for (dcap, dval) in ((k, 20 + (10 * v)) for (v, k) in enumerate(tens)): + if dval + 10 > val: + if val % 10: + return dcap + '-' + to_19[val % 10] + return dcap -hundred = { - 0:'',1: 'One Hundred', 2: 'Two Hundred', 3: 'Three Hundred', 4 :'Four Hundred', 5: 'Five Hundred', 6: 'Six Hundred', 7 :'Seven Hundred', 8:' Eight Hundred ', 9:'Nine Hundred ' -} - -thousands ={ - 0:'',1: 'One Thousand' -} - -lacs = { - 0:'',1: 'Lac' -} - -def _100_to_text(number): - if number in ones: - return ones[number] - else: - if number%10>0: - return tens[number / 10]+'-'+ones[number % 10] - else: - return tens[number / 10] - -def _1000_to_text(number): - d = _100_to_text(number % 100) - d2 = number/100 - if d2>0 and d: - return hundred[d2]+' '+d - elif d2>1 and not(d): - return hundred[d2]+'s' - else: - return hundred[d2] or d - -def _10000_to_text(number): - if number==0: - return 'zero' - part1 = _1000_to_text(number % 1000) - part2 = thousands.get(number / 1000, _1000_to_text(number / 1000)+' Thousands') - if part2 and part1: - part1 = ' '+part1 - return part2+part1 - -def _1000000_to_text(number): - if number==0: - return 'zero' - part1 = _10000_to_text(number % 100000) - part2 = lacs.get(number / 100000, _10000_to_text(number / 100000)+' Lacs') - if part2 and part1: - part1 = ' '+part1 - return part2+part1 +# convert a value < 1000 to english, special cased because it is the level that kicks +# off the < 100 special case. The rest are more general. This also allows you to +# get strings in the form of 'forty-five hundred' if called directly. +def _convert_nnn(val): + word = '' + (mod, rem) = (val % 100, val // 100) + if rem > 0: + word = to_19[rem] + ' Hundred' + if mod > 0: + word = word + ' ' + if mod > 0: + word = word + _convert_nn(mod) + return word +def english_number(val): + if val < 100: + return _convert_nn(val) + if val < 1000: + return _convert_nnn(val) + for (didx, dval) in ((v - 1, 1000 ** v) for v in range(len(denom))): + if dval > val: + mod = 1000 ** didx + l = val // mod + r = val - (l * mod) + ret = _convert_nnn(l) + ' ' + denom[didx] + if r > 0: + ret = ret + ', ' + english_number(r) + return ret def amount_to_text(number, currency): - lacs_number = int(number) units_name = currency - if lacs_number > 1: - units_name += 's' - - lacs = _1000000_to_text(lacs_number) - lacs = lacs_number and '%s %s' % (lacs, units_name) or '' - - units_number = int(number * 10000) % 10000 - units = _10000_to_text(units_number) - units = units_number and '%s %s' % (units, units_name) or '' - - cents_number = int(number * 100) % 100 - cents_name = (cents_number > 1) and 'cents' or 'cent' - cents = _100_to_text(cents_number) - cents = cents_number and '%s %s' % (cents.lower(), cents_name) or '' - if cents: - lacs += ' and %s' % (cents, ) - return lacs + list = str(number).split('.') + start_word = english_number(int(list[0])) + end_word = english_number(int(list[1])) + cents_number = int(list[1]) + cents_name = (cents_number > 1) and 'Cents' or 'Cent' + final_result = start_word +' '+units_name+' and ' + end_word +' '+cents_name + return final_result #------------------------------------------------------------- @@ -118,15 +99,15 @@ def amount_to_text(nbr, lang='en', currency='euro'): 1654: thousands six cent cinquante-quatre. """ import netsvc - if nbr > 10000000: - netsvc.Logger().notifyChannel('translate', netsvc.LOG_WARNING, _("Number too large '%d', can not translate it")) - return str(nbr) +# if nbr > 10000000: +# netsvc.Logger().notifyChannel('translate', netsvc.LOG_WARNING, _("Number too large '%d', can not translate it")) +# return str(nbr) if not _translate_funcs.has_key(lang): netsvc.Logger().notifyChannel('translate', netsvc.LOG_WARNING, _("no translation function found for lang: '%s'" % (lang,))) #TODO: (default should be en) same as above lang = 'en' - return _translate_funcs[lang](nbr, currency) + return _translate_funcs[lang](abs(nbr), currency) if __name__=='__main__': from sys import argv diff --git a/bin/tools/convert.py b/bin/tools/convert.py index bc603c761c0..495c71ef05a 100644 --- a/bin/tools/convert.py +++ b/bin/tools/convert.py @@ -21,7 +21,7 @@ import re import cStringIO -import xml.dom.minidom +from lxml import etree import osv import ir import pooler @@ -119,7 +119,6 @@ def _eval_xml(self,node, pool, cr, uid, idref, context=None): idref[id]=self.id_get(cr, False, id) return s % idref txt = '\n'+_process("".join([etree.tostring(i).encode("utf8") for i in node.getchildren()]), idref) - return txt if t in ('char', 'int', 'float'): d = node.text @@ -281,9 +280,10 @@ form: module.record_id""" % (xml_id,) res['header'] = eval(rec.get('header','')) if rec.get('report_type'): res['report_type'] = rec.get('report_type','') + res['multi'] = rec.get('multi','') and eval(rec.get('multi','')) - xml_id = rec.get('id','').encode('utf8') - + xml_id = rec.get('id','').encode('utf8') + self._test_xml_id(xml_id) if rec.get('groups'): @@ -302,7 +302,6 @@ 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','')): keyword = str(rec.get('keyword','') or 'client_print_multi') keys = [('action',keyword),('res_model',res['model'])] @@ -380,7 +379,7 @@ form: module.record_id""" % (xml_id,) view_id = False if rec.get('view'): view_id = self.id_get(cr, 'ir.actions.act_window', rec.get('view','').encode('utf-8')) - domain = rec.get('domain','').encode('utf-8') + 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') @@ -675,7 +674,6 @@ form: module.record_id""" % (xml_id,) if rec_context: rec_context = eval(rec_context) self._test_xml_id(rec_id) - if self.isnoupdate(data_node) and self.mode != 'init': # check if the xml record has an id string if rec_id: @@ -701,7 +699,6 @@ form: module.record_id""" % (xml_id,) else: # otherwise it is skipped return None - res = {} for field in [i for i in rec.getchildren() if (i.tag == "field")]: #TODO: most of this code is duplicated above (in _eval_xml)... @@ -895,7 +892,6 @@ def convert_xml_export(res): data = etree.SubElement ( page, 'data' ) text_node = etree.SubElement ( page, 'text' ) text_node.text = 'Some textual content.' - cr.commit() cr.close() diff --git a/bin/tools/translate.py b/bin/tools/translate.py index 50178bec47c..50f71a22626 100644 --- a/bin/tools/translate.py +++ b/bin/tools/translate.py @@ -134,12 +134,18 @@ class GettextAlias(object): cr = frame.f_locals.get('cr') try: lang = (frame.f_locals.get('context') or {}).get('lang', False) + if not (cr and lang): + args = frame.f_locals.get('args',False) + if args: + lang = args[-1].get('lang',False) + if frame.f_globals.get('pooler',False): + cr = pooler.get_db(frame.f_globals['pooler'].pool_dic.keys()[0]).cursor() if not (lang and cr): return source except: return source - cr.execute('select value from ir_translation where lang=%s and type=%s and src=%s', (lang, 'code', source)) + cr.execute('select value from ir_translation where lang=%s and type in (%s,%s) and src=%s', (lang, 'code','sql_constraint', source)) res_trans = cr.fetchone() return res_trans and res_trans[0] or source _ = GettextAlias() diff --git a/setup.py b/setup.py index bac72008235..1c088b3f8f2 100755 --- a/setup.py +++ b/setup.py @@ -59,6 +59,7 @@ required_modules = [ ('reportlab', 'reportlab module'), ('pychart', 'pychart module'), ('pydot', 'pydot module'), + ('lxml', 'lxml module: pythonic libxml2 and libxslt bindings'), ] def check_modules():