From d9c7febf49931172b76b229b84f8ce19afac172f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paulius=20Sladkevi=C4=8Dius?= Date: Wed, 24 Oct 2012 13:03:21 +0300 Subject: [PATCH 01/80] Fixed sorting for m2o bzr revid: paulius@hacbee.com-20121024100321-tqjm4pyk9y392qur --- openerp/osv/orm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 4c3330c97b7..72c394debdb 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -4766,6 +4766,7 @@ class BaseModel(object): """ if context is None: context = {} + order = order or self._order self.check_access_rights(cr, access_rights_uid or user, 'read') # For transient models, restrict acces to the current user, except for the super-user From c5d3036674fdd4345876f85cb1009d76daaf56ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 27 Nov 2012 15:29:44 +0100 Subject: [PATCH 02/80] [IMP] Needaction: couters are not counter with the menu anymore. Web-side: a call to get_needaction_data is performed to have action counters. Menus are then updated synchronously with this value. bzr revid: tde@openerp.com-20121127142944-m17zo62er5rh2lvg --- addons/web/controllers/main.py | 27 ++++++++++++++++++++++++--- addons/web/static/src/js/chrome.js | 15 +++++++++++++++ addons/web/tests/test_menu.py | 4 ++-- 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index ad5dd21d0d8..99cd6711d33 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -1034,6 +1034,10 @@ class Menu(openerpweb.Controller): def load(self, req): return {'data': self.do_load(req)} + @openerpweb.jsonrequest + def load_needaction(self, req, menu_ids): + return {'data': self.do_load_needaction(req, menu_ids)} + @openerpweb.jsonrequest def get_user_roots(self, req): return self.do_get_user_roots(req) @@ -1071,13 +1075,13 @@ class Menu(openerpweb.Controller): context = req.session.eval_context(req.context) Menus = req.session.model('ir.ui.menu') - menu_roots = Menus.read(self.do_get_user_roots(req), ['name', 'sequence', 'parent_id', 'action', 'needaction_enabled', 'needaction_counter'], context) - menu_root = {'id': False, 'name': 'root', 'parent_id': [-1, ''], 'children' : menu_roots} + menu_roots = Menus.read(self.do_get_user_roots(req), ['name', 'sequence', 'parent_id', 'action', 'needaction_enabled'], context) + menu_root = {'id': False, 'name': 'root', 'parent_id': [-1, ''], 'children': menu_roots} # menus are loaded fully unlike a regular tree view, cause there are a # limited number of items (752 when all 6.1 addons are installed) menu_ids = Menus.search([], 0, False, False, context) - menu_items = Menus.read(menu_ids, ['name', 'sequence', 'parent_id', 'action', 'needaction_enabled', 'needaction_counter'], context) + menu_items = Menus.read(menu_ids, ['name', 'sequence', 'parent_id', 'action', 'needaction_enabled'], context) # adds roots at the end of the sequence, so that they will overwrite # equivalent menu items from full menu read when put into id:item # mapping, resulting in children being correctly set on the roots. @@ -1101,6 +1105,23 @@ class Menu(openerpweb.Controller): return menu_root + def do_load_needaction(self, req, menu_ids=False): + """ Loads needaction counters for all or some specific menu ids. + + :param req: A request object, with an OpenERP session attribute + :type req: < session -> OpenERPSession > + :return: the menu root + :rtype: dict('children': menu_nodes) + """ + context = req.session.eval_context(req.context) + Menus = req.session.model('ir.ui.menu') + + if menu_ids == False: + menu_ids = Menus.search([('needaction_enabled', '=', True)], context=context) + + menu_needaction_data = Menus.get_needaction_data(menu_ids, context) + return menu_needaction_data + @openerpweb.jsonrequest def action(self, req, menu_id): actions = load_actions_from_ir_values(req,'action', 'tree_but_open', diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index 1bca6bf94cd..c1a506fd107 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -835,6 +835,21 @@ instance.web.Menu = instance.web.Widget.extend({ } this.trigger('menu_loaded', data); this.has_been_loaded.resolve(); + // Now launch the fetch of needaction counters, asynchronous + this.rpc("web/menu/load_needaction", {menu_ids: false}).done(function(r) { + self.on_needaction_loaded(r); + }); + }, + on_needaction_loaded: function(data) { + var self = this; + this.needaction_data = data; + _.each(this.needaction_data.data, function (item, menu_id) { + var $item = self.$secondary_menus.find('a[data-menu="' + menu_id + '"]'); + $item.remove('oe_menu_counter'); + if (item.needaction_counter && item.needaction_counter > 0) { + $item.append('
' + item.needaction_counter + '
'); + } + }); }, limit_entries: function() { var maximum_visible_links = this.maximum_visible_links; diff --git a/addons/web/tests/test_menu.py b/addons/web/tests/test_menu.py index c5e1b948b67..5e1d61708e1 100644 --- a/addons/web/tests/test_menu.py +++ b/addons/web/tests/test_menu.py @@ -43,7 +43,7 @@ class LoadTest(unittest2.TestCase): [], 0, False, False, self.request.session.eval_context()) self.MockMenus.read.assert_called_with( [], ['name', 'sequence', 'parent_id', - 'action', 'needaction_enabled', 'needaction_counter'], + 'action', 'needaction_enabled'], self.request.session.eval_context()) self.assertListEqual( @@ -62,7 +62,7 @@ class LoadTest(unittest2.TestCase): self.MockMenus.read.assert_called_with( [1, 2, 3], ['name', 'sequence', 'parent_id', - 'action', 'needaction_enabled', 'needaction_counter'], + 'action', 'needaction_enabled'], self.request.session.eval_context()) self.assertEqual( From 6c453fe4044d5444213ce5e65f98e375f8bf4bcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 27 Nov 2012 15:31:37 +0100 Subject: [PATCH 03/80] [IMP] Needaction: couters are not counter with the menu anymore. Server-side: removed needaction_counter field on menys. Added get_needaction_data method that calculates the needaction counters. Added a limit on needaction, because this allows to limitate the search without impacting the result (having 2386 unread entries or 99+ is basically equivalent). bzr revid: tde@openerp.com-20121127143137-k152ail3m2trlobl --- openerp/addons/base/ir/ir_needaction.py | 3 ++- openerp/addons/base/ir/ir_ui_menu.py | 29 +++++++++++++++++-------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/openerp/addons/base/ir/ir_needaction.py b/openerp/addons/base/ir/ir_needaction.py index db133ad9904..57b324de3b9 100644 --- a/openerp/addons/base/ir/ir_needaction.py +++ b/openerp/addons/base/ir/ir_needaction.py @@ -21,6 +21,7 @@ from osv import osv + class ir_needaction_mixin(osv.AbstractModel): '''Mixin class for objects using the need action feature. @@ -60,4 +61,4 @@ class ir_needaction_mixin(osv.AbstractModel): dom = self._needaction_domain_get(cr, uid, context=context) if not dom: return 0 - return self.search(cr, uid, (domain or []) +dom, context=context, count=True) + return self.search(cr, uid, (domain or []) + dom, limit=100, context=context, count=True) diff --git a/openerp/addons/base/ir/ir_ui_menu.py b/openerp/addons/base/ir/ir_ui_menu.py index 43ece8c0868..ef9ed3b7bf3 100644 --- a/openerp/addons/base/ir/ir_ui_menu.py +++ b/openerp/addons/base/ir/ir_ui_menu.py @@ -249,7 +249,7 @@ class ir_ui_menu(osv.osv): icon_image = False if icon_path: try: - icon_file = tools.file_open(icon_path,'rb') + icon_file = tools.file_open(icon_path, 'rb') icon_image = base64.encodestring(icon_file.read()) finally: icon_file.close() @@ -265,17 +265,26 @@ class ir_ui_menu(osv.osv): return res - def _get_needaction(self, cr, uid, ids, field_names, args, context=None): + def _get_needaction_enabled(self, cr, uid, ids, field_names, args, context=None): + res = dict.fromkeys(ids, False) + for menu in self.browse(cr, uid, ids, context=context): + if menu.action and menu.action.type in ('ir.actions.act_window', 'ir.actions.client') and menu.action.res_model: + obj = self.pool.get(menu.action.res_model) + if obj and obj._needaction: + res[menu.id] = True + return res + + def get_needaction_data(self, cr, uid, ids, context=None): res = {} for menu in self.browse(cr, uid, ids, context=context): res[menu.id] = { 'needaction_enabled': False, 'needaction_counter': False, } - if menu.action and menu.action.type in ('ir.actions.act_window','ir.actions.client') and menu.action.res_model: + if menu.action and menu.action.type in ('ir.actions.act_window', 'ir.actions.client') and menu.action.res_model: obj = self.pool.get(menu.action.res_model) if obj and obj._needaction: - if menu.action.type=='ir.actions.act_window': + if menu.action.type == 'ir.actions.act_window': dom = menu.action.domain and eval(menu.action.domain, {'uid': uid}) or [] else: dom = eval(menu.action.params_store or '{}', {'uid': uid}).get('domain') @@ -286,7 +295,7 @@ class ir_ui_menu(osv.osv): _columns = { 'name': fields.char('Menu', size=64, required=True, translate=True), 'sequence': fields.integer('Sequence'), - 'child_id' : fields.one2many('ir.ui.menu', 'parent_id','Child IDs'), + 'child_id': fields.one2many('ir.ui.menu', 'parent_id', 'Child IDs'), 'parent_id': fields.many2one('ir.ui.menu', 'Parent Menu', select=True), 'groups_id': fields.many2many('res.groups', 'ir_ui_menu_group_rel', 'menu_id', 'gid', 'Groups', help="If you have groups, the visibility of this menu will be based on these groups. "\ @@ -296,11 +305,13 @@ class ir_ui_menu(osv.osv): 'icon': fields.selection(tools.icons, 'Icon', size=64), 'icon_pict': fields.function(_get_icon_pict, type='char', size=32), 'web_icon': fields.char('Web Icon File', size=128), - 'web_icon_hover':fields.char('Web Icon File (hover)', size=128), + 'web_icon_hover': fields.char('Web Icon File (hover)', size=128), 'web_icon_data': fields.function(_get_image_icon, string='Web Icon Image', type='binary', readonly=True, store=True, multi='icon'), - 'web_icon_hover_data':fields.function(_get_image_icon, string='Web Icon Image (hover)', type='binary', readonly=True, store=True, multi='icon'), - 'needaction_enabled': fields.function(_get_needaction, string='Target model uses the need action mechanism', type='boolean', help='If the menu entry action is an act_window action, and if this action is related to a model that uses the need_action mechanism, this field is set to true. Otherwise, it is false.', multi='_get_needaction'), - 'needaction_counter': fields.function(_get_needaction, string='Number of actions the user has to perform', type='integer', help='If the target model uses the need action mechanism, this field gives the number of actions the current user has to perform.', multi='_get_needaction'), + 'web_icon_hover_data': fields.function(_get_image_icon, string='Web Icon Image (hover)', type='binary', readonly=True, store=True, multi='icon'), + 'needaction_enabled': fields.function(_get_needaction_enabled, + type='boolean', store=True, + string='Target model uses the need action mechanism', + help='If the menu entry action is an act_window action, and if this action is related to a model that uses the need_action mechanism, this field is set to true. Otherwise, it is false.'), 'action': fields.function(_action, fnct_inv=_action_inv, type='reference', string='Action', selection=[ From 9dc08f3d7016b9efa560b224ed6086cd8a565179 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 27 Nov 2012 15:32:22 +0100 Subject: [PATCH 04/80] [IMP] mail_thread: improved search function of message_unread, now directly implemented in SQL to speedup the result. bzr revid: tde@openerp.com-20121127143222-nj9i2o5byor4m7oq --- addons/mail/mail_thread.py | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 15bc53223ba..dce5bc861e4 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -121,18 +121,15 @@ class mail_thread(osv.AbstractModel): return res - def _search_unread(self, cr, uid, obj=None, name=None, domain=None, context=None): - partner_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] - res = {} - notif_obj = self.pool.get('mail.notification') - notif_ids = notif_obj.search(cr, uid, [ - ('partner_id', '=', partner_id), - ('message_id.model', '=', self._name), - ('read', '=', False) - ], context=context) - for notif in notif_obj.browse(cr, uid, notif_ids, context=context): - res[notif.message_id.res_id] = True - return [('id', 'in', res.keys())] + def _search_message_unread(self, cr, uid, obj=None, name=None, domain=None, context=None): + user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] + cr.execute(""" SELECT DISTINCT m.res_id FROM mail_message m + JOIN mail_notification n + ON (n.message_id = m.id AND n.partner_id = %s AND (n.read = False or n.read IS NULL)) + WHERE m.model = %s""", + (user_pid, self._name,)) + res_ids = [result[0] for result in cr.fetchall()] + return [('id', 'in', res_ids)] def _get_followers(self, cr, uid, ids, name, arg, context=None): fol_obj = self.pool.get('mail.followers') @@ -209,8 +206,9 @@ class mail_thread(osv.AbstractModel): domain=lambda self: [('model', '=', self._name)], string='Messages', help="Messages and communication history"), - 'message_unread': fields.function(_get_message_data, fnct_search=_search_unread, - type='boolean', string='Unread Messages', multi="_get_message_data", + 'message_unread': fields.function(_get_message_data, + fnct_search=_search_message_unread, multi="_get_message_data", + type='boolean', string='Unread Messages', help="If checked new messages require your attention."), 'message_summary': fields.function(_get_message_data, method=True, type='text', string='Summary', multi="_get_message_data", From e0fa7da24fc25be4f0a83572018d900dc6ca24c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 27 Nov 2012 15:35:20 +0100 Subject: [PATCH 05/80] [REM] mail_thread: removed field message_comment_ids, not really necessary. In message_summary, we display the true number of messages, not only comment number. This is more consistent with what you will see in Chatter, aka all messages. bzr revid: tde@openerp.com-20121127143520-cv4os3dosrqo01wb --- addons/mail/mail_thread.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index dce5bc861e4..096d7d71045 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -88,7 +88,7 @@ class mail_thread(osv.AbstractModel): for thread in self.browse(cr, uid, ids, context=context): cls = res[thread.id]['message_unread'] and ' class="oe_kanban_mail_new"' or '' - res[thread.id]['message_summary'] = "9 %d + %d" % (cls, len(thread.message_comment_ids), len(thread.message_follower_ids)) + res[thread.id]['message_summary'] = "9 %d + %d" % (cls, len(thread.message_ids), len(thread.message_follower_ids)) return res @@ -198,10 +198,6 @@ class mail_thread(osv.AbstractModel): 'message_follower_ids': fields.function(_get_followers, fnct_inv=_set_followers, fnct_search=_search_followers, type='many2many', obj='res.partner', string='Followers', multi='_get_followers'), - 'message_comment_ids': fields.one2many('mail.message', 'res_id', - domain=lambda self: [('model', '=', self._name), ('type', 'in', ('comment', 'email'))], - string='Comments and emails', - help="Comments and emails"), 'message_ids': fields.one2many('mail.message', 'res_id', domain=lambda self: [('model', '=', self._name)], string='Messages', @@ -243,7 +239,6 @@ class mail_thread(osv.AbstractModel): def copy(self, cr, uid, id, default=None, context=None): default = default or {} default['message_ids'] = [] - default['message_comment_ids'] = [] default['message_follower_ids'] = [] return super(mail_thread, self).copy(cr, uid, id, default=default, context=context) From c1c5a852312df8bd34836cc91bff90c42d2ad296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 27 Nov 2012 16:01:59 +0100 Subject: [PATCH 06/80] [CLEAN] expression.py: renamed some variables of expression; cleaned a bit the coding style (mainly spaces after commas). bzr revid: tde@openerp.com-20121127150159-su9x00m7tc8tc6dj --- openerp/osv/expression.py | 93 ++++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 41 deletions(-) diff --git a/openerp/osv/expression.py b/openerp/osv/expression.py index 2b2d33a496e..bb2fce62b21 100644 --- a/openerp/osv/expression.py +++ b/openerp/osv/expression.py @@ -362,11 +362,24 @@ class expression(object): """ def __init__(self, cr, uid, exp, table, context): + """ Initialize expression object and automatically parse the expression + right after initialization. + + :param exp: expression (using domain ('foo', '=', 'bar' format)) + :param table: root table object + + :attr dict leaf_to_table: used to store the table to use for the + sql generation, according to the domain leaf. + structure: { [leaf index]: table object } + :attr set table_aliases: set of aliases. + :attr list joins: list of join conditions, such as (res_country_state."id" = res_partner."state_id") + :attr root_table: root table, set by parse() + """ self.has_unaccent = openerp.modules.registry.RegistryManager.get(cr.dbname).has_unaccent - self.__field_tables = {} # used to store the table to use for the sql generation. key = index of the leaf - self.__all_tables = set() - self.__joins = [] - self.__main_table = None # 'root' table. set by parse() + self.leaf_to_table = {} + self.table_aliases = set() + self.joins = [] + self.root_table = None # assign self.__exp with the normalized, parsed domain. self.parse(cr, uid, distribute_not(normalize(exp)), table, context) @@ -378,8 +391,8 @@ class expression(object): def parse(self, cr, uid, exp, table, context): """ transform the leaves of the expression """ self.__exp = exp - self.__main_table = table - self.__all_tables.add(table) + self.root_table = table + self.table_aliases.add(table) def child_of_domain(left, ids, left_model, parent=None, prefix=''): """Returns a domain implementing the child_of operator for [(left,child_of,ids)], @@ -419,7 +432,7 @@ class expression(object): return list(value) i = -1 - while i + 1','<=','>='] ) and 'in' or operator + operator = (operator in ['<', '>', '<=', '>=']) and 'in' or operator - dict_op = {'not in':'!=','in':'=','=':'in','!=':'not in'} + dict_op = {'not in': '!=', 'in': '=', '=': 'in', '!=': 'not in'} if isinstance(right, tuple): right = list(right) - if (not isinstance(right, list)) and operator in ['not in','in']: + if (not isinstance(right, list)) and operator in ['not in', 'in']: operator = dict_op[operator] - elif isinstance(right, list) and operator in ['!=','=']: #for domain (FIELD,'=',['value1','value2']) + elif isinstance(right, list) and operator in ['!=', '=']: # for domain (FIELD,'=',['value1','value2']) operator = dict_op[operator] res_ids = [x[0] for x in field_obj.name_search(cr, uid, right, [], operator, limit=None, context=c)] if operator in NEGATIVE_TERM_OPERATORS: - res_ids.append(False) # TODO this should not be appended if False was in 'right' + res_ids.append(False) # TODO this should not be appended if False was in 'right' return (left, 'in', res_ids) # resolve string-based m2o criterion into IDs if isinstance(right, basestring) or \ - right and isinstance(right, (tuple,list)) and all(isinstance(item, basestring) for item in right): + right and isinstance(right, (tuple, list)) and all(isinstance(item, basestring) for item in right): self.__exp[i] = _get_expression(field_obj, cr, uid, left, right, operator, context=context) - else: + else: # right == [] or right == False and all other cases are handled by __leaf_to_sql() pass @@ -640,7 +653,7 @@ class expression(object): if field.translate: need_wildcard = operator in ('like', 'ilike', 'not like', 'not ilike') - sql_operator = {'=like':'like','=ilike':'ilike'}.get(operator,operator) + sql_operator = {'=like': 'like', '=ilike': 'ilike'}.get(operator, operator) if need_wildcard: right = '%%%s%%' % right @@ -651,13 +664,13 @@ class expression(object): ' AND type = %s' instr = ' %s' #Covering in,not in operators with operands (%s,%s) ,etc. - if sql_operator in ['in','not in']: + if sql_operator in ['in', 'not in']: instr = ','.join(['%s'] * len(right)) - subselect += ' AND value ' + sql_operator + ' ' +" (" + instr + ")" \ + subselect += ' AND value ' + sql_operator + ' ' + " (" + instr + ")" \ ') UNION (' \ ' SELECT id' \ ' FROM "' + working_table._table + '"' \ - ' WHERE "' + left + '" ' + sql_operator + ' ' +" (" + instr + "))" + ' WHERE "' + left + '" ' + sql_operator + ' ' + " (" + instr + "))" else: subselect += ' AND value ' + sql_operator + instr + \ ') UNION (' \ @@ -729,11 +742,11 @@ class expression(object): elif not check_nulls and operator == 'not in': query = '(%s OR %s."%s" IS NULL)' % (query, table._table, left) elif check_nulls and operator == 'not in': - query = '(%s AND %s."%s" IS NOT NULL)' % (query, table._table, left) # needed only for TRUE. - else: # Must not happen + query = '(%s AND %s."%s" IS NOT NULL)' % (query, table._table, left) # needed only for TRUE. + else: # Must not happen raise ValueError("Invalid domain term %r" % (leaf,)) - elif right == False and (left in table._columns) and table._columns[left]._type=="boolean" and (operator == '='): + elif right == False and (left in table._columns) and table._columns[left]._type == "boolean" and (operator == '='): query = '(%s."%s" IS NULL or %s."%s" = false )' % (table._table, left, table._table, left) params = [] @@ -741,7 +754,7 @@ class expression(object): query = '%s."%s" IS NULL ' % (table._table, left) params = [] - elif right == False and (left in table._columns) and table._columns[left]._type=="boolean" and (operator == '!='): + elif right == False and (left in table._columns) and table._columns[left]._type == "boolean" and (operator == '!='): query = '(%s."%s" IS NOT NULL and %s."%s" != false)' % (table._table, left, table._table, left) params = [] @@ -764,7 +777,7 @@ class expression(object): else: need_wildcard = operator in ('like', 'ilike', 'not like', 'not ilike') - sql_operator = {'=like':'like','=ilike':'ilike'}.get(operator,operator) + sql_operator = {'=like': 'like', '=ilike': 'ilike'}.get(operator, operator) if left in table._columns: format = need_wildcard and '%s' or table._columns[left]._symbol_set[0] @@ -775,7 +788,7 @@ class expression(object): elif left in MAGIC_COLUMNS: query = "(%s.\"%s\" %s %%s)" % (table._table, left, sql_operator) params = right - else: # Must not happen + else: # Must not happen raise ValueError("Invalid field %r in domain term %r" % (left, leaf)) add_null = False @@ -798,14 +811,13 @@ class expression(object): params = [params] return (query, params) - def to_sql(self): stack = [] params = [] # Process the domain from right to left, using a stack, to generate a SQL expression. for i, e in reverse_enumerate(self.__exp): if is_leaf(e, internal=True): - table = self.__field_tables.get(i, self.__main_table) + table = self.leaf_to_table.get(i, self.root_table) q, p = self.__leaf_to_sql(e, table) params.insert(0, p) stack.append(q) @@ -819,13 +831,12 @@ class expression(object): assert len(stack) == 1 query = stack[0] - joins = ' AND '.join(self.__joins) + joins = ' AND '.join(self.joins) if joins: query = '(%s) AND %s' % (joins, query) return (query, flatten(params)) def get_tables(self): - return ['"%s"' % t._table for t in self.__all_tables] + return ['"%s"' % t._table for t in self.table_aliases] # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: - From e147a5483f378eaa53f148017e289801897e8f64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 27 Nov 2012 16:11:13 +0100 Subject: [PATCH 07/80] [CLEAN] expression.py: table_aliases now holds a list of aliases (names), and table_aliases_mapping does the mapping between a name and a table. The parsing algorithm now uses some accessors instead of directly accessing the data structures. bzr revid: tde@openerp.com-20121127151113-zregov1i2isoll9z --- openerp/osv/expression.py | 47 ++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/openerp/osv/expression.py b/openerp/osv/expression.py index bb2fce62b21..58f9defccd4 100644 --- a/openerp/osv/expression.py +++ b/openerp/osv/expression.py @@ -371,28 +371,56 @@ class expression(object): :attr dict leaf_to_table: used to store the table to use for the sql generation, according to the domain leaf. structure: { [leaf index]: table object } - :attr set table_aliases: set of aliases. + :attr set table_aliases: set of aliases. Previously this attribute + was a set of table objects; now that joins generation is included + into the expression parsing, it holds aliases, and a mapping + exist linking aliases to tables. + :attr dict table_aliases_mapping: mapping alias -> table object :attr list joins: list of join conditions, such as (res_country_state."id" = res_partner."state_id") :attr root_table: root table, set by parse() """ self.has_unaccent = openerp.modules.registry.RegistryManager.get(cr.dbname).has_unaccent self.leaf_to_table = {} self.table_aliases = set() + self.table_aliases_mapping = {} self.joins = [] self.root_table = None # assign self.__exp with the normalized, parsed domain. self.parse(cr, uid, distribute_not(normalize(exp)), table, context) - # TODO used only for osv_memory - @property - def exp(self): - return self.__exp[:] + # TDE note: this seems not to be used anymore, commenting + # # TODO used only for osv_memory + # @property + # def exp(self): + # return self.__exp[:] + + def _has_table_alias(self, alias): + return alias in self.table_aliases + + def _get_table_from_alias(self, alias): + return self.table_aliases_mapping.get(alias) + + def _get_full_alias(self, alias): + if not self._get_table_from_alias(alias): + return False + return '%s as %s' % (self._get_table_from_alias(alias)._table, alias) + + def _add_table_alias(self, alias, table): + if not self._has_table_alias(alias): + self.table_aliases.add(alias) + self.table_aliases_mapping[alias] = table + else: + raise ValueError("Already existing alias %s for table %s, trying to set it for table %s" % (alias, self._get_table_from_alias(alias)._table, table._table)) + + def get_tables(self): + """ Returns the list of tables for SQL queries, like select from ... """ + return ['"%s"' % item for item in self.table_aliases] def parse(self, cr, uid, exp, table, context): """ transform the leaves of the expression """ self.__exp = exp self.root_table = table - self.table_aliases.add(table) + self._add_table_alias(table._table, table) def child_of_domain(left, ids, left_model, parent=None, prefix=''): """Returns a domain implementing the child_of operator for [(left,child_of,ids)], @@ -460,9 +488,9 @@ class expression(object): self.leaf_to_table[i] = working_table break next_table = working_table.pool.get(working_table._inherit_fields[field_path[0]][0]) - if next_table not in self.table_aliases: + if not self._has_table_alias(next_table._table): self.joins.append('%s."%s"=%s."%s"' % (next_table._table, 'id', working_table._table, working_table._inherits[next_table._name])) - self.table_aliases.add(next_table) + self._add_table_alias(next_table._table, next_table) working_table = next_table # Or (try to) directly extract the field. else: @@ -836,7 +864,4 @@ class expression(object): query = '(%s) AND %s' % (joins, query) return (query, flatten(params)) - def get_tables(self): - return ['"%s"' % t._table for t in self.table_aliases] - # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: From 1bda6f44db6895f9ce6264c400066c0a1f0caa2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 27 Nov 2012 16:34:32 +0100 Subject: [PATCH 08/80] [ADD] fields.py: added _auto_join attribute. This attribute will be used to automatically generate join queries, instead of doing sub-queries returning an 'id in' domain. bzr revid: tde@openerp.com-20121127153432-3ttqm8pmgk3l7a77 --- openerp/osv/fields.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openerp/osv/fields.py b/openerp/osv/fields.py index c8cf5909ecc..41a15b757e3 100644 --- a/openerp/osv/fields.py +++ b/openerp/osv/fields.py @@ -67,6 +67,7 @@ class _column(object): """ _classic_read = True _classic_write = True + _auto_join = False _prefetch = True _properties = False _type = 'unknown' From 8163c0027b5935e5d1903a78be901a3c519e42f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 27 Nov 2012 17:06:27 +0100 Subject: [PATCH 09/80] [IMP] [TEST] orm: updated order_by generation by adding the root table name to the order by condition generated based on self._order. Added a mockup of where_calc in test_expression, to be able to track generated Query objects. Added a first test on a one2many query, to track current behavior. bzr revid: tde@openerp.com-20121127160627-qs1tupvgr8ypexoo --- openerp/addons/base/tests/test_expression.py | 81 +++++++++++++++++++- openerp/osv/orm.py | 12 ++- 2 files changed, 90 insertions(+), 3 deletions(-) diff --git a/openerp/addons/base/tests/test_expression.py b/openerp/addons/base/tests/test_expression.py index 5690cff443a..0f9241bce86 100644 --- a/openerp/addons/base/tests/test_expression.py +++ b/openerp/addons/base/tests/test_expression.py @@ -1,10 +1,21 @@ import unittest2 - +from openerp.osv.orm import BaseModel import openerp.tests.common as common + class test_expression(common.TransactionCase): - def test_in_not_in_m2m(self): + def _reinit_mock(self): + self.query_list = list() + + def _mock_base_model_where_calc(self, model, *args, **kwargs): + """ Mock build_email to be able to test its values. Store them into + some internal variable for latter processing. """ + self.query_list.append(self._base_model_where_calc(model, *args, **kwargs)) + # return the lastly stored query, the one the ORM wants to perform + return self.query_list[-1] + + def test_00_in_not_in_m2m(self): registry, cr, uid = self.registry, self.cr, self.uid @@ -70,3 +81,69 @@ class test_expression(common.TransactionCase): # self.assertTrue(a not in with_any_other_than_a, "Search for category_id with any other than cat_a failed (1).") # self.assertTrue(ab in with_any_other_than_a, "Search for category_id with any other than cat_a failed (2).") + def test_10_auto_join(self): + registry, cr, uid = self.registry, self.cr, self.uid + + # Mock BaseModel._where_calc(), to be able to proceed to some tests about generated expression + self._reinit_mock() + self._base_model_where_calc = BaseModel._where_calc + BaseModel._where_calc = lambda model, cr, uid, args, context: self._mock_base_model_where_calc(model, cr, uid, args, context) + + # Get models + partner_obj = registry('res.partner') + state_obj = registry('res.country.state') + bank_obj = registry('res.partner.bank') + + # Get test columns + state_id_col = partner_obj._columns.get('state_id') # many2one on res.partner to res.country.state + child_ids_col = partner_obj._columns.get('child_ids') # one2many on res.partner to res.partner + bank_ids_col = partner_obj._columns.get('bank_ids') # one2many on res.partner to res.partner.bank + country_id_col = state_obj._columns.get('country_id') # many2one on res.country.state on res.country + + # Get the first bank account type to be able to create a res.partner.bank + bank_type = bank_obj._bank_type_get(cr, uid)[0] + + # Create demo data: partners and bank object + p_a = partner_obj.create(cr, uid, {'name': 'test__A'}) + p_b = partner_obj.create(cr, uid, {'name': 'test__B'}) + p_aa = partner_obj.create(cr, uid, {'name': 'test__AA', 'parent_id': p_a}) + p_ab = partner_obj.create(cr, uid, {'name': 'test__AB', 'parent_id': p_a}) + b_a = bank_obj.create(cr, uid, {'name': '__bank_test_a', 'state': bank_type[0], 'partner_id': p_a, 'acc_number': '1234'}) + + # ---------------------------------------- + # Test2: one2many + # ---------------------------------------- + + name_test = 'test_a' + + # Do: one2many without _auto_join + self._reinit_mock() + partner_ids = partner_obj.search(cr, uid, [('bank_ids.name', 'like', name_test)]) + # Test result + self.assertEqual(set(partner_ids), set([p_a]), 'one2many without join failed') + # Test produced queries + self.assertEqual(len(self.query_list), 3, + "_auto_join off: ('bank_ids.name', 'like', '..') should produce 3 queries (1 in res_partner_bank, 1 on res_partner with active, 1 on res_partner)") + sql_query = self.query_list[0].get_sql() + self.assertIn('res_partner_bank', sql_query[0], "_auto_join off: ('bank_ids.name', 'like', '..') first query should be done in res_partner_bank") + self.assertIn('(res_partner_bank."name" like %s)', sql_query[1], "_auto_join off: ('bank_ids.name', 'like', '..') first query incorrect where condition") + self.assertEqual(set(['%' + name_test + '%']), set(sql_query[2]), "_auto_join off: ('bank_ids.name', 'like', '..') first query incorrect parameter") + sql_query = self.query_list[2].get_sql() + self.assertIn('res_partner', sql_query[0], "_auto_join off: ('bank_ids.name', 'like', '..') third query should be done in res_partner") + self.assertIn('(res_partner."id" in (%s))', sql_query[1], "_auto_join off: ('bank_ids.name', 'like', '..') third query incorrect where condition") + self.assertEqual(set([p_a]), set(sql_query[2]), "_auto_join off: ('bank_ids.name', 'like', '..') third query incorrect parameter") + + # ---------------------------------------- + # Test2: many2one + # ---------------------------------------- + + # ---------------------------------------- + # Test2: more complex tests + # ---------------------------------------- + + # Remove mocks and modifications + bank_ids_col._auto_join = False + BaseModel._where_calc = self._base_model_where_calc + +if __name__ == '__main__': + unittest2.main() diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 6a1f917caa1..4120168cc41 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -4712,7 +4712,17 @@ class BaseModel(object): :raise" except_orm in case order_spec is malformed """ - order_by_clause = self._order + def _split_order(order, table): + """ from name, id asc make "table"."id", "table."."name" asc """ + order_list = [] + for elem in order.split(','): + subelems = elem.strip().split(' ') + if len(subelems) == 1: + subelems.append(' ') + order_list.append('"%s"."%s" %s' % (table, subelems[0], subelems[1])) + return order_list + + order_by_clause = ','.join(_split_order(self._order, self._table)) if order_spec: order_by_elements = [] self._check_qorder(order_spec) From 53efed664d44560ce9ba507eb08622594622164b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 27 Nov 2012 17:13:54 +0100 Subject: [PATCH 10/80] [IMP] expression.py: _leaf_to_sql now uses a table_alias instead of table._table for query generation. Currently table_alias equals table._table, so the behavior has not changed. bzr revid: tde@openerp.com-20121127161354-fskwsmfvx3j8nto6 --- openerp/osv/expression.py | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/openerp/osv/expression.py b/openerp/osv/expression.py index 58f9defccd4..31a10d508f9 100644 --- a/openerp/osv/expression.py +++ b/openerp/osv/expression.py @@ -724,6 +724,8 @@ class expression(object): assert leaf in (TRUE_LEAF, FALSE_LEAF) or left in table._all_columns \ or left in MAGIC_COLUMNS, "Invalid field %r in domain term %r" % (left, leaf) + table_alias = table._table + if leaf == TRUE_LEAF: query = 'TRUE' params = [] @@ -733,7 +735,7 @@ class expression(object): params = [] elif operator == 'inselect': - query = '(%s."%s" in (%s))' % (table._table, left, right[0]) + query = '(%s."%s" in (%s))' % (table_alias, left, right[0]) params = right[1] elif operator in ['in', 'not in']: @@ -745,7 +747,7 @@ class expression(object): r = 'NOT NULL' if right else 'NULL' else: r = 'NULL' if right else 'NOT NULL' - query = '(%s."%s" IS %s)' % (table._table, left, r) + query = '(%s."%s" IS %s)' % (table_alias, left, r) params = [] elif isinstance(right, (list, tuple)): params = right[:] @@ -760,34 +762,34 @@ class expression(object): instr = ','.join(['%s'] * len(params)) else: instr = ','.join([table._columns[left]._symbol_set[0]] * len(params)) - query = '(%s."%s" %s (%s))' % (table._table, left, operator, instr) + query = '(%s."%s" %s (%s))' % (table_alias, left, operator, instr) else: # The case for (left, 'in', []) or (left, 'not in', []). query = 'FALSE' if operator == 'in' else 'TRUE' if check_nulls and operator == 'in': - query = '(%s OR %s."%s" IS NULL)' % (query, table._table, left) + query = '(%s OR %s."%s" IS NULL)' % (query, table_alias, left) elif not check_nulls and operator == 'not in': - query = '(%s OR %s."%s" IS NULL)' % (query, table._table, left) + query = '(%s OR %s."%s" IS NULL)' % (query, table_alias, left) elif check_nulls and operator == 'not in': - query = '(%s AND %s."%s" IS NOT NULL)' % (query, table._table, left) # needed only for TRUE. + query = '(%s AND %s."%s" IS NOT NULL)' % (query, table_alias, left) # needed only for TRUE. else: # Must not happen raise ValueError("Invalid domain term %r" % (leaf,)) elif right == False and (left in table._columns) and table._columns[left]._type == "boolean" and (operator == '='): - query = '(%s."%s" IS NULL or %s."%s" = false )' % (table._table, left, table._table, left) + query = '(%s."%s" IS NULL or %s."%s" = false )' % (table_alias, left, table_alias, left) params = [] elif (right is False or right is None) and (operator == '='): - query = '%s."%s" IS NULL ' % (table._table, left) + query = '%s."%s" IS NULL ' % (table_alias, left) params = [] elif right == False and (left in table._columns) and table._columns[left]._type == "boolean" and (operator == '!='): - query = '(%s."%s" IS NOT NULL and %s."%s" != false)' % (table._table, left, table._table, left) + query = '(%s."%s" IS NOT NULL and %s."%s" != false)' % (table_alias, left, table_alias, left) params = [] elif (right is False or right is None) and (operator == '!='): - query = '%s."%s" IS NOT NULL' % (table._table, left) + query = '%s."%s" IS NOT NULL' % (table_alias, left) params = [] elif (operator == '=?'): @@ -800,7 +802,7 @@ class expression(object): query, params = self.__leaf_to_sql((left, '=', right), table) elif left == 'id': - query = '%s.id %s %%s' % (table._table, operator) + query = '%s.id %s %%s' % (table_alias, operator) params = right else: @@ -810,11 +812,11 @@ class expression(object): if left in table._columns: format = need_wildcard and '%s' or table._columns[left]._symbol_set[0] if self.has_unaccent and sql_operator in ('ilike', 'not ilike'): - query = '(unaccent(%s."%s") %s unaccent(%s))' % (table._table, left, sql_operator, format) + query = '(unaccent(%s."%s") %s unaccent(%s))' % (table_alias, left, sql_operator, format) else: - query = '(%s."%s" %s %s)' % (table._table, left, sql_operator, format) + query = '(%s."%s" %s %s)' % (table_alias, left, sql_operator, format) elif left in MAGIC_COLUMNS: - query = "(%s.\"%s\" %s %%s)" % (table._table, left, sql_operator) + query = "(%s.\"%s\" %s %%s)" % (table_alias, left, sql_operator) params = right else: # Must not happen raise ValueError("Invalid field %r in domain term %r" % (left, leaf)) @@ -833,7 +835,7 @@ class expression(object): params = table._columns[left]._symbol_set[1](right) if add_null: - query = '(%s OR %s."%s" IS NULL)' % (query, table._table, left) + query = '(%s OR %s."%s" IS NULL)' % (query, table_alias, left) if isinstance(params, basestring): params = [params] From 48138c9d26a535bce3100b2d595177e437b75a16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 27 Nov 2012 17:30:14 +0100 Subject: [PATCH 11/80] [IMP] [DOC] expression.py: renamed field_obj to relational_table, that make more sens to me. Added comments in the code to understand the algorithm. Please note that currently, no logic has been altered. bzr revid: tde@openerp.com-20121127163014-5ww5mre6j7opmzp1 --- openerp/osv/expression.py | 93 ++++++++++++++++++++++++++++----------- 1 file changed, 68 insertions(+), 25 deletions(-) diff --git a/openerp/osv/expression.py b/openerp/osv/expression.py index 31a10d508f9..22e49d42b8a 100644 --- a/openerp/osv/expression.py +++ b/openerp/osv/expression.py @@ -417,7 +417,23 @@ class expression(object): return ['"%s"' % item for item in self.table_aliases] def parse(self, cr, uid, exp, table, context): - """ transform the leaves of the expression """ + """ Transform the leaves of the expression + + For each element in the expression + 1. validity check: operator or True / False or a valid leaf + 2. TDE FIXME: TO COMPLETE + + Some var explanation for those who have 2 bytes of brain cache memory like me and that cannot remember 32^16 similar variable names. + :var obj working_table: table object, table containing the field (the name provided in the left operand) + :var list field_path: left operand seen as a path (foo.bar -> [foo, bar]) + :var string field_table: working_table._table + :var obj relational_table: relational table of a field (field._obj) + ex: res_partner.bank_ids -> res_partner_bank + :var obj field_obj: deleted var, now renamed to relational_table + + :param exp: expression (domain) + :param table: table object + """ self.__exp = exp self.root_table = table self._add_table_alias(table._table, table) @@ -445,7 +461,7 @@ class expression(object): return ids + recursive_children(ids2, model, parent_field) return [(left, 'in', recursive_children(ids, left_model, parent or left_model._parent_name))] - def to_ids(value, field_obj): + def to_ids(value, relational_table): """Normalize a single id or name, or a list of those, into a list of ids""" names = [] if isinstance(value, basestring): @@ -453,7 +469,7 @@ class expression(object): if value and isinstance(value, (tuple, list)) and isinstance(value[0], basestring): names = value if names: - return flatten([[x[0] for x in field_obj.name_search(cr, uid, n, [], 'ilike', context=context, limit=None)] \ + return flatten([[x[0] for x in relational_table.name_search(cr, uid, n, [], 'ilike', context=context, limit=None)] \ for n in names]) elif isinstance(value, (int, long)): return [value] @@ -462,11 +478,16 @@ class expression(object): i = -1 while i + 1 < len(self.__exp): i += 1 + + # 0 Preparation + # - Check validity of current element of expression (operator OR True/False leaf OR leaf) + # - Normalize the leaf's operator + # - Set working variables + + # check validity e = self.__exp[i] if is_operator(e) or e == TRUE_LEAF or e == FALSE_LEAF: continue - - # check if the expression is valid if not is_leaf(e): raise ValueError("Invalid term %r in domain expression %r" % (e, exp)) @@ -475,11 +496,13 @@ class expression(object): self.__exp[i] = e left, operator, right = e + # working variables working_table = table # The table containing the field (the name provided in the left operand) field_path = left.split('.', 1) - # If the field is _inherits'd, search for the working_table, - # and extract the field. + # 1 Handle inherits fields: replace by a join + # - Search for the working_table and extract the field + field = None if field_path[0] in table._inherit_fields: while True: @@ -496,6 +519,11 @@ class expression(object): else: field = working_table._columns.get(field_path[0]) + # 2 Field not found + # - ('id', 'child_of', ids) and continue the processing OR + # - field in magic columns (ex: id) and continue the processing OR + # - raise an error + if not field: if left == 'id' and operator == 'child_of': ids2 = to_ids(right, table) @@ -509,14 +537,29 @@ class expression(object): raise ValueError("Invalid field %r in domain expression %r" % (left, exp)) continue - field_obj = table.pool.get(field._obj) + # 3 Field found + # - get relational table, existing for relational fields + # - if domain is a path (ex: ('partner_id.name', '=', 'foo')): + # replace all the expression by a normalized equivalent domain + # - find the related ids: partner_id.name='foo' -> res_partner.search(('name', '=', 'foo'))) + # - many2one: leaf becomes directly ('partner_id', 'in', 'partner_ids') + # - one2many: + # - search on current table where partner_id is in partner_ids + # - leaf becomes ('id', 'in', ids) + # - get out of current leaf is field is not a property field + # - if domain is not a path + # - handle function fields + # - handle one2many, many2many and many2one fields + # - other fields: handle datetime and translatable fields + + relational_table = table.pool.get(field._obj) if len(field_path) > 1: if field._type == 'many2one': - right = field_obj.search(cr, uid, [(field_path[1], operator, right)], context=context) + right = relational_table.search(cr, uid, [(field_path[1], operator, right)], context=context) self.__exp[i] = (field_path[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, [(field_path[1], operator, right)], context=context) + right = relational_table.search(cr, uid, [(field_path[1], operator, right)], context=context) right1 = table.search(cr, uid, [(field_path[0], 'in', right)], context=dict(context, active_test=False)) self.__exp[i] = ('id', 'in', right1) @@ -552,9 +595,9 @@ class expression(object): elif field._type == 'one2many': # Applying recursivity on field(one2many) if operator == 'child_of': - ids2 = to_ids(right, field_obj) + ids2 = to_ids(right, relational_table) if field._obj != working_table._name: - dom = child_of_domain(left, ids2, field_obj, prefix=field._obj) + dom = child_of_domain(left, ids2, relational_table, prefix=field._obj) else: dom = child_of_domain('id', ids2, working_table, parent=left) self.__exp = self.__exp[:i] + dom + self.__exp[i + 1:] @@ -564,7 +607,7 @@ class expression(object): if right is not False: if isinstance(right, basestring): - ids2 = [x[0] for x in field_obj.name_search(cr, uid, right, [], operator, context=context, limit=None)] + ids2 = [x[0] for x in relational_table.name_search(cr, uid, right, [], operator, context=context, limit=None)] if ids2: operator = 'in' else: @@ -578,7 +621,7 @@ class expression(object): call_null = False self.__exp[i] = FALSE_LEAF else: - ids2 = select_from_where(cr, field._fields_id, field_obj._table, 'id', ids2, operator) + ids2 = select_from_where(cr, field._fields_id, relational_table._table, 'id', ids2, operator) if ids2: call_null = False o2m_op = 'not in' if operator in NEGATIVE_TERM_OPERATORS else 'in' @@ -586,26 +629,26 @@ class expression(object): if call_null: o2m_op = 'in' if operator in NEGATIVE_TERM_OPERATORS else 'not in' - self.__exp[i] = ('id', o2m_op, select_distinct_from_where_not_null(cr, field._fields_id, field_obj._table)) + self.__exp[i] = ('id', o2m_op, select_distinct_from_where_not_null(cr, field._fields_id, relational_table._table)) elif field._type == 'many2many': rel_table, rel_id1, rel_id2 = field._sql_names(working_table) #FIXME if operator == 'child_of': def _rec_convert(ids): - if field_obj == table: + if relational_table == table: return ids return select_from_where(cr, rel_id1, rel_table, rel_id2, ids, operator) - ids2 = to_ids(right, field_obj) - dom = child_of_domain('id', ids2, field_obj) - ids2 = field_obj.search(cr, uid, dom, context=context) + ids2 = to_ids(right, relational_table) + dom = child_of_domain('id', ids2, relational_table) + ids2 = relational_table.search(cr, uid, dom, context=context) self.__exp[i] = ('id', 'in', _rec_convert(ids2)) else: call_null_m2m = True if right is not False: if isinstance(right, basestring): - res_ids = [x[0] for x in field_obj.name_search(cr, uid, right, [], operator, context=context)] + res_ids = [x[0] for x in relational_table.name_search(cr, uid, right, [], operator, context=context)] if res_ids: operator = 'in' else: @@ -631,14 +674,14 @@ class expression(object): elif field._type == 'many2one': if operator == 'child_of': - ids2 = to_ids(right, field_obj) + ids2 = to_ids(right, relational_table) if field._obj != working_table._name: - dom = child_of_domain(left, ids2, field_obj, prefix=field._obj) + dom = child_of_domain(left, ids2, relational_table, prefix=field._obj) else: dom = child_of_domain('id', ids2, working_table, parent=left) self.__exp = self.__exp[:i] + dom + self.__exp[i + 1:] else: - def _get_expression(field_obj, cr, uid, left, right, operator, context=None): + def _get_expression(relational_table, cr, uid, left, right, operator, context=None): if context is None: context = {} c = context.copy() @@ -653,14 +696,14 @@ class expression(object): operator = dict_op[operator] elif isinstance(right, list) and operator in ['!=', '=']: # for domain (FIELD,'=',['value1','value2']) operator = dict_op[operator] - res_ids = [x[0] for x in field_obj.name_search(cr, uid, right, [], operator, limit=None, context=c)] + res_ids = [x[0] for x in relational_table.name_search(cr, uid, right, [], operator, limit=None, context=c)] if operator in NEGATIVE_TERM_OPERATORS: res_ids.append(False) # TODO this should not be appended if False was in 'right' return (left, 'in', res_ids) # resolve string-based m2o criterion into IDs if isinstance(right, basestring) or \ right and isinstance(right, (tuple, list)) and all(isinstance(item, basestring) for item in right): - self.__exp[i] = _get_expression(field_obj, cr, uid, left, right, operator, context=context) + self.__exp[i] = _get_expression(relational_table, cr, uid, left, right, operator, context=context) else: # right == [] or right == False and all other cases are handled by __leaf_to_sql() pass From 07f26441bbce36885de14fadf893f03ce7736dd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 27 Nov 2012 17:37:58 +0100 Subject: [PATCH 12/80] [IMP] expression.parse: improved code about extracting inherits\'d fields. I do not like while True statements, I prefer having a clear condition. Behavior should still be the same as before. bzr revid: tde@openerp.com-20121127163758-tddhbglygcbkvzed --- openerp/osv/expression.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/openerp/osv/expression.py b/openerp/osv/expression.py index 22e49d42b8a..d2de370a459 100644 --- a/openerp/osv/expression.py +++ b/openerp/osv/expression.py @@ -500,24 +500,21 @@ class expression(object): working_table = table # The table containing the field (the name provided in the left operand) field_path = left.split('.', 1) - # 1 Handle inherits fields: replace by a join - # - Search for the working_table and extract the field + # 1 Extract field + # - Try to directly extract the field + # - Handle inherits fields: replace by a join, find the final new + # working table and extract the field - field = None + field = working_table._columns.get(field_path[0]) if field_path[0] in table._inherit_fields: - while True: - field = working_table._columns.get(field_path[0]) - if field: - self.leaf_to_table[i] = working_table - break + while not field: next_table = working_table.pool.get(working_table._inherit_fields[field_path[0]][0]) if not self._has_table_alias(next_table._table): self.joins.append('%s."%s"=%s."%s"' % (next_table._table, 'id', working_table._table, working_table._inherits[next_table._name])) self._add_table_alias(next_table._table, next_table) working_table = next_table - # Or (try to) directly extract the field. - else: - field = working_table._columns.get(field_path[0]) + field = working_table._columns.get(field_path[0]) + self.leaf_to_table[i] = working_table # 2 Field not found # - ('id', 'child_of', ids) and continue the processing OR From 873d6cd5b13118a6ebc7c1e1906d8c79ca196ae3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 27 Nov 2012 17:45:31 +0100 Subject: [PATCH 13/80] [CLEAN] expression.py: self.__exp -> self.exp, because too much underscores kill my eyes. Added a bit of comments. Please note that the behavior has still not changed. bzr revid: tde@openerp.com-20121127164531-hbsyhshmefdw0f93 --- openerp/osv/expression.py | 69 ++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/openerp/osv/expression.py b/openerp/osv/expression.py index d2de370a459..1a2fd0cc0ca 100644 --- a/openerp/osv/expression.py +++ b/openerp/osv/expression.py @@ -385,14 +385,14 @@ class expression(object): self.table_aliases_mapping = {} self.joins = [] self.root_table = None - # assign self.__exp with the normalized, parsed domain. + # assign self.exp with the normalized, parsed domain. self.parse(cr, uid, distribute_not(normalize(exp)), table, context) # TDE note: this seems not to be used anymore, commenting # # TODO used only for osv_memory # @property # def exp(self): - # return self.__exp[:] + # return self.exp[:] def _has_table_alias(self, alias): return alias in self.table_aliases @@ -434,7 +434,7 @@ class expression(object): :param exp: expression (domain) :param table: table object """ - self.__exp = exp + self.exp = exp self.root_table = table self._add_table_alias(table._table, table) @@ -476,7 +476,7 @@ class expression(object): return list(value) i = -1 - while i + 1 < len(self.__exp): + while i + 1 < len(self.exp): i += 1 # 0 Preparation @@ -485,7 +485,7 @@ class expression(object): # - Set working variables # check validity - e = self.__exp[i] + e = self.exp[i] if is_operator(e) or e == TRUE_LEAF or e == FALSE_LEAF: continue if not is_leaf(e): @@ -493,7 +493,7 @@ class expression(object): # normalize the leaf's operator e = normalize_leaf(*e) - self.__exp[i] = e + self.exp[i] = e left, operator, right = e # working variables @@ -517,15 +517,16 @@ class expression(object): self.leaf_to_table[i] = working_table # 2 Field not found - # - ('id', 'child_of', ids) and continue the processing OR - # - field in magic columns (ex: id) and continue the processing OR + # - ('id', 'child_of', ids): replace the leaf by a computed domain + # after searching and continue the processing OR + # - field in magic columns (ex: id): continue the processing OR # - raise an error if not field: if left == 'id' and operator == 'child_of': ids2 = to_ids(right, table) dom = child_of_domain(left, ids2, working_table) - self.__exp = self.__exp[:i] + dom + self.__exp[i + 1:] + self.exp = self.exp[:i] + dom + self.exp[i + 1:] else: # field could not be found in model columns, it's probably invalid, unless # it's one of the _log_access special fields @@ -553,12 +554,12 @@ class expression(object): if len(field_path) > 1: if field._type == 'many2one': right = relational_table.search(cr, uid, [(field_path[1], operator, right)], context=context) - self.__exp[i] = (field_path[0], 'in', right) + self.exp[i] = (field_path[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 = relational_table.search(cr, uid, [(field_path[1], operator, right)], context=context) right1 = table.search(cr, uid, [(field_path[0], 'in', right)], context=dict(context, active_test=False)) - self.__exp[i] = ('id', 'in', right1) + self.exp[i] = ('id', 'in', right1) if not isinstance(field, fields.property): continue @@ -568,7 +569,7 @@ class expression(object): if not field._fnct_search: # the function field doesn't provide a search function and doesn't store # values in the database, so we must ignore it : we generate a dummy leaf - self.__exp[i] = TRUE_LEAF + self.exp[i] = TRUE_LEAF _logger.error( "The field '%s' (%s) can not be searched: non-stored " "function field without fnct_search", @@ -577,16 +578,16 @@ class expression(object): if _logger.isEnabledFor(logging.DEBUG): _logger.debug(''.join(traceback.format_stack())) else: - subexp = field.search(cr, uid, table, left, [self.__exp[i]], context=context) + subexp = field.search(cr, uid, table, left, [self.exp[i]], context=context) if not subexp: - self.__exp[i] = TRUE_LEAF + self.exp[i] = TRUE_LEAF else: # we assume that the expression is valid # we create a dummy leaf for forcing the parsing of the resulting expression - self.__exp[i] = AND_OPERATOR - self.__exp.insert(i + 1, TRUE_LEAF) + self.exp[i] = AND_OPERATOR + self.exp.insert(i + 1, TRUE_LEAF) for j, se in enumerate(subexp): - self.__exp.insert(i + 2 + j, se) + self.exp.insert(i + 2 + j, se) # else, the value of the field is store in the database, so we search on it elif field._type == 'one2many': @@ -597,7 +598,7 @@ class expression(object): dom = child_of_domain(left, ids2, relational_table, prefix=field._obj) else: dom = child_of_domain('id', ids2, working_table, parent=left) - self.__exp = self.__exp[:i] + dom + self.__exp[i + 1:] + self.exp = self.exp[:i] + dom + self.exp[i + 1:] else: call_null = True @@ -616,17 +617,17 @@ class expression(object): if operator in ['like', 'ilike', 'in', '=']: #no result found with given search criteria call_null = False - self.__exp[i] = FALSE_LEAF + self.exp[i] = FALSE_LEAF else: ids2 = select_from_where(cr, field._fields_id, relational_table._table, 'id', ids2, operator) if ids2: call_null = False o2m_op = 'not in' if operator in NEGATIVE_TERM_OPERATORS else 'in' - self.__exp[i] = ('id', o2m_op, ids2) + self.exp[i] = ('id', o2m_op, ids2) if call_null: o2m_op = 'in' if operator in NEGATIVE_TERM_OPERATORS else 'not in' - self.__exp[i] = ('id', o2m_op, select_distinct_from_where_not_null(cr, field._fields_id, relational_table._table)) + self.exp[i] = ('id', o2m_op, select_distinct_from_where_not_null(cr, field._fields_id, relational_table._table)) elif field._type == 'many2many': rel_table, rel_id1, rel_id2 = field._sql_names(working_table) @@ -640,7 +641,7 @@ class expression(object): ids2 = to_ids(right, relational_table) dom = child_of_domain('id', ids2, relational_table) ids2 = relational_table.search(cr, uid, dom, context=context) - self.__exp[i] = ('id', 'in', _rec_convert(ids2)) + self.exp[i] = ('id', 'in', _rec_convert(ids2)) else: call_null_m2m = True if right is not False: @@ -657,17 +658,17 @@ class expression(object): if operator in ['like', 'ilike', 'in', '=']: #no result found with given search criteria call_null_m2m = False - self.__exp[i] = FALSE_LEAF + self.exp[i] = FALSE_LEAF else: operator = 'in' # operator changed because ids are directly related to main object else: call_null_m2m = False m2m_op = 'not in' if operator in NEGATIVE_TERM_OPERATORS else 'in' - self.__exp[i] = ('id', m2m_op, select_from_where(cr, rel_id1, rel_table, rel_id2, res_ids, operator) or [0]) + self.exp[i] = ('id', m2m_op, select_from_where(cr, rel_id1, rel_table, rel_id2, res_ids, operator) or [0]) if call_null_m2m: m2m_op = 'in' if operator in NEGATIVE_TERM_OPERATORS else 'not in' - self.__exp[i] = ('id', m2m_op, select_distinct_from_where_not_null(cr, rel_id1, rel_table)) + self.exp[i] = ('id', m2m_op, select_distinct_from_where_not_null(cr, rel_id1, rel_table)) elif field._type == 'many2one': if operator == 'child_of': @@ -676,7 +677,7 @@ class expression(object): dom = child_of_domain(left, ids2, relational_table, prefix=field._obj) else: dom = child_of_domain('id', ids2, working_table, parent=left) - self.__exp = self.__exp[:i] + dom + self.__exp[i + 1:] + self.exp = self.exp[:i] + dom + self.exp[i + 1:] else: def _get_expression(relational_table, cr, uid, left, right, operator, context=None): if context is None: @@ -700,7 +701,7 @@ class expression(object): # resolve string-based m2o criterion into IDs if isinstance(right, basestring) or \ right and isinstance(right, (tuple, list)) and all(isinstance(item, basestring) for item in right): - self.__exp[i] = _get_expression(relational_table, cr, uid, left, right, operator, context=context) + self.exp[i] = _get_expression(relational_table, cr, uid, left, right, operator, context=context) else: # right == [] or right == False and all other cases are handled by __leaf_to_sql() pass @@ -708,16 +709,16 @@ class expression(object): else: # other field type # add the time part to datetime field when it's not there: - if field._type == 'datetime' and self.__exp[i][2] and len(self.__exp[i][2]) == 10: + if field._type == 'datetime' and self.exp[i][2] and len(self.exp[i][2]) == 10: - self.__exp[i] = list(self.__exp[i]) + self.exp[i] = list(self.exp[i]) if operator in ('>', '>='): - self.__exp[i][2] += ' 00:00:00' + self.exp[i][2] += ' 00:00:00' elif operator in ('<', '<='): - self.__exp[i][2] += ' 23:59:59' + self.exp[i][2] += ' 23:59:59' - self.__exp[i] = tuple(self.__exp[i]) + self.exp[i] = tuple(self.exp[i]) if field.translate: need_wildcard = operator in ('like', 'ilike', 'not like', 'not ilike') @@ -753,7 +754,7 @@ class expression(object): right, ] - self.__exp[i] = ('id', 'inselect', (subselect, params)) + self.exp[i] = ('id', 'inselect', (subselect, params)) def __leaf_to_sql(self, leaf, table): left, operator, right = leaf @@ -885,7 +886,7 @@ class expression(object): stack = [] params = [] # Process the domain from right to left, using a stack, to generate a SQL expression. - for i, e in reverse_enumerate(self.__exp): + for i, e in reverse_enumerate(self.exp): if is_leaf(e, internal=True): table = self.leaf_to_table.get(i, self.root_table) q, p = self.__leaf_to_sql(e, table) From 9b196638cb22a1b7d4e2a9a91b5ce1f1f2380e0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 27 Nov 2012 18:01:17 +0100 Subject: [PATCH 14/80] [IMP] expression: replaced field paths now use an alias.field leaf style to prepare the inclusion of joins. In leaf_to_sql, handle this case accordingly. bzr revid: tde@openerp.com-20121127170117-qlhfs0r1ned5uz3c --- openerp/osv/expression.py | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/openerp/osv/expression.py b/openerp/osv/expression.py index 1a2fd0cc0ca..947d392065e 100644 --- a/openerp/osv/expression.py +++ b/openerp/osv/expression.py @@ -536,7 +536,9 @@ class expression(object): continue # 3 Field found - # - get relational table, existing for relational fields + # - update working variables + # - get relational table that exists for relational fields + # - prepare the alias for tables, that can be the table name if only one-level # - if domain is a path (ex: ('partner_id.name', '=', 'foo')): # replace all the expression by a normalized equivalent domain # - find the related ids: partner_id.name='foo' -> res_partner.search(('name', '=', 'foo'))) @@ -545,21 +547,23 @@ class expression(object): # - search on current table where partner_id is in partner_ids # - leaf becomes ('id', 'in', ids) # - get out of current leaf is field is not a property field - # - if domain is not a path + # - if domain is not a path: handle some leaf replacement / tweaking # - handle function fields # - handle one2many, many2many and many2one fields # - other fields: handle datetime and translatable fields relational_table = table.pool.get(field._obj) + alias = working_table._table + if len(field_path) > 1: if field._type == 'many2one': right = relational_table.search(cr, uid, [(field_path[1], operator, right)], context=context) - self.exp[i] = (field_path[0], 'in', right) + self.exp[i] = (alias + '.' + field_path[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 = relational_table.search(cr, uid, [(field_path[1], operator, right)], context=context) right1 = table.search(cr, uid, [(field_path[0], 'in', right)], context=dict(context, active_test=False)) - self.exp[i] = ('id', 'in', right1) + self.exp[i] = (alias + '.id', 'in', right1) if not isinstance(field, fields.property): continue @@ -763,9 +767,20 @@ class expression(object): assert operator in (TERM_OPERATORS + ('inselect',)), \ "Invalid operator %r in domain term %r" % (operator, leaf) assert leaf in (TRUE_LEAF, FALSE_LEAF) or left in table._all_columns \ - or left in MAGIC_COLUMNS, "Invalid field %r in domain term %r" % (left, leaf) + or left in MAGIC_COLUMNS \ + or ('.' in left and self._has_table_alias(left.split('.')[0])), \ + "Invalid field %r in domain term %r" % (left, leaf) - table_alias = table._table + if not leaf in (TRUE_LEAF, FALSE_LEAF) and '.' in left: + # leaf still contains '.' -> should be aliases (alias.field) + # update table with alias, and left with field + leaf_path = left.split('.') + assert len(leaf_path) == 2, "Invalid leaf with alias %r in leaf %r" % (left, leaf) + table_alias = leaf_path[0] + table = self._get_table_from_alias(table_alias) + left = leaf_path[1] + else: + table_alias = table._table if leaf == TRUE_LEAF: query = 'TRUE' From 2fcaf2252cb5c815f29489828e7d536f1f8d425c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 27 Nov 2012 19:11:38 +0100 Subject: [PATCH 15/80] [IMP] mail_message model: now uses _auto_join. bzr revid: tde@openerp.com-20121127181138-1mdebb6ehal2r973 --- addons/mail/mail_message.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index ba6d101541c..4b2878a6bcd 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -63,7 +63,7 @@ class mail_message(osv.Model): # protection for `default_type` values leaking from menu action context (e.g. for invoices) if context and context.get('default_type') and context.get('default_type') not in self._columns['type'].selection: context = dict(context, default_type=None) - return super(mail_message, self).default_get(cr, uid, fields, context=context) + return super(mail_message, self).default_get(cr, uid, fields, context=context) def _shorten_name(self, name): if len(name) <= (self._message_record_name_length + 3): @@ -146,7 +146,7 @@ class mail_message(osv.Model): store=True, string='Message Record Name', help="Name get of the related document."), 'notification_ids': fields.one2many('mail.notification', 'message_id', - string='Notifications', + string='Notifications', _auto_join=True, help='Technical field holding the message notifications. Use notified_partner_ids to access notified partners.'), 'subject': fields.char('Subject'), 'date': fields.datetime('Date'), From 0544565a57db056005bb098ffc92513e5b084159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 28 Nov 2012 10:42:34 +0100 Subject: [PATCH 16/80] [REV] ir_ui_menu: temporarily set needaction_enabled column back to non-stored, to be able to use the branch on internal test database with needaction, without requiring too many hand-made modifications. bzr revid: tde@openerp.com-20121128094234-ux06ludum7ub59u5 --- openerp/addons/base/ir/ir_ui_menu.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openerp/addons/base/ir/ir_ui_menu.py b/openerp/addons/base/ir/ir_ui_menu.py index ef9ed3b7bf3..f236845dfc7 100644 --- a/openerp/addons/base/ir/ir_ui_menu.py +++ b/openerp/addons/base/ir/ir_ui_menu.py @@ -309,7 +309,8 @@ class ir_ui_menu(osv.osv): 'web_icon_data': fields.function(_get_image_icon, string='Web Icon Image', type='binary', readonly=True, store=True, multi='icon'), 'web_icon_hover_data': fields.function(_get_image_icon, string='Web Icon Image (hover)', type='binary', readonly=True, store=True, multi='icon'), 'needaction_enabled': fields.function(_get_needaction_enabled, - type='boolean', store=True, + type='boolean', + # store=True, string='Target model uses the need action mechanism', help='If the menu entry action is an act_window action, and if this action is related to a model that uses the need_action mechanism, this field is set to true. Otherwise, it is false.'), 'action': fields.function(_action, fnct_inv=_action_inv, From ac587f68fec384730e4fbb748cd3662659037660 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 28 Nov 2012 10:50:42 +0100 Subject: [PATCH 17/80] [IMP] expression.py: added support of _auto_join in parse. The purpose is to add join clauses to queries instead of replacing many2one or one2many fields by id in ... equivalent leafs. This should lessen the number of executed queries, as well as the time required for some queries like mailboxes queries. Updated the generation of full table aliases when generating SQL, to avoid having a table having it own name as alias. bzr revid: tde@openerp.com-20121128095042-x5rzgwydv0g9uy0m --- openerp/osv/expression.py | 47 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/openerp/osv/expression.py b/openerp/osv/expression.py index 947d392065e..4f2ae3989b9 100644 --- a/openerp/osv/expression.py +++ b/openerp/osv/expression.py @@ -403,7 +403,11 @@ class expression(object): def _get_full_alias(self, alias): if not self._get_table_from_alias(alias): return False - return '%s as %s' % (self._get_table_from_alias(alias)._table, alias) + table_name = self._get_table_from_alias(alias)._table + if table_name == alias: + return '"%s"' % alias + else: + return '"%s" as %s' % (table_name, alias) def _add_table_alias(self, alias, table): if not self._has_table_alias(alias): @@ -414,7 +418,7 @@ class expression(object): def get_tables(self): """ Returns the list of tables for SQL queries, like select from ... """ - return ['"%s"' % item for item in self.table_aliases] + return [self._get_full_alias(item) for item in self.table_aliases] def parse(self, cr, uid, exp, table, context): """ Transform the leaves of the expression @@ -540,6 +544,7 @@ class expression(object): # - get relational table that exists for relational fields # - prepare the alias for tables, that can be the table name if only one-level # - if domain is a path (ex: ('partner_id.name', '=', 'foo')): + # TDE TO ADD: once fully implemented, explain auto_join # replace all the expression by a normalized equivalent domain # - find the related ids: partner_id.name='foo' -> res_partner.search(('name', '=', 'foo'))) # - many2one: leaf becomes directly ('partner_id', 'in', 'partner_ids') @@ -555,6 +560,44 @@ class expression(object): relational_table = table.pool.get(field._obj) alias = working_table._table + while len(field_path) > 1 and field._auto_join: + if not field._type in ['many2one', 'one2many']: + raise '_auto_join attribute on something else than a many2one or one2many is currently not implemented... crashing' + + previous_alias = alias + alias = alias + '__' + field_path[0] + if not self._get_table_from_alias(alias): + self._add_table_alias(alias, relational_table) + if field._type == 'many2one': + self.joins.append('%s."%s"=%s."%s"' % (alias, 'id', previous_alias, field_path[0])) + elif field._type == 'one2many': + self.joins.append('%s."%s"=%s."%s"' % (alias, field._fields_id, previous_alias, 'id')) + self.exp[i] = (alias + '.' + field_path[1], self.exp[i][1], self.exp[i][2]) + + # udpate working variables + field_path = field_path[1].split('.', 1) + working_table = relational_table + field = working_table._columns.get(field_path[0]) + + if not field: + if left == 'id' and operator == 'child_of': + ids2 = to_ids(right, table) + dom = child_of_domain(left, ids2, working_table) + self.exp = self.exp[:i] + dom + self.exp[i + 1:] + else: + # field could not be found in model columns, it's probably invalid, unless + # it's one of the _log_access special fields + # TODO: make these fields explicitly available in self.columns instead! + if field_path[0] not in MAGIC_COLUMNS: + raise ValueError("Invalid field %r in domain expression %r" % (left, exp)) + break + + # moved on top + relational_table = table.pool.get(field._obj) + + if not field: + continue + if len(field_path) > 1: if field._type == 'many2one': right = relational_table.search(cr, uid, [(field_path[1], operator, right)], context=context) From 4be660369d6c00e53f5c0a102d13399916851a50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 28 Nov 2012 12:04:28 +0100 Subject: [PATCH 18/80] [DOC] ir_ui_menu: added some comments to the methods related to needaction. bzr revid: tde@openerp.com-20121128110428-lebsx4s3zlmgdhxj --- openerp/addons/base/ir/ir_ui_menu.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/openerp/addons/base/ir/ir_ui_menu.py b/openerp/addons/base/ir/ir_ui_menu.py index f236845dfc7..fa4e5b2b540 100644 --- a/openerp/addons/base/ir/ir_ui_menu.py +++ b/openerp/addons/base/ir/ir_ui_menu.py @@ -266,6 +266,8 @@ class ir_ui_menu(osv.osv): return res def _get_needaction_enabled(self, cr, uid, ids, field_names, args, context=None): + """ needaction_enabled: tell whether the menu has a related action + that uses the needaction mechanism. """ res = dict.fromkeys(ids, False) for menu in self.browse(cr, uid, ids, context=context): if menu.action and menu.action.type in ('ir.actions.act_window', 'ir.actions.client') and menu.action.res_model: @@ -275,6 +277,11 @@ class ir_ui_menu(osv.osv): return res def get_needaction_data(self, cr, uid, ids, context=None): + """ Return for each menu entry of ids : + - if it uses the needaction mechanism (needaction_enabled) + - the needaction counter of the related action, taking into account + the action domain + """ res = {} for menu in self.browse(cr, uid, ids, context=context): res[menu.id] = { @@ -310,7 +317,7 @@ class ir_ui_menu(osv.osv): 'web_icon_hover_data': fields.function(_get_image_icon, string='Web Icon Image (hover)', type='binary', readonly=True, store=True, multi='icon'), 'needaction_enabled': fields.function(_get_needaction_enabled, type='boolean', - # store=True, + # store=True, TDE TODO: uncomment after internal tests string='Target model uses the need action mechanism', help='If the menu entry action is an act_window action, and if this action is related to a model that uses the need_action mechanism, this field is set to true. Otherwise, it is false.'), 'action': fields.function(_action, fnct_inv=_action_inv, From 10ba4e5e606ace471ad8b43e9224c78cfcf033e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 28 Nov 2012 12:05:20 +0100 Subject: [PATCH 19/80] [IMP] [TMP] controller: search for all menus to have their needaction info, because currently needaction_enabled is not searchable (non stored function field). Revert this revision when the column is back as stored. bzr revid: tde@openerp.com-20121128110520-wijh1sr5qvsch7b8 --- addons/web/controllers/main.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 517ef242684..7658d3de0b8 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -1117,7 +1117,9 @@ class Menu(openerpweb.Controller): Menus = req.session.model('ir.ui.menu') if menu_ids == False: - menu_ids = Menus.search([('needaction_enabled', '=', True)], context=context) + menu_ids = Menus.search([], context=context) + # TDE FIXME: set needaction_enabled column to store, to enable this more limited search + # menu_ids = Menus.search([('needaction_enabled', '=', True)], context=context) menu_needaction_data = Menus.get_needaction_data(menu_ids, context) return menu_needaction_data From a853b9f2ad72136780d02402b01e107d2ece77dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 28 Nov 2012 15:00:42 +0100 Subject: [PATCH 20/80] [TEST] Added some tests about the _auto_join feature. bzr revid: tde@openerp.com-20121128140042-4r58wnzahk0io3lw --- openerp/addons/base/tests/test_expression.py | 277 +++++++++++++++++-- 1 file changed, 252 insertions(+), 25 deletions(-) diff --git a/openerp/addons/base/tests/test_expression.py b/openerp/addons/base/tests/test_expression.py index 0f9241bce86..1eb22ccba43 100644 --- a/openerp/addons/base/tests/test_expression.py +++ b/openerp/addons/base/tests/test_expression.py @@ -15,8 +15,19 @@ class test_expression(common.TransactionCase): # return the lastly stored query, the one the ORM wants to perform return self.query_list[-1] - def test_00_in_not_in_m2m(self): + def setUp(self): + super(test_expression, self).setUp() + # Mock BaseModel._where_calc(), to be able to proceed to some tests about generated expression + self._reinit_mock() + self._base_model_where_calc = BaseModel._where_calc + BaseModel._where_calc = lambda model, cr, uid, args, context: self._mock_base_model_where_calc(model, cr, uid, args, context) + def tearDown(self): + # Remove mocks + BaseModel._where_calc = self._base_model_where_calc + super(test_expression, self).tearDown() + + def test_00_in_not_in_m2m(self): registry, cr, uid = self.registry, self.cr, self.uid # Create 4 partners with no category, or one or two categories (out of two categories). @@ -84,34 +95,36 @@ class test_expression(common.TransactionCase): def test_10_auto_join(self): registry, cr, uid = self.registry, self.cr, self.uid - # Mock BaseModel._where_calc(), to be able to proceed to some tests about generated expression - self._reinit_mock() - self._base_model_where_calc = BaseModel._where_calc - BaseModel._where_calc = lambda model, cr, uid, args, context: self._mock_base_model_where_calc(model, cr, uid, args, context) - # Get models partner_obj = registry('res.partner') state_obj = registry('res.country.state') bank_obj = registry('res.partner.bank') # Get test columns - state_id_col = partner_obj._columns.get('state_id') # many2one on res.partner to res.country.state - child_ids_col = partner_obj._columns.get('child_ids') # one2many on res.partner to res.partner - bank_ids_col = partner_obj._columns.get('bank_ids') # one2many on res.partner to res.partner.bank - country_id_col = state_obj._columns.get('country_id') # many2one on res.country.state on res.country + partner_state_id_col = partner_obj._columns.get('state_id') # many2one on res.partner to res.country.state + partner_parent_id_col = partner_obj._columns.get('parent_id') # many2one on res.partner to res.partner + partner_child_ids_col = partner_obj._columns.get('child_ids') # one2many on res.partner to res.partner + partner_bank_ids_col = partner_obj._columns.get('bank_ids') # one2many on res.partner to res.partner.bank + state_country_id_col = state_obj._columns.get('country_id') # many2one on res.country.state on res.country # Get the first bank account type to be able to create a res.partner.bank bank_type = bank_obj._bank_type_get(cr, uid)[0] + # Get country/state data + country_us_id = registry('res.country').search(cr, uid, [('code', 'like', 'US')])[0] + state_ids = registry('res.country.state').search(cr, uid, [('country_id', '=', country_us_id)], limit=2) # Create demo data: partners and bank object - p_a = partner_obj.create(cr, uid, {'name': 'test__A'}) - p_b = partner_obj.create(cr, uid, {'name': 'test__B'}) - p_aa = partner_obj.create(cr, uid, {'name': 'test__AA', 'parent_id': p_a}) - p_ab = partner_obj.create(cr, uid, {'name': 'test__AB', 'parent_id': p_a}) - b_a = bank_obj.create(cr, uid, {'name': '__bank_test_a', 'state': bank_type[0], 'partner_id': p_a, 'acc_number': '1234'}) + p_a = partner_obj.create(cr, uid, {'name': 'test__A', 'state_id': state_ids[0]}) + p_b = partner_obj.create(cr, uid, {'name': 'test__B', 'state_id': state_ids[1]}) + p_aa = partner_obj.create(cr, uid, {'name': 'test__AA', 'parent_id': p_a, 'state_id': state_ids[0]}) + p_ab = partner_obj.create(cr, uid, {'name': 'test__AB', 'parent_id': p_a, 'state_id': state_ids[1]}) + p_ba = partner_obj.create(cr, uid, {'name': 'test__BA', 'parent_id': p_b, 'state_id': state_ids[0]}) + b_aa = bank_obj.create(cr, uid, {'name': '__bank_test_a', 'state': bank_type[0], 'partner_id': p_aa, 'acc_number': '1234'}) + b_ab = bank_obj.create(cr, uid, {'name': '__bank_test_b', 'state': bank_type[0], 'partner_id': p_ab, 'acc_number': '5678'}) + b_ba = bank_obj.create(cr, uid, {'name': '__bank_test_b', 'state': bank_type[0], 'partner_id': p_ba, 'acc_number': '9876'}) # ---------------------------------------- - # Test2: one2many + # Test1: one2many # ---------------------------------------- name_test = 'test_a' @@ -120,29 +133,243 @@ class test_expression(common.TransactionCase): self._reinit_mock() partner_ids = partner_obj.search(cr, uid, [('bank_ids.name', 'like', name_test)]) # Test result - self.assertEqual(set(partner_ids), set([p_a]), 'one2many without join failed') + self.assertEqual(set(partner_ids), set([p_aa]), + "_auto_join off: ('bank_ids.name', 'like', '..'): incorrect result") # Test produced queries self.assertEqual(len(self.query_list), 3, "_auto_join off: ('bank_ids.name', 'like', '..') should produce 3 queries (1 in res_partner_bank, 1 on res_partner with active, 1 on res_partner)") sql_query = self.query_list[0].get_sql() - self.assertIn('res_partner_bank', sql_query[0], "_auto_join off: ('bank_ids.name', 'like', '..') first query should be done in res_partner_bank") - self.assertIn('(res_partner_bank."name" like %s)', sql_query[1], "_auto_join off: ('bank_ids.name', 'like', '..') first query incorrect where condition") - self.assertEqual(set(['%' + name_test + '%']), set(sql_query[2]), "_auto_join off: ('bank_ids.name', 'like', '..') first query incorrect parameter") + self.assertIn('res_partner_bank', sql_query[0], + "_auto_join off: ('bank_ids.name', 'like', '..') first query incorrect main table") + self.assertIn('res_partner_bank."name" like %s', sql_query[1], + "_auto_join off: ('bank_ids.name', 'like', '..') first query incorrect where condition") + self.assertEqual(set(['%' + name_test + '%']), set(sql_query[2]), + "_auto_join off: ('bank_ids.name', 'like', '..') first query incorrect parameter") sql_query = self.query_list[2].get_sql() - self.assertIn('res_partner', sql_query[0], "_auto_join off: ('bank_ids.name', 'like', '..') third query should be done in res_partner") - self.assertIn('(res_partner."id" in (%s))', sql_query[1], "_auto_join off: ('bank_ids.name', 'like', '..') third query incorrect where condition") - self.assertEqual(set([p_a]), set(sql_query[2]), "_auto_join off: ('bank_ids.name', 'like', '..') third query incorrect parameter") + self.assertIn('res_partner', sql_query[0], + "_auto_join off: ('bank_ids.name', 'like', '..') third query incorrect main table") + self.assertIn('res_partner."id" in (%s)', sql_query[1], + "_auto_join off: ('bank_ids.name', 'like', '..') third query incorrect where condition") + self.assertEqual(set([p_aa]), set(sql_query[2]), + "_auto_join off: ('bank_ids.name', 'like', '..') third query incorrect parameter") + + # Do: cascaded one2many without _auto_join + self._reinit_mock() + partner_ids = partner_obj.search(cr, uid, [('child_ids.bank_ids.id', 'in', [b_aa, b_ba])]) + # Test result + self.assertEqual(set(partner_ids), set([p_a, p_b]), + "_auto_join off: ('child_ids.bank_ids.id', 'in', [..]): incorrect result") + # Test produced queries + self.assertEqual(len(self.query_list), 5, + "_auto_join off: ('child_ids.bank_ids.id', 'in', [..]) should produce 5 queries (1 in res_partner_bank, 2 on res_partner with active, 2 on res_partner, childs then parents)") + + # Do: one2many with _auto_join + partner_bank_ids_col._auto_join = True + self._reinit_mock() + partner_ids = partner_obj.search(cr, uid, [('bank_ids.name', 'like', 'test_a')]) + # Test result + self.assertEqual(set(partner_ids), set([p_aa]), + "_auto_join on: ('bank_ids.name', 'like', '..') incorrect result") + # # Test produced queries + self.assertEqual(len(self.query_list), 1, + "_auto_join on: ('bank_ids.name', 'like', '..') should produce 1 query") + sql_query = self.query_list[0].get_sql() + self.assertIn('"res_partner"', sql_query[0], + "_auto_join on: ('bank_ids.name', 'like', '..') query incorrect main table") + self.assertIn('"res_partner_bank" as res_partner__bank_ids', sql_query[0], + "_auto_join on: ('bank_ids.name', 'like', '..') query incorrect join") + self.assertIn('res_partner__bank_ids."name" like %s', sql_query[1], + "_auto_join on: ('bank_ids.name', 'like', '..') query incorrect where condition") + self.assertIn('res_partner__bank_ids."partner_id"=res_partner."id"', sql_query[1], + "_auto_join on: ('bank_ids.name', 'like', '..') query incorrect join condition") + self.assertEqual(set(['%' + name_test + '%']), set(sql_query[2]), + "_auto_join on: ('bank_ids.name', 'like', '..') query incorrect parameter") + + # Do: one2many with _auto_join, test final leaf is an id + self._reinit_mock() + partner_ids = partner_obj.search(cr, uid, [('bank_ids.id', 'in', [b_aa, b_ab])]) + # Test result + self.assertEqual(set(partner_ids), set([p_aa, p_ab]), + "_auto_join on: ('bank_ids.id', 'in', [..]) incorrect result") + # # Test produced queries + self.assertEqual(len(self.query_list), 1, + "_auto_join on: ('bank_ids.id', 'in', [..]) should produce 1 query") + sql_query = self.query_list[0].get_sql() + self.assertIn('"res_partner"', sql_query[0], + "_auto_join on: ('bank_ids.id', 'in', [..]) query incorrect main table") + self.assertIn('"res_partner_bank" as res_partner__bank_ids', sql_query[0], + "_auto_join on: ('bank_ids.id', 'in', [..]) query incorrect join") + self.assertIn('res_partner__bank_ids."id" in (%s,%s)', sql_query[1], + "_auto_join on: ('bank_ids.id', 'in', [..]) query incorrect where condition") + self.assertIn('res_partner__bank_ids."partner_id"=res_partner."id"', sql_query[1], + "_auto_join on: ('bank_ids.id', 'in', [..]) query incorrect join condition") + self.assertEqual(set([b_aa, b_ab]), set(sql_query[2]), + "_auto_join on: ('bank_ids.id', 'in', [..]) query incorrect parameter") + + # Do: 2 cascaded one2many with _auto_join, test final leaf is an id + partner_child_ids_col._auto_join = True + self._reinit_mock() + partner_ids = partner_obj.search(cr, uid, [('child_ids.bank_ids.id', 'in', [b_aa, b_ba])]) + # Test result + self.assertEqual(set(partner_ids), set([p_a, p_b]), + "_auto_join on: ('child_ids.bank_ids.id', 'not in', [..]): incorrect result") + # # Test produced queries + self.assertEqual(len(self.query_list), 1, + "_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) should produce 1 query") + sql_query = self.query_list[0].get_sql() + self.assertIn('"res_partner"', sql_query[0], + "_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) incorrect main table") + self.assertIn('"res_partner" as res_partner__child_ids', sql_query[0], + "_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect join") + self.assertIn('"res_partner_bank" as res_partner__child_ids__bank_ids', sql_query[0], + "_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect join") + self.assertIn('res_partner__child_ids__bank_ids."id" in (%s,%s)', sql_query[1], + "_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect where condition") + self.assertIn('res_partner__child_ids."parent_id"=res_partner."id"', sql_query[1], + "_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect join condition") + self.assertIn('res_partner__child_ids__bank_ids."partner_id"=res_partner__child_ids."id"', sql_query[1], + "_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect join condition") + self.assertEqual(set([b_aa, b_ba]), set(sql_query[2]), + "_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect parameter") # ---------------------------------------- # Test2: many2one # ---------------------------------------- + name_test = 'US' + + # Do: many2one without _auto_join + self._reinit_mock() + partner_ids = partner_obj.search(cr, uid, [('state_id.country_id.code', 'like', name_test)]) + # Test result: at least our added data + demo data + self.assertTrue(set([p_a, p_b, p_aa, p_ab, p_ba]).issubset(set(partner_ids)), + "_auto_join off: ('state_id.country_id.code', 'like', '..') incorrect result") + # Test produced queries + self.assertEqual(len(self.query_list), 3, + "_auto_join off: ('state_id.country_id.code', 'like', '..') should produce 3 queries (1 on res_country, 1 on res_country_state, 1 on res_partner)") + + # Do: many2one with 1 _auto_join on the first many2one + partner_state_id_col._auto_join = True + self._reinit_mock() + partner_ids = partner_obj.search(cr, uid, [('state_id.country_id.code', 'like', name_test)]) + # Test result: at least our added data + demo data + self.assertTrue(set([p_a, p_b, p_aa, p_ab, p_ba]).issubset(set(partner_ids)), + "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') incorrect result") + # Test produced queries + self.assertEqual(len(self.query_list), 2, + "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') should produce 2 query") + sql_query = self.query_list[0].get_sql() + self.assertIn('"res_country"', sql_query[0], + "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect main table") + self.assertIn('res_country."code" like %s', sql_query[1], + "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect where condition") + self.assertEqual(['%' + name_test + '%'], sql_query[2], + "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect parameter") + sql_query = self.query_list[1].get_sql() + self.assertIn('"res_partner"', sql_query[0], + "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect main table") + self.assertIn('"res_country_state" as res_partner__state_id', sql_query[0], + "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect join") + self.assertIn('res_partner__state_id."country_id" in (%s)', sql_query[1], + "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect where condition") + self.assertIn('(res_partner__state_id."id"=res_partner."state_id"', sql_query[1], + "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect join condition") + + # Do: many2one with 1 _auto_join on the second many2one + partner_state_id_col._auto_join = False + state_country_id_col._auto_join = True + self._reinit_mock() + partner_ids = partner_obj.search(cr, uid, [('state_id.country_id.code', 'like', name_test)]) + # Test result: at least our added data + demo data + self.assertTrue(set([p_a, p_b, p_aa, p_ab, p_ba]).issubset(set(partner_ids)), + "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') incorrect result") + # Test produced queries + self.assertEqual(len(self.query_list), 2, + "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') should produce 2 query") + # -- first query + sql_query = self.query_list[0].get_sql() + self.assertIn('"res_country_state"', sql_query[0], + "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect main table") + self.assertIn('"res_country" as res_country_state__country_id', sql_query[0], + "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect join") + self.assertIn('res_country_state__country_id."code" like %s', sql_query[1], + "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect where condition") + self.assertIn('res_country_state__country_id."id"=res_country_state."country_id"', sql_query[1], + "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect join condition") + self.assertEqual(['%' + name_test + '%'], sql_query[2], + "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect parameter") + # -- second query + sql_query = self.query_list[1].get_sql() + self.assertIn('"res_partner"', sql_query[0], + "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect main table") + self.assertIn('res_partner."state_id" in', sql_query[1], + "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect where condition") + + # Do: many2one with 2 _auto_join + partner_state_id_col._auto_join = True + state_country_id_col._auto_join = True + self._reinit_mock() + partner_ids = partner_obj.search(cr, uid, [('state_id.country_id.code', 'like', name_test)]) + # Test result: at least our added data + demo data + self.assertTrue(set([p_a, p_b, p_aa, p_ab, p_ba]).issubset(set(partner_ids)), + "_auto_join on: ('state_id.country_id.code', 'like', '..') incorrect result") + # Test produced queries + self.assertEqual(len(self.query_list), 1, + "_auto_join on: ('state_id.country_id.code', 'like', '..') should produce 1 query") + sql_query = self.query_list[0].get_sql() + self.assertIn('"res_partner"', sql_query[0], + "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect main table") + self.assertIn('"res_country_state" as res_partner__state_id', sql_query[0], + "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join") + self.assertIn('"res_country" as res_partner__state_id__country_id', sql_query[0], + "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join") + self.assertIn('res_partner__state_id__country_id."code" like %s', sql_query[1], + "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect where condition") + self.assertIn('res_partner__state_id."id"=res_partner."state_id"', sql_query[1], + "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join condition") + self.assertIn('res_partner__state_id__country_id."id"=res_partner__state_id."country_id"', sql_query[1], + "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join condition") + self.assertEqual(['%' + name_test + '%'], sql_query[2], + "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect parameter") + # ---------------------------------------- - # Test2: more complex tests + # Test3: result-based tests # ---------------------------------------- + partner_bank_ids_col._auto_join = False + partner_child_ids_col._auto_join = False + partner_state_id_col._auto_join = False + partner_parent_id_col._auto_join = False + state_country_id_col._auto_join = False + + # Do: ('child_ids.state_id.country_id.code', 'like', '..') without _auto_join + self._reinit_mock() + partner_ids = partner_obj.search(cr, uid, [('child_ids.state_id.country_id.code', 'like', name_test)]) + # Test result: at least our added data + demo data + self.assertTrue(set([p_a, p_b]).issubset(set(partner_ids)), + "_auto_join off: ('child_ids.state_id.country_id.code', 'like', '..') incorrect result") + # Test produced queries + self.assertEqual(len(self.query_list), 5, + "_auto_join off: ('child_ids.state_id.country_id.code', 'like', '..') number of queries incorrect") + + # Do: ('child_ids.state_id.country_id.code', 'like', '..') with _auto_join + partner_child_ids_col._auto_join = True + partner_state_id_col._auto_join = True + state_country_id_col._auto_join = True + self._reinit_mock() + partner_ids = partner_obj.search(cr, uid, [('child_ids.state_id.country_id.code', 'like', name_test)]) + # Test result: at least our added data + demo data + self.assertTrue(set([p_a, p_b]).issubset(set(partner_ids)), + "_auto_join on: ('child_ids.state_id.country_id.code', 'like', '..') incorrect result") + # Test produced queries + self.assertEqual(len(self.query_list), 1, + "_auto_join on: ('child_ids.state_id.country_id.code', 'like', '..') number of queries incorrect") + # Remove mocks and modifications - bank_ids_col._auto_join = False + partner_bank_ids_col._auto_join = False + partner_child_ids_col._auto_join = False + partner_state_id_col._auto_join = False + state_country_id_col._auto_join = False BaseModel._where_calc = self._base_model_where_calc if __name__ == '__main__': From 47fda8226445590d604a2899457a25e290eda8e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 28 Nov 2012 19:12:58 +0100 Subject: [PATCH 21/80] [FIX] expression.parse: fixed indentation error preventing leafs to be correctly replaced in auto_join. bzr revid: tde@openerp.com-20121128181258-cgmm7wqgv657iptb --- openerp/osv/expression.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/osv/expression.py b/openerp/osv/expression.py index 4f2ae3989b9..5fb18fb8cc1 100644 --- a/openerp/osv/expression.py +++ b/openerp/osv/expression.py @@ -572,7 +572,7 @@ class expression(object): self.joins.append('%s."%s"=%s."%s"' % (alias, 'id', previous_alias, field_path[0])) elif field._type == 'one2many': self.joins.append('%s."%s"=%s."%s"' % (alias, field._fields_id, previous_alias, 'id')) - self.exp[i] = (alias + '.' + field_path[1], self.exp[i][1], self.exp[i][2]) + self.exp[i] = (alias + '.' + field_path[1], self.exp[i][1], self.exp[i][2]) # udpate working variables field_path = field_path[1].split('.', 1) From a123d95dcadfdceeddf1f9cb91629b61c0dd1c09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 29 Nov 2012 12:30:25 +0100 Subject: [PATCH 22/80] [IMP] mail_message: star feature is now on mail.notification. A starred message has a notification with starred=True. favorite_user_ids many2many field disappear. _search_to_read and _search_starred replaced by returning a domain on the notifications. Using auto_join, this should speedup chatter a lot. Updated tests accordingly (should be updated a bit more than that, though). Updated JS. Updated mailboxes domains, as to_read and starred already contain a reference to the pid. bzr revid: tde@openerp.com-20121129113025-rizon3eaf14vrla2 --- addons/mail/mail_followers.py | 29 +------- addons/mail/mail_message.py | 107 ++++++++++++++++++++++-------- addons/mail/mail_thread_view.xml | 19 ++++-- addons/mail/static/src/js/mail.js | 4 +- addons/mail/tests/test_mail.py | 15 +++-- 5 files changed, 107 insertions(+), 67 deletions(-) diff --git a/addons/mail/mail_followers.py b/addons/mail/mail_followers.py index 8d3876ec91f..f02835e5b3b 100644 --- a/addons/mail/mail_followers.py +++ b/addons/mail/mail_followers.py @@ -19,7 +19,6 @@ # ############################################################################## -from openerp import SUPERUSER_ID from osv import osv from osv import fields import tools @@ -63,12 +62,15 @@ class mail_notification(osv.Model): 'partner_id': fields.many2one('res.partner', string='Contact', ondelete='cascade', required=True, select=1), 'read': fields.boolean('Read', select=1), + 'starred': fields.boolean('Starred', select=1, + help='Starred message that goes into the todo mailbox'), 'message_id': fields.many2one('mail.message', string='Message', ondelete='cascade', required=True, select=1), } _defaults = { 'read': False, + 'starred': False, } def init(self, cr): @@ -83,31 +85,6 @@ class mail_notification(osv.Model): return super(mail_notification, self).create(cr, uid, vals, context=context) return False - def set_message_read(self, cr, uid, msg_ids, read=None, context=None): - """ Set messages as (un)read. Technically, the notifications related - to uid are set to (un)read. If for some msg_ids there are missing - notifications (i.e. due to load more or thread parent fetching), - they are created. - - :param bool read: (un)read notification - """ - user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] - notif_ids = self.search(cr, uid, [ - ('partner_id', '=', user_pid), - ('message_id', 'in', msg_ids) - ], context=context) - - # all message have notifications: already set them as (un)read - if len(notif_ids) == len(msg_ids): - return self.write(cr, uid, notif_ids, {'read': read}, context=context) - - # some messages do not have notifications: find which one, create notification, update read status - notified_msg_ids = [notification.message_id.id for notification in self.browse(cr, uid, notif_ids, context=context)] - to_create_msg_ids = list(set(msg_ids) - set(notified_msg_ids)) - for msg_id in to_create_msg_ids: - self.create(cr, uid, {'partner_id': user_pid, 'read': read, 'message_id': msg_id}, context=context) - return self.write(cr, uid, notif_ids, {'read': read}, context=context) - def get_partners_to_notify(self, cr, uid, message, context=None): """ Return the list of partners to notify, based on their preferences. diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 4b2878a6bcd..c1df5edf906 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -55,7 +55,7 @@ class mail_message(osv.Model): _message_read_limit = 30 _message_read_fields = ['id', 'parent_id', 'model', 'res_id', 'body', 'subject', 'date', 'to_read', 'email_from', - 'type', 'vote_user_ids', 'attachment_ids', 'author_id', 'partner_ids', 'record_name', 'favorite_user_ids'] + 'type', 'vote_user_ids', 'attachment_ids', 'author_id', 'partner_ids', 'record_name'] _message_record_name_length = 18 _message_read_more_limit = 1024 @@ -98,15 +98,28 @@ class mail_message(osv.Model): def _search_to_read(self, cr, uid, obj, name, domain, context=None): """ Search for messages to read by the current user. Condition is inversed because we search unread message on a read column. """ - if domain[0][2]: - read_cond = "(read = False OR read IS NULL)" - else: - read_cond = "read = True" + pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] + return ['&', ('notification_ids.partner_id', '=', pid), ('notification_ids.read', '=', not domain[0][2])] + + def _get_starred(self, cr, uid, ids, name, arg, context=None): + """ Compute if the message is unread by the current user. """ + res = dict((id, False) for id in ids) partner_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] - cr.execute("SELECT message_id FROM mail_notification "\ - "WHERE partner_id = %%s AND %s" % read_cond, - (partner_id,)) - return [('id', 'in', [r[0] for r in cr.fetchall()])] + notif_obj = self.pool.get('mail.notification') + notif_ids = notif_obj.search(cr, uid, [ + ('partner_id', 'in', [partner_id]), + ('message_id', 'in', ids), + ('starred', '=', True), + ], context=context) + for notif in notif_obj.browse(cr, uid, notif_ids, context=context): + res[notif.message_id.id] = True + return res + + def _search_starred(self, cr, uid, obj, name, domain, context=None): + """ Search for messages to read by the current user. Condition is + inversed because we search unread message on a read column. """ + pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] + return ['&', ('notification_ids.partner_id', '=', pid), ('notification_ids.starred', '=', domain[0][2])] def name_get(self, cr, uid, ids, context=None): # name_get may receive int id instead of an id list @@ -154,15 +167,15 @@ class mail_message(osv.Model): 'body': fields.html('Contents', help='Automatically sanitized HTML contents'), 'to_read': fields.function(_get_to_read, fnct_search=_search_to_read, type='boolean', string='To read', - help='Functional field to search for messages the current user has to read'), + help='Current user has an unread notification linked to this message'), + 'starred': fields.function(_get_starred, fnct_search=_search_starred, + type='boolean', string='Starred', + help='Current user has a starred notification linked to this message'), 'subtype_id': fields.many2one('mail.message.subtype', 'Subtype', ondelete='set null', select=1,), 'vote_user_ids': fields.many2many('res.users', 'mail_vote', 'message_id', 'user_id', string='Votes', help='Users that voted for this message'), - 'favorite_user_ids': fields.many2many('res.users', 'mail_favorite', - 'message_id', 'user_id', string='Favorite', - help='Users that set this message in their favorites'), } def _needaction_domain_get(self, cr, uid, context=None): @@ -196,19 +209,62 @@ class mail_message(osv.Model): return new_has_voted or False #------------------------------------------------------ - # Favorite + # Notification API #------------------------------------------------------ - def favorite_toggle(self, cr, uid, ids, context=None): - ''' Toggles favorite. Performed using read to avoid access rights issues. - Done as SUPERUSER_ID because uid may star a message he cannot modify. ''' - for message in self.read(cr, uid, ids, ['favorite_user_ids'], context=context): - new_is_favorite = not (uid in message.get('favorite_user_ids')) - if new_is_favorite: - self.write(cr, SUPERUSER_ID, message.get('id'), {'favorite_user_ids': [(4, uid)]}, context=context) - else: - self.write(cr, SUPERUSER_ID, message.get('id'), {'favorite_user_ids': [(3, uid)]}, context=context) - return new_is_favorite or False + def set_message_read(self, cr, uid, msg_ids, read, context=None): + """ Set messages as (un)read. Technically, the notifications related + to uid are set to (un)read. If for some msg_ids there are missing + notifications (i.e. due to load more or thread parent fetching), + they are created. + + :param bool read: set notification as (un)read + """ + notification_obj = self.pool.get('mail.notification') + user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] + notif_ids = notification_obj.search(cr, uid, [ + ('partner_id', '=', user_pid), + ('message_id', 'in', msg_ids) + ], context=context) + + # all message have notifications: already set them as (un)read + if len(notif_ids) == len(msg_ids): + return notification_obj.write(cr, uid, notif_ids, {'read': read}, context=context) + + # some messages do not have notifications: find which one, create notification, update read status + notified_msg_ids = [notification.message_id.id for notification in notification_obj.browse(cr, uid, notif_ids, context=context)] + to_create_msg_ids = list(set(msg_ids) - set(notified_msg_ids)) + for msg_id in to_create_msg_ids: + notification_obj.create(cr, uid, {'partner_id': user_pid, 'read': read, 'message_id': msg_id}, context=context) + return notification_obj.write(cr, uid, notif_ids, {'read': read}, context=context) + + def set_message_starred(self, cr, uid, msg_ids, starred, context=None): + """ Set messages as (un)starred. Technically, the notifications related + to uid are set to (un)starred. If for some msg_ids there are missing + notifications (i.e. due to load more or thread parent fetching), + they are created. + + :param bool starred: set notification as (un)starred + """ + notification_obj = self.pool.get('mail.notification') + user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] + notif_ids = notification_obj.search(cr, uid, [ + ('partner_id', '=', user_pid), + ('message_id', 'in', msg_ids) + ], context=context) + + # all message have notifications: already set them as (un)starred + if len(notif_ids) == len(msg_ids): + notification_obj.write(cr, uid, notif_ids, {'starred': starred}, context=context) + return starred + + # some messages do not have notifications: find which one, create notification, update starred status + notified_msg_ids = [notification.message_id.id for notification in notification_obj.browse(cr, uid, notif_ids, context=context)] + to_create_msg_ids = list(set(msg_ids) - set(notified_msg_ids)) + for msg_id in to_create_msg_ids: + notification_obj.create(cr, uid, {'partner_id': user_pid, 'starred': starred, 'message_id': msg_id}, context=context) + notification_obj.write(cr, uid, notif_ids, {'starred': starred}, context=context) + return starred #------------------------------------------------------ # Message loading for web interface @@ -283,7 +339,6 @@ class mail_message(osv.Model): # votes and favorites: res.users ids, no prefetching should be done vote_nb = len(message.vote_user_ids) has_voted = uid in [user.id for user in message.vote_user_ids] - is_favorite = uid in [user.id for user in message.favorite_user_ids] return {'id': message.id, 'type': message.type, @@ -301,7 +356,7 @@ class mail_message(osv.Model): 'partner_ids': [], 'vote_nb': vote_nb, 'has_voted': has_voted, - 'is_favorite': is_favorite, + 'is_favorite': message.starred, 'attachment_ids': [], } diff --git a/addons/mail/mail_thread_view.xml b/addons/mail/mail_thread_view.xml index a232aa12642..fedcef36fda 100644 --- a/addons/mail/mail_thread_view.xml +++ b/addons/mail/mail_thread_view.xml @@ -11,9 +11,8 @@ } @@ -62,7 +63,9 @@ 'search_default_message_unread': True } @@ -83,7 +86,11 @@ 'default_res_id': uid }

diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index 44bb4e9ae09..3b521678fe8 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -851,7 +851,7 @@ openerp.mail = function (session) { } var message_ids = _.map(messages, function (val) { return val.id; }); - this.ds_notification.call('set_message_read', [message_ids, read_value, this.context]) + this.ds_message.call('set_message_read', [message_ids, read_value, this.context]) .then(function () { // apply modification _.each(messages, function (msg) { @@ -900,7 +900,7 @@ openerp.mail = function (session) { var self=this; var button = self.$('.oe_star:first'); - this.ds_message.call('favorite_toggle', [[self.id]]) + this.ds_message.call('set_message_starred', [[self.id], !self.is_favorite]) .then(function (star) { self.is_favorite=star; if (self.is_favorite) { diff --git a/addons/mail/tests/test_mail.py b/addons/mail/tests/test_mail.py index bc6df813ef7..c18dea46452 100644 --- a/addons/mail/tests/test_mail.py +++ b/addons/mail/tests/test_mail.py @@ -795,7 +795,7 @@ class test_mail(test_mail_mockup.TestMailMockups): # Test: msg has Bert as voter self.assertEqual(set(msg.vote_user_ids), set([user_raoul]), 'mail_message vote: after unvoting, Bert should be in the voter') - def test_70_message_favorite(self): + def test_70_message_star(self): """ Tests for favorites. """ cr, uid, user_admin, user_raoul, group_pigs = self.cr, self.uid, self.user_admin, self.user_raoul, self.group_pigs # Data: post a message on Pigs @@ -803,17 +803,18 @@ class test_mail(test_mail_mockup.TestMailMockups): msg = self.mail_message.browse(cr, uid, msg_id) # Do: Admin stars msg - self.mail_message.favorite_toggle(cr, uid, [msg.id]) + self.mail_message.set_message_starred(cr, uid, [msg.id], True) msg.refresh() # Test: msg starred by Admin - self.assertEqual(set(msg.favorite_user_ids), set([user_admin]), 'mail_message favorite: after starring, Admin should be in favorite_user_ids') + self.assertTrue(msg.starred, 'mail_message starred failed') # Do: Bert stars msg - self.mail_message.favorite_toggle(cr, user_raoul.id, [msg.id]) + self.mail_message.set_message_starred(cr, user_raoul.id, [msg.id], True) msg.refresh() # Test: msg starred by Admin and Raoul - self.assertEqual(set(msg.favorite_user_ids), set([user_admin, user_raoul]), 'mail_message favorite: after starring, Admin and Raoul should be in favorite_user_ids') + # self.assertTrue(msg.starred, set([user_admin, user_raoul]), 'mail_message favorite: after starring, Admin and Raoul should be in favorite_user_ids') # Do: Admin unvote for msg - self.mail_message.favorite_toggle(cr, uid, [msg.id]) + self.mail_message.set_message_starred(cr, uid, [msg.id], False) msg.refresh() # Test: msg starred by Raoul - self.assertEqual(set(msg.favorite_user_ids), set([user_raoul]), 'mail_message favorite: after unstarring, Raoul should be in favorite_user_ids') + self.assertFalse(msg.starred, 'mail_message starred failed') + # self.assertEqual(set(msg.favorite_user_ids), set([user_raoul]), 'mail_message favorite: after unstarring, Raoul should be in favorite_user_ids') From 9f84bbddfb3abfc69cdb6b3dc126b7e2381cc452 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 29 Nov 2012 15:42:53 +0100 Subject: [PATCH 23/80] [IMP] mail: message_unread searchable field now returns a domain. This domain is quite complex, because it bypasses limitations of the _auto_join feature used on message_ids. It should be: message_ids.read = False. However, it takes into account the one2many model, as well as to_read being a function field also searchable. Ths parsing is not capable to handle this kind of thing correctly now without using an ugly hack. Updated _search_to_read and _search_starred. bzr revid: tde@openerp.com-20121129144253-i9e7m2kqjteq8ajr --- addons/mail/mail_message.py | 19 +++---------------- addons/mail/mail_thread.py | 19 +++++++++++-------- addons/mail/mail_thread_view.xml | 2 +- 3 files changed, 15 insertions(+), 25 deletions(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index c1df5edf906..28ac087edc0 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -98,8 +98,7 @@ class mail_message(osv.Model): def _search_to_read(self, cr, uid, obj, name, domain, context=None): """ Search for messages to read by the current user. Condition is inversed because we search unread message on a read column. """ - pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] - return ['&', ('notification_ids.partner_id', '=', pid), ('notification_ids.read', '=', not domain[0][2])] + return ['&', ('notification_ids.partner_id.user_ids', 'in', [uid]), ('notification_ids.read', '=', not domain[0][2])] def _get_starred(self, cr, uid, ids, name, arg, context=None): """ Compute if the message is unread by the current user. """ @@ -118,8 +117,7 @@ class mail_message(osv.Model): def _search_starred(self, cr, uid, obj, name, domain, context=None): """ Search for messages to read by the current user. Condition is inversed because we search unread message on a read column. """ - pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] - return ['&', ('notification_ids.partner_id', '=', pid), ('notification_ids.starred', '=', domain[0][2])] + return ['&', ('notification_ids.partner_id.user_ids', 'in', [uid]), ('notification_ids.starred', '=', domain[0][2])] def name_get(self, cr, uid, ids, context=None): # name_get may receive int id instead of an id list @@ -179,9 +177,7 @@ class mail_message(osv.Model): } def _needaction_domain_get(self, cr, uid, context=None): - if self._needaction: - return [('to_read', '=', True)] - return [] + return [('to_read', '=', True)] def _get_default_author(self, cr, uid, context=None): return self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] @@ -529,15 +525,6 @@ class mail_message(osv.Model): thread_level=thread_level, message_unload_ids=message_unload_ids, domain=domain, parent_id=parent_id, context=context) return message_list - # TDE Note: do we need this ? - # def user_free_attachment(self, cr, uid, context=None): - # attachment = self.pool.get('ir.attachment') - # attachment_list = [] - # attachment_ids = attachment.search(cr, uid, [('res_model', '=', 'mail.message'), ('create_uid', '=', uid)]) - # if len(attachment_ids): - # attachment_list = [{'id': attach.id, 'name': attach.name, 'date': attach.create_date} for attach in attachment.browse(cr, uid, attachment_ids, context=context)] - # return attachment_list - #------------------------------------------------------ # mail_message internals #------------------------------------------------------ diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 2240c91e4fa..40f13434717 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -122,14 +122,16 @@ class mail_thread(osv.AbstractModel): return res def _search_message_unread(self, cr, uid, obj=None, name=None, domain=None, context=None): - user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] - cr.execute(""" SELECT DISTINCT m.res_id FROM mail_message m - JOIN mail_notification n - ON (n.message_id = m.id AND n.partner_id = %s AND (n.read = False or n.read IS NULL)) - WHERE m.model = %s""", - (user_pid, self._name,)) - res_ids = [result[0] for result in cr.fetchall()] - return [('id', 'in', res_ids)] + """ TDE FIXME in 7.1: searching unread messages + _auto_join is currently a very limited feature; therefore the returned + domain to search for unread messages take into acount the following + limitations of this feature : + - does not take into account the domain on the one2many field + (i.e. message_ids.model = self._name is needed) + - does not correctly handle function field on relational table + (i.e. message_ids.read crashes) + """ + return ['&', '&', ('message_ids.model', '=', self._name), ('message_ids.notification_ids.partner_id.user_ids', 'in', [uid]), ('message_ids.notification_ids.read', '=', False)] def _get_followers(self, cr, uid, ids, name, arg, context=None): fol_obj = self.pool.get('mail.followers') @@ -200,6 +202,7 @@ class mail_thread(osv.AbstractModel): obj='res.partner', string='Followers', multi='_get_followers'), 'message_ids': fields.one2many('mail.message', 'res_id', domain=lambda self: [('model', '=', self._name)], + _auto_join=True, string='Messages', help="Messages and communication history"), 'message_unread': fields.function(_get_message_data, diff --git a/addons/mail/mail_thread_view.xml b/addons/mail/mail_thread_view.xml index 80b9800f7ec..d5c111573ea 100644 --- a/addons/mail/mail_thread_view.xml +++ b/addons/mail/mail_thread_view.xml @@ -11,7 +11,7 @@ } Date: Thu, 29 Nov 2012 16:57:31 +0100 Subject: [PATCH 26/80] [IMP] ir_ui_menu: needaction_enabled is back to store True. bzr revid: tde@openerp.com-20121129155731-xk5cm9yvzbuyxpdc --- openerp/addons/base/ir/ir_ui_menu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/addons/base/ir/ir_ui_menu.py b/openerp/addons/base/ir/ir_ui_menu.py index fa4e5b2b540..7d5915c89a8 100644 --- a/openerp/addons/base/ir/ir_ui_menu.py +++ b/openerp/addons/base/ir/ir_ui_menu.py @@ -317,7 +317,7 @@ class ir_ui_menu(osv.osv): 'web_icon_hover_data': fields.function(_get_image_icon, string='Web Icon Image (hover)', type='binary', readonly=True, store=True, multi='icon'), 'needaction_enabled': fields.function(_get_needaction_enabled, type='boolean', - # store=True, TDE TODO: uncomment after internal tests + store=True, string='Target model uses the need action mechanism', help='If the menu entry action is an act_window action, and if this action is related to a model that uses the need_action mechanism, this field is set to true. Otherwise, it is false.'), 'action': fields.function(_action, fnct_inv=_action_inv, From f0087538ac10b05126fd158c29f4b2132ca9edc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 29 Nov 2012 16:58:01 +0100 Subject: [PATCH 27/80] [IMP] as ir_ui_menu.needaction_enabled is back as store=True, we can filter the initial search for needaction_counters. bzr revid: tde@openerp.com-20121129155801-0wxdogo9o9emk6i4 --- addons/web/controllers/main.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 23dd42e32e1..9da87631fe1 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -1013,9 +1013,7 @@ class Menu(openerpweb.Controller): Menus = req.session.model('ir.ui.menu') if menu_ids == False: - menu_ids = Menus.search([], context=req.context) - # TDE FIXME: set needaction_enabled column to store, to enable this more limited search - # menu_ids = Menus.search([('needaction_enabled', '=', True)], context=req.context) + menu_ids = Menus.search([('needaction_enabled', '=', True)], context=req.context) menu_needaction_data = Menus.get_needaction_data(menu_ids, req.context) return menu_needaction_data From 0de15c9bbf84e86cafa84c7ab025f853e4d987d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 29 Nov 2012 17:14:28 +0100 Subject: [PATCH 28/80] [CLEAN] Moved needaction html into a dedicated template. bzr revid: tde@openerp.com-20121129161428-usvgdoykg0g0etr7 --- addons/web/static/src/js/chrome.js | 2 +- addons/web/static/src/xml/base.xml | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index 072d3582159..68493b36701 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -847,7 +847,7 @@ instance.web.Menu = instance.web.Widget.extend({ var $item = self.$secondary_menus.find('a[data-menu="' + menu_id + '"]'); $item.remove('oe_menu_counter'); if (item.needaction_counter && item.needaction_counter > 0) { - $item.append('

' + item.needaction_counter + '
'); + $item.append(QWeb.render("Menu.needaction_counter", { widget : item })); } }); }, diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 62d8a872f25..7421027315b 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -388,14 +388,16 @@ t-att-data-action-model="menu.action ? menu.action.split(',')[0] : ''" t-att-data-action-id="menu.action ? menu.action.split(',')[1] : ''"> - -
- 99+ -
-
+ +
+ 99+ + +
+
+ From a5ca416f055871fb2765567c2cf2f06445c1d2ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 29 Nov 2012 17:42:53 +0100 Subject: [PATCH 29/80] [IMP] needaction counters: moved rpc call on the trigger menu_loaded, to be able to reload the needaction counters through this trigger. bzr revid: tde@openerp.com-20121129164253-j2vol27w0libpdsd --- addons/web/static/src/js/chrome.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index 68493b36701..c6d216d128c 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -799,10 +799,18 @@ instance.web.client_actions.add("change_password", "instance.web.ChangePassword" instance.web.Menu = instance.web.Widget.extend({ template: 'Menu', init: function() { + var self = this; this._super.apply(this, arguments); this.has_been_loaded = $.Deferred(); this.maximum_visible_links = 'auto'; // # of menu to show. 0 = do not crop, 'auto' = algo this.data = {data:{children:[]}}; + this.on("menu_loaded", this, function (e) { + // launch the fetch of needaction counters, asynchronous + this.rpc("/web/menu/load_needaction", {menu_ids: false}).done(function(r) { + self.on_needaction_loaded(r); + }); + }); + }, start: function() { this._super.apply(this, arguments); @@ -835,10 +843,6 @@ instance.web.Menu = instance.web.Widget.extend({ } this.trigger('menu_loaded', data); this.has_been_loaded.resolve(); - // Now launch the fetch of needaction counters, asynchronous - this.rpc("web/menu/load_needaction", {menu_ids: false}).done(function(r) { - self.on_needaction_loaded(r); - }); }, on_needaction_loaded: function(data) { var self = this; From 681d7832341c4b39fb331455bf4fd53be9746cbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 29 Nov 2012 17:50:32 +0100 Subject: [PATCH 30/80] [IMP] expression.parse: added some asserts about non supported cases. bzr revid: tde@openerp.com-20121129165032-nnlnf1k0iwal24ny --- openerp/osv/expression.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openerp/osv/expression.py b/openerp/osv/expression.py index 5fb18fb8cc1..b13586f5311 100644 --- a/openerp/osv/expression.py +++ b/openerp/osv/expression.py @@ -561,8 +561,8 @@ class expression(object): alias = working_table._table while len(field_path) > 1 and field._auto_join: - if not field._type in ['many2one', 'one2many']: - raise '_auto_join attribute on something else than a many2one or one2many is currently not implemented... crashing' + assert field._type in ['many2one', 'one2many'], \ + '_auto_join attribute on something else than a many2one or one2many is currently not supported' previous_alias = alias alias = alias + '__' + field_path[0] @@ -625,6 +625,8 @@ class expression(object): if _logger.isEnabledFor(logging.DEBUG): _logger.debug(''.join(traceback.format_stack())) else: + assert working_table == table, \ + 'function fields can not be contained in paths using _auto_join' subexp = field.search(cr, uid, table, left, [self.exp[i]], context=context) if not subexp: self.exp[i] = TRUE_LEAF From 91cb411e803fd095c69066fb79eb6bcd8ffcdae3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 30 Nov 2012 11:40:38 +0100 Subject: [PATCH 31/80] [FIX] mail: default_starred key is recognized when creating a mail_message, handled in mail_messgae.create() to avoid issues with notifications if using an invert function on starred function field. bzr revid: tde@openerp.com-20121130104038-3nivz6kvdyi22f3l --- addons/crm/crm_lead_demo.xml | 5 +++-- addons/mail/mail_message.py | 11 +++++++++++ addons/mail/static/src/js/mail.js | 3 +-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/addons/crm/crm_lead_demo.xml b/addons/crm/crm_lead_demo.xml index 913ce9fc612..465c876fae1 100644 --- a/addons/crm/crm_lead_demo.xml +++ b/addons/crm/crm_lead_demo.xml @@ -641,7 +641,6 @@ Andrew
comment - crm.lead @@ -651,7 +650,6 @@ Andrew - Inquiry @@ -669,6 +667,9 @@ Andrew Want to know features and benefits to use the new software. comment + diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 28ac087edc0..cf691cceecf 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -682,12 +682,23 @@ class mail_message(osv.Model): (self._description, operation)) def create(self, cr, uid, values, context=None): + if context is None: + context = {} + default_starred = context.pop('default_starred', False) if not values.get('message_id') and values.get('res_id') and values.get('model'): values['message_id'] = tools.generate_tracking_message_id('%(res_id)s-%(model)s' % values) elif not values.get('message_id'): values['message_id'] = tools.generate_tracking_message_id('private') newid = super(mail_message, self).create(cr, uid, values, context) self._notify(cr, SUPERUSER_ID, newid, context=context) + # TDE FIXME: handle default_starred. Why not setting an inv on starred ? + # Because starred will call set_message_starred, that looks for notifications. + # When creating a new mail_message, it will create a notification to a message + # that does not exist, leading to an error (key not existing). Also this + # this means unread notifications will be created, yet we can not assure + # this is what we want. + if default_starred: + self.set_message_starred(cr, uid, [newid], True, context=context) return newid def read(self, cr, uid, ids, fields=None, context=None, load='_classic_read'): diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index 453b464a409..31e77a6ec7d 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -990,8 +990,7 @@ openerp.mail = function (session) { // add message composition form view if (!this.compose_message) { this.compose_message = new mail.ThreadComposeMessage(this, this, { - // TDE FIXME: default_favorite_user_ids does not exist anymore -> you should create the message, set starred then update - 'context': this.options.compose_as_todo && !this.thread_level ? this.context : this.context, + 'context': this.options.compose_as_todo && !this.thread_level ? _.extend(this.context, { 'default_starred': true }) : this.context, 'options': this.options, }); if (!this.thread_level || this.thread_level > this.options.display_indented_thread) { From 9e6c2805abe603818ba112471bd1221fce7eeedf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 4 Dec 2012 15:26:50 +0100 Subject: [PATCH 32/80] [REF] [IMP] expression.parse: refactored the main parsing loop, now using source and result stacks. Leaf elements are encapsulated into a specific object, allowing to delegate the join condition formation and aliases generation, as well as working_table management to them. The main parsing loop is now a loop taking a leaf, doing one action on it, and putting the result back to be analyzed or in the results. This allows to avoid having while loops inside a main while loop with some weird corner effects. bzr revid: tde@openerp.com-20121204142650-gkhmjdeu5upi25kp --- openerp/addons/base/ir/ir_rule.py | 4 +- openerp/addons/base/tests/test_expression.py | 104 ++- openerp/osv/expression.py | 912 +++++++++++-------- openerp/osv/orm.py | 9 +- openerp/osv/query.py | 4 +- openerp/tests/test_expression.py | 11 +- 6 files changed, 650 insertions(+), 394 deletions(-) diff --git a/openerp/addons/base/ir/ir_rule.py b/openerp/addons/base/ir/ir_rule.py index a4341436158..ba4665ddd69 100644 --- a/openerp/addons/base/ir/ir_rule.py +++ b/openerp/addons/base/ir/ir_rule.py @@ -52,7 +52,7 @@ class ir_rule(osv.osv): eval_context = self._eval_context(cr, uid) for rule in self.browse(cr, uid, ids, context): if rule.domain_force: - res[rule.id] = expression.normalize(eval(rule.domain_force, eval_context)) + res[rule.id] = expression.normalize_domain(eval(rule.domain_force, eval_context)) else: res[rule.id] = [] return res @@ -130,7 +130,7 @@ class ir_rule(osv.osv): for rule in self.browse(cr, SUPERUSER_ID, rule_ids): # read 'domain' as UID to have the correct eval context for the rule. rule_domain = self.read(cr, uid, rule.id, ['domain'])['domain'] - dom = expression.normalize(rule_domain) + dom = expression.normalize_domain(rule_domain) for group in rule.groups: if group in user.groups_id: group_domains.setdefault(group, []).append(dom) diff --git a/openerp/addons/base/tests/test_expression.py b/openerp/addons/base/tests/test_expression.py index d3fe9de3efd..5286e1c5657 100644 --- a/openerp/addons/base/tests/test_expression.py +++ b/openerp/addons/base/tests/test_expression.py @@ -92,7 +92,30 @@ class test_expression(common.TransactionCase): # self.assertTrue(a not in with_any_other_than_a, "Search for category_id with any other than cat_a failed (1).") # self.assertTrue(ab in with_any_other_than_a, "Search for category_id with any other than cat_a failed (2).") - def test_10_auto_join(self): + def test_10_expression_parse(self): + # TDE note: those tests have been added when refactoring the expression.parse() method. + # They come in addition to the already existing test_osv_expression.yml; maybe some tests + # will be a bit redundant + registry, cr, uid = self.registry, self.cr, self.uid + users_obj = registry('res.users') + + # Create users + a = users_obj.create(cr, uid, {'name': 'test_A', 'login': 'test_A'}) + b1 = users_obj.create(cr, uid, {'name': 'test_B', 'login': 'test_B'}) + b1_user = users_obj.browse(cr, uid, [b1])[0] + b2 = users_obj.create(cr, uid, {'name': 'test_B2', 'login': 'test_B2', 'parent_id': b1_user.partner_id.id}) + + # Test1: simple inheritance + user_ids = users_obj.search(cr, uid, [('name', 'like', 'test')]) + self.assertEqual(set(user_ids), set([a, b1, b2]), 'searching through inheritance failed') + user_ids = users_obj.search(cr, uid, [('name', '=', 'test_B')]) + self.assertEqual(set(user_ids), set([b1]), 'searching through inheritance failed') + + # Test2: inheritance + relational fields + user_ids = users_obj.search(cr, uid, [('child_ids.name', 'like', 'test_B')]) + self.assertEqual(set(user_ids), set([b1]), 'searching through inheritance failed') + + def test_20_auto_join(self): registry, cr, uid = self.registry, self.cr, self.uid # Get models @@ -132,6 +155,9 @@ class test_expression(common.TransactionCase): # Do: one2many without _auto_join self._reinit_mock() partner_ids = partner_obj.search(cr, uid, [('bank_ids.name', 'like', name_test)]) + for query in self.query_list: + print query + print '----------------------' # Test result self.assertEqual(set(partner_ids), set([p_aa]), "_auto_join off: ('bank_ids.name', 'like', '..'): incorrect result") @@ -141,14 +167,14 @@ class test_expression(common.TransactionCase): sql_query = self.query_list[0].get_sql() self.assertIn('res_partner_bank', sql_query[0], "_auto_join off: ('bank_ids.name', 'like', '..') first query incorrect main table") - self.assertIn('res_partner_bank."name" like %s', sql_query[1], + self.assertIn('"res_partner_bank"."name" like %s', sql_query[1], "_auto_join off: ('bank_ids.name', 'like', '..') first query incorrect where condition") self.assertEqual(set(['%' + name_test + '%']), set(sql_query[2]), "_auto_join off: ('bank_ids.name', 'like', '..') first query incorrect parameter") sql_query = self.query_list[2].get_sql() self.assertIn('res_partner', sql_query[0], "_auto_join off: ('bank_ids.name', 'like', '..') third query incorrect main table") - self.assertIn('res_partner."id" in (%s)', sql_query[1], + self.assertIn('"res_partner"."id" in (%s)', sql_query[1], "_auto_join off: ('bank_ids.name', 'like', '..') third query incorrect where condition") self.assertEqual(set([p_aa]), set(sql_query[2]), "_auto_join off: ('bank_ids.name', 'like', '..') third query incorrect parameter") @@ -156,6 +182,9 @@ class test_expression(common.TransactionCase): # Do: cascaded one2many without _auto_join self._reinit_mock() partner_ids = partner_obj.search(cr, uid, [('child_ids.bank_ids.id', 'in', [b_aa, b_ba])]) + for query in self.query_list: + print query + print '----------------------' # Test result self.assertEqual(set(partner_ids), set([p_a, p_b]), "_auto_join off: ('child_ids.bank_ids.id', 'in', [..]): incorrect result") @@ -167,6 +196,8 @@ class test_expression(common.TransactionCase): partner_bank_ids_col._auto_join = True self._reinit_mock() partner_ids = partner_obj.search(cr, uid, [('bank_ids.name', 'like', 'test_a')]) + # for query in self.query_list: + # print query # Test result self.assertEqual(set(partner_ids), set([p_aa]), "_auto_join on: ('bank_ids.name', 'like', '..') incorrect result") @@ -178,9 +209,9 @@ class test_expression(common.TransactionCase): "_auto_join on: ('bank_ids.name', 'like', '..') query incorrect main table") self.assertIn('"res_partner_bank" as res_partner__bank_ids', sql_query[0], "_auto_join on: ('bank_ids.name', 'like', '..') query incorrect join") - self.assertIn('res_partner__bank_ids."name" like %s', sql_query[1], + self.assertIn('"res_partner__bank_ids"."name" like %s', sql_query[1], "_auto_join on: ('bank_ids.name', 'like', '..') query incorrect where condition") - self.assertIn('res_partner__bank_ids."partner_id"=res_partner."id"', sql_query[1], + self.assertIn('res_partner."id"=res_partner__bank_ids."partner_id"', sql_query[1], "_auto_join on: ('bank_ids.name', 'like', '..') query incorrect join condition") self.assertEqual(set(['%' + name_test + '%']), set(sql_query[2]), "_auto_join on: ('bank_ids.name', 'like', '..') query incorrect parameter") @@ -188,6 +219,9 @@ class test_expression(common.TransactionCase): # Do: one2many with _auto_join, test final leaf is an id self._reinit_mock() partner_ids = partner_obj.search(cr, uid, [('bank_ids.id', 'in', [b_aa, b_ab])]) + for query in self.query_list: + print query + print '----------------------' # Test result self.assertEqual(set(partner_ids), set([p_aa, p_ab]), "_auto_join on: ('bank_ids.id', 'in', [..]) incorrect result") @@ -199,9 +233,9 @@ class test_expression(common.TransactionCase): "_auto_join on: ('bank_ids.id', 'in', [..]) query incorrect main table") self.assertIn('"res_partner_bank" as res_partner__bank_ids', sql_query[0], "_auto_join on: ('bank_ids.id', 'in', [..]) query incorrect join") - self.assertIn('res_partner__bank_ids."id" in (%s,%s)', sql_query[1], + self.assertIn('"res_partner__bank_ids"."id" in (%s,%s)', sql_query[1], "_auto_join on: ('bank_ids.id', 'in', [..]) query incorrect where condition") - self.assertIn('res_partner__bank_ids."partner_id"=res_partner."id"', sql_query[1], + self.assertIn('res_partner."id"=res_partner__bank_ids."partner_id"', sql_query[1], "_auto_join on: ('bank_ids.id', 'in', [..]) query incorrect join condition") self.assertEqual(set([b_aa, b_ab]), set(sql_query[2]), "_auto_join on: ('bank_ids.id', 'in', [..]) query incorrect parameter") @@ -210,6 +244,9 @@ class test_expression(common.TransactionCase): partner_child_ids_col._auto_join = True self._reinit_mock() partner_ids = partner_obj.search(cr, uid, [('child_ids.bank_ids.id', 'in', [b_aa, b_ba])]) + for query in self.query_list: + print query + print '----------------------' # Test result self.assertEqual(set(partner_ids), set([p_a, p_b]), "_auto_join on: ('child_ids.bank_ids.id', 'not in', [..]): incorrect result") @@ -223,11 +260,11 @@ class test_expression(common.TransactionCase): "_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect join") self.assertIn('"res_partner_bank" as res_partner__child_ids__bank_ids', sql_query[0], "_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect join") - self.assertIn('res_partner__child_ids__bank_ids."id" in (%s,%s)', sql_query[1], + self.assertIn('"res_partner__child_ids__bank_ids"."id" in (%s,%s)', sql_query[1], "_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect where condition") - self.assertIn('res_partner__child_ids."parent_id"=res_partner."id"', sql_query[1], + self.assertIn('res_partner."id"=res_partner__child_ids."parent_id"', sql_query[1], "_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect join condition") - self.assertIn('res_partner__child_ids__bank_ids."partner_id"=res_partner__child_ids."id"', sql_query[1], + self.assertIn('res_partner__child_ids."id"=res_partner__child_ids__bank_ids."partner_id"', sql_query[1], "_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect join condition") self.assertEqual(set([b_aa, b_ba]), set(sql_query[2]), "_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect parameter") @@ -241,6 +278,9 @@ class test_expression(common.TransactionCase): # Do: many2one without _auto_join self._reinit_mock() partner_ids = partner_obj.search(cr, uid, [('state_id.country_id.code', 'like', name_test)]) + for query in self.query_list: + print query + print '----------------------' # Test result: at least our added data + demo data self.assertTrue(set([p_a, p_b, p_aa, p_ab, p_ba]).issubset(set(partner_ids)), "_auto_join off: ('state_id.country_id.code', 'like', '..') incorrect result") @@ -252,6 +292,9 @@ class test_expression(common.TransactionCase): partner_state_id_col._auto_join = True self._reinit_mock() partner_ids = partner_obj.search(cr, uid, [('state_id.country_id.code', 'like', name_test)]) + for query in self.query_list: + print query + print '----------------------' # Test result: at least our added data + demo data self.assertTrue(set([p_a, p_b, p_aa, p_ab, p_ba]).issubset(set(partner_ids)), "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') incorrect result") @@ -261,7 +304,7 @@ class test_expression(common.TransactionCase): sql_query = self.query_list[0].get_sql() self.assertIn('"res_country"', sql_query[0], "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect main table") - self.assertIn('res_country."code" like %s', sql_query[1], + self.assertIn('"res_country"."code" like %s', sql_query[1], "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect where condition") self.assertEqual(['%' + name_test + '%'], sql_query[2], "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect parameter") @@ -270,9 +313,9 @@ class test_expression(common.TransactionCase): "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect main table") self.assertIn('"res_country_state" as res_partner__state_id', sql_query[0], "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect join") - self.assertIn('res_partner__state_id."country_id" in (%s)', sql_query[1], + self.assertIn('"res_partner__state_id"."country_id" in (%s)', sql_query[1], "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect where condition") - self.assertIn('(res_partner__state_id."id"=res_partner."state_id"', sql_query[1], + self.assertIn('res_partner."state_id"=res_partner__state_id."id"', sql_query[1], "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect join condition") # Do: many2one with 1 _auto_join on the second many2one @@ -280,6 +323,9 @@ class test_expression(common.TransactionCase): state_country_id_col._auto_join = True self._reinit_mock() partner_ids = partner_obj.search(cr, uid, [('state_id.country_id.code', 'like', name_test)]) + for query in self.query_list: + print query + print '----------------------' # Test result: at least our added data + demo data self.assertTrue(set([p_a, p_b, p_aa, p_ab, p_ba]).issubset(set(partner_ids)), "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') incorrect result") @@ -292,9 +338,9 @@ class test_expression(common.TransactionCase): "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect main table") self.assertIn('"res_country" as res_country_state__country_id', sql_query[0], "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect join") - self.assertIn('res_country_state__country_id."code" like %s', sql_query[1], + self.assertIn('"res_country_state__country_id"."code" like %s', sql_query[1], "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect where condition") - self.assertIn('res_country_state__country_id."id"=res_country_state."country_id"', sql_query[1], + self.assertIn('res_country_state."country_id"=res_country_state__country_id."id"', sql_query[1], "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect join condition") self.assertEqual(['%' + name_test + '%'], sql_query[2], "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect parameter") @@ -302,7 +348,7 @@ class test_expression(common.TransactionCase): sql_query = self.query_list[1].get_sql() self.assertIn('"res_partner"', sql_query[0], "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect main table") - self.assertIn('res_partner."state_id" in', sql_query[1], + self.assertIn('"res_partner"."state_id" in', sql_query[1], "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect where condition") # Do: many2one with 2 _auto_join @@ -310,6 +356,8 @@ class test_expression(common.TransactionCase): state_country_id_col._auto_join = True self._reinit_mock() partner_ids = partner_obj.search(cr, uid, [('state_id.country_id.code', 'like', name_test)]) + for query in self.query_list: + print query # Test result: at least our added data + demo data self.assertTrue(set([p_a, p_b, p_aa, p_ab, p_ba]).issubset(set(partner_ids)), "_auto_join on: ('state_id.country_id.code', 'like', '..') incorrect result") @@ -323,11 +371,11 @@ class test_expression(common.TransactionCase): "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join") self.assertIn('"res_country" as res_partner__state_id__country_id', sql_query[0], "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join") - self.assertIn('res_partner__state_id__country_id."code" like %s', sql_query[1], + self.assertIn('"res_partner__state_id__country_id"."code" like %s', sql_query[1], "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect where condition") - self.assertIn('res_partner__state_id."id"=res_partner."state_id"', sql_query[1], + self.assertIn('res_partner."state_id"=res_partner__state_id."id"', sql_query[1], "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join condition") - self.assertIn('res_partner__state_id__country_id."id"=res_partner__state_id."country_id"', sql_query[1], + self.assertIn('res_partner__state_id."country_id"=res_partner__state_id__country_id."id"', sql_query[1], "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join condition") self.assertEqual(['%' + name_test + '%'], sql_query[2], "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect parameter") @@ -345,6 +393,9 @@ class test_expression(common.TransactionCase): # Do: ('child_ids.state_id.country_id.code', 'like', '..') without _auto_join self._reinit_mock() partner_ids = partner_obj.search(cr, uid, [('child_ids.state_id.country_id.code', 'like', name_test)]) + for query in self.query_list: + print query + print '----------------------' # Test result: at least our added data + demo data self.assertTrue(set([p_a, p_b]).issubset(set(partner_ids)), "_auto_join off: ('child_ids.state_id.country_id.code', 'like', '..') incorrect result") @@ -358,6 +409,9 @@ class test_expression(common.TransactionCase): state_country_id_col._auto_join = True self._reinit_mock() partner_ids = partner_obj.search(cr, uid, [('child_ids.state_id.country_id.code', 'like', name_test)]) + for query in self.query_list: + print query + print '----------------------' # Test result: at least our added data + demo data self.assertTrue(set([p_a, p_b]).issubset(set(partner_ids)), "_auto_join on: ('child_ids.state_id.country_id.code', 'like', '..') incorrect result") @@ -365,12 +419,12 @@ class test_expression(common.TransactionCase): self.assertEqual(len(self.query_list), 1, "_auto_join on: ('child_ids.state_id.country_id.code', 'like', '..') number of queries incorrect") - # Remove mocks and modifications - partner_bank_ids_col._auto_join = False - partner_child_ids_col._auto_join = False - partner_state_id_col._auto_join = False - state_country_id_col._auto_join = False - BaseModel._where_calc = self._base_model_where_calc + # # Remove mocks and modifications + # partner_bank_ids_col._auto_join = False + # partner_child_ids_col._auto_join = False + # partner_state_id_col._auto_join = False + # state_country_id_col._auto_join = False + # BaseModel._where_calc = self._base_model_where_calc if __name__ == '__main__': unittest2.main() diff --git a/openerp/osv/expression.py b/openerp/osv/expression.py index b13586f5311..2474e6665cd 100644 --- a/openerp/osv/expression.py +++ b/openerp/osv/expression.py @@ -136,10 +136,9 @@ start the server specifying the ``--unaccent`` flag. import logging import traceback -from openerp.tools import flatten, reverse_enumerate -import fields import openerp.modules from openerp.osv.orm import MAGIC_COLUMNS +import openerp.tools as tools #.apidoc title: Domain Expressions @@ -174,7 +173,12 @@ FALSE_DOMAIN = [FALSE_LEAF] _logger = logging.getLogger(__name__) -def normalize(domain): + +# -------------------------------------------------- +# Generic domain manipulation +# -------------------------------------------------- + +def normalize_domain(domain): """Returns a normalized version of ``domain_expr``, where all implicit '&' operators have been made explicit. One property of normalized domain expressions is that they can be easily combined together as if they were single domain components. @@ -190,13 +194,14 @@ def normalize(domain): result[0:0] = [AND_OPERATOR] # put an extra '&' in front expected = 1 result.append(token) - if isinstance(token, (list, tuple)): # domain term + if isinstance(token, (list, tuple)): # domain term expected -= 1 else: expected += op_arity.get(token, 0) - 1 assert expected == 0 return result + def combine(operator, unit, zero, domains): """Returns a new domain expression where all domain components from ``domains`` have been added together using the binary operator ``operator``. The given @@ -227,46 +232,16 @@ def combine(operator, unit, zero, domains): result = [operator] * (count - 1) + result return result + def AND(domains): """AND([D1,D2,...]) returns a domain representing D1 and D2 and ... """ return combine(AND_OPERATOR, TRUE_DOMAIN, FALSE_DOMAIN, domains) + def OR(domains): """OR([D1,D2,...]) returns a domain representing D1 or D2 or ... """ return combine(OR_OPERATOR, FALSE_DOMAIN, TRUE_DOMAIN, domains) -def is_operator(element): - """Test whether an object is a valid domain operator. """ - return isinstance(element, basestring) and element in DOMAIN_OPERATORS - -# TODO change the share wizard to use this function. -def is_leaf(element, internal=False): - """ Test whether an object is a valid domain term. - - :param internal: allow or not the 'inselect' internal operator in the term. - This normally should be always left to False. - """ - INTERNAL_OPS = TERM_OPERATORS + ('inselect',) - return (isinstance(element, tuple) or isinstance(element, list)) \ - and len(element) == 3 \ - and (((not internal) and element[1] in TERM_OPERATORS + ('<>',)) \ - or (internal and element[1] in INTERNAL_OPS + ('<>',))) - -def normalize_leaf(left, operator, right): - """ Change a term's operator to some canonical form, simplifying later - processing. - """ - original = operator - operator = operator.lower() - if operator == '<>': - operator = '!=' - if isinstance(right, bool) and operator in ('in', 'not in'): - _logger.warning("The domain term '%s' should use the '=' or '!=' operator." % ((left, original, right),)) - operator = '=' if operator == 'in' else '!=' - if isinstance(right, (list, tuple)) and operator in ('=', '!='): - _logger.warning("The domain term '%s' should use the 'in' or 'not in' operator." % ((left, original, right),)) - operator = 'in' if operator == '=' else 'not in' - return left, operator, right def distribute_not(domain): """ Distribute any '!' domain operators found inside a normalized domain. @@ -308,6 +283,7 @@ def distribute_not(domain): operator = mapping[operator] return [(left, operator, right)] return [NOT_OPERATOR, (left, operator, right)] + def distribute_negate(domain): """Negate the domain ``subtree`` rooted at domain[0], leaving the rest of the domain intact, and return @@ -331,34 +307,268 @@ def distribute_not(domain): done, todo = distribute_negate(domain[1:]) return done + distribute_not(todo) + +# -------------------------------------------------- +# Generic leaf manipulation +# -------------------------------------------------- + +def normalize_leaf(element): + """ Change a term's operator to some canonical form, simplifying later + processing. """ + if not is_leaf(element): + return element + left, operator, right = element + original = operator + operator = operator.lower() + if operator == '<>': + operator = '!=' + if isinstance(right, bool) and operator in ('in', 'not in'): + _logger.warning("The domain term '%s' should use the '=' or '!=' operator." % ((left, original, right),)) + operator = '=' if operator == 'in' else '!=' + if isinstance(right, (list, tuple)) and operator in ('=', '!='): + _logger.warning("The domain term '%s' should use the 'in' or 'not in' operator." % ((left, original, right),)) + operator = 'in' if operator == '=' else 'not in' + return (left, operator, right) + + +def is_operator(element): + """Test whether an object is a valid domain operator. """ + return isinstance(element, basestring) and element in DOMAIN_OPERATORS + + +# TODO change the share wizard to use this function. +def is_leaf(element, internal=False): + """ Test whether an object is a valid domain term: + - is a list or tuple + - with 3 elements + - second element if a valid op + + :param tuple element: a leaf in form (left, operator, right) + :param boolean internal: allow or not the 'inselect' internal operator + in the term. This should be always left to False. + """ + INTERNAL_OPS = TERM_OPERATORS + ('<>',) + if internal: + INTERNAL_OPS += ('inselect',) + return (isinstance(element, tuple) or isinstance(element, list)) \ + and len(element) == 3 \ + and element[1] in INTERNAL_OPS + + +# -------------------------------------------------- +# SQL utils +# -------------------------------------------------- + def select_from_where(cr, select_field, from_table, where_field, where_ids, where_operator): # todo: merge into parent query as sub-query res = [] if where_ids: - if where_operator in ['<','>','>=','<=']: + if where_operator in ['<', '>', '>=', '<=']: cr.execute('SELECT "%s" FROM "%s" WHERE "%s" %s %%s' % \ (select_field, from_table, where_field, where_operator), - (where_ids[0],)) # TODO shouldn't this be min/max(where_ids) ? + (where_ids[0],)) # TODO shouldn't this be min/max(where_ids) ? res = [r[0] for r in cr.fetchall()] - else: # TODO where_operator is supposed to be 'in'? It is called with child_of... + else: # TODO where_operator is supposed to be 'in'? It is called with child_of... for i in range(0, len(where_ids), cr.IN_MAX): - subids = where_ids[i:i+cr.IN_MAX] + subids = where_ids[i:i + cr.IN_MAX] cr.execute('SELECT "%s" FROM "%s" WHERE "%s" IN %%s' % \ (select_field, from_table, where_field), (tuple(subids),)) res.extend([r[0] for r in cr.fetchall()]) return res + def select_distinct_from_where_not_null(cr, select_field, from_table): - cr.execute('SELECT distinct("%s") FROM "%s" where "%s" is not null' % \ - (select_field, from_table, select_field)) + cr.execute('SELECT distinct("%s") FROM "%s" where "%s" is not null' % (select_field, from_table, select_field)) return [r[0] for r in cr.fetchall()] + +# -------------------------------------------------- +# ExtendedLeaf class for managing leafs and contexts +# ------------------------------------------------- + +class ExtendedLeaf(object): + + def __init__(self, leaf, table, context_stack=None): + """ Initialize the ExtendedLeaf + + :attr [string, tuple] leaf: operator or tuple-formatted domain + expression + :attr object table: table object + :attr list _tables: list of chained table objects, updated when + adding joins + :attr tuple elements: manipulation-friendly leaf + :attr object field: field obj, taken from table, not necessarily + found (inherits, 'id') + :attr list field_path: exploded left of elements + (partner_id.name -> ['partner_id', 'name']) + :attr object relational_table: distant table for relational fields + """ + assert table, 'Invalid leaf creation without table' + self.context_stack = context_stack or [] + # validate the leaf + self.leaf = leaf + # normalize the leaf's operator + self.normalize_leaf() + # set working variables; handle the context stack and previous tables + self.table = table + self._tables = [] + for item in self.context_stack: + self._tables.append(item[0]) + self._tables.append(table) + # if leaf.is_operator: + # self.elements = self.leaf, None, None + # elif leaf.is_true_leaf() or leaf.is_false_leaf(): + # # because we consider left as a string + # self.elements = ('%s' % leaf.leaf[0], leaf.leaf[1], leaf.leaf[2]) + # else: + # self.elements = leaf.leaf + # self.field_path = self.elements[0].split('.', 1) + # self.field = self.table._columns.get(self.field_path[0]) + # if self.field and self.field._obj: + # relational_table = working_table.pool.get(field._obj) + # else: + # relational_table = None + # check validity + self.check_leaf() + + def __str__(self): + return '' % (str(self.leaf), self.table._table, ','.join(self._get_context_debug())) + + def create_substitution_leaf(self, new_leaf, new_table=None): + if new_table is None: + new_table = self.table + return ExtendedLeaf(new_leaf, new_table, self.context_stack) + + def create_sibling_leaf(self, new_leaf): + pass + + # -------------------------------------------------- + # Join / Context manipulation + # running examples: + # - res_users.name, like, foo: name is on res_partner, not on res_users + # - res_partner.bank_ids.name, like, foo: bank_ids is a one2many with _auto_join + # - res_partner.state_id.name, like, foo: state_id is a many2one with _auto_join + # A join: + # - link between src_table and dst_table, using src_field and dst_field + # i.e.: inherits: res_users.partner_id = res_partner.id + # i.e.: one2many: res_partner.id = res_partner_bank.partner_id + # i.e.: many2one: res_partner.state_id = res_country_state.id + # - done in the context of a field + # i.e.: inherits: 'partner_id' + # i.e.: one2many: 'bank_ids' + # i.e.: many2one: 'state_id' + # - table names use aliases: initial table followed by the context field + # names, joined using a '__' + # i.e.: inherits: res_partner as res_users__partner_id + # i.e.: one2many: res_partner_bank as res_partner__bank_ids + # i.e.: many2one: res_country_state as res_partner__state_id + # - join condition use aliases + # i.e.: inherits: res_users.partner_id = res_users__partner_id.id + # i.e.: one2many: res_partner.id = res_partner__bank_ids.partner_id + # i.e.: many2one: res_partner.state_id = res_partner__state_id.id + # Variables explanation: + # - src_table: working table before the join + # -> res_users, res_partner, res_partner + # - dst_table: working table after the join + # -> res_partner, res_partner_bank, res_country_state + # - src_table_link_name: field name used to link the src table, not + # necessarily a field (because 'id' is not a field instance) + # i.e.: inherits: 'partner_id', found in the inherits of the current table + # i.e.: one2many: 'id', not a field + # i.e.: many2one: 'state_id', the current field name + # - dst_table_link_name: field name used to link the dst table, not + # necessarily a field (because 'id' is not a field instance) + # i.e.: inherits: 'id', not a field + # i.e.: one2many: 'partner_id', _fields_id of the current field + # i.e.: many2one: 'id', not a field + # - context_field_name: field name used as a context to make the alias + # i.e.: inherits: 'partner_id': found in the inherits of the current table + # i.e.: one2many: 'bank_ids': current field name + # i.e.: many2one: 'state_id': current field name + # -------------------------------------------------- + + def _generate_alias(self): + alias = self._tables[0]._table + for context in self.context_stack: + alias += '__' + context[4] + return alias + + def add_join_context(self, context_field_name, src_table_link_name, dst_table, dst_table_link_name): + """ See above comments for more details. A join context is a tuple structure + holding the following elements: (src_table (self.table), src_table_link_name, + dst_table, dst_table_link_name, context_field_name) + After adding the join, the table of the current leaf is updated. + """ + self.context_stack.append((self.table, src_table_link_name, dst_table, dst_table_link_name, context_field_name)) + self._tables.append(dst_table) + self.table = dst_table + + def get_join_conditions(self): + names = [] + alias = self._tables[0]._table + for context in self.context_stack: + previous_alias = alias + alias += '__' + context[4] + names.append('%s."%s"=%s."%s"' % (previous_alias, context[1], alias, context[3])) + return names + + def get_tables(self): + tables = set() + alias = self._tables[0]._table + for context in self.context_stack: + alias += '__' + context[4] + table_full_alias = '"%s" as %s' % (context[2]._table, alias) + tables.add(table_full_alias) + return tables + + def _get_context_debug(self): + names = ['%s."%s"=%s."%s" (%s)' % (table[0]._table, table[1], table[2]._table, table[3], table[4]) for table in self.context_stack] + return names + + # -------------------------------------------------- + # Leaf manipulation + # -------------------------------------------------- + + def check_leaf(self): + """ Leaf validity rules: + - a valid leaf is an operator or a leaf + - a valid leaf has a field objects unless + - it is not a tuple + - it is an inherited field + - left is id, operator is 'child_of' + - left is in MAGIC_COLUMNS + """ + if not is_operator(self.leaf) and not is_leaf(self.leaf, True): + raise ValueError("Invalid leaf %s" % str(self.leaf)) + + # if self.is_leaf() and not (self.is_true_leaf() or self.is_false_leaf()) \ + # and not self.field and not self.field_path[0] in self.table._inherit_fields \ + # and not (self.leaf[0] == 'id' and self.leaf[1] == 'child_of') \ + # and not (self.field_path[0] in MAGIC_COLUMNS): + # raise ValueError("Invalid field %r in leaf %r" % (self.leaf[0], self.leaf)) + + def is_operator(self): + return is_operator(self.leaf) + + def is_true_leaf(self): + return self.leaf == TRUE_LEAF + + def is_false_leaf(self): + return self.leaf == FALSE_LEAF + + def is_leaf(self, internal=False): + return is_leaf(self.leaf, internal=internal) + + def normalize_leaf(self): + self.leaf = normalize_leaf(self.leaf) + return True + + class expression(object): - """ - parse a domain expression - use a real polish notation - leafs are still in a ('foo', '=', 'bar') format - For more info: http://christophe-simonis-at-tiny.blogspot.com/2008/08/new-new-domain-notation.html + """ Parse a domain expression + Use a real polish notation + Leafs are still in a ('foo', '=', 'bar') format + For more info: http://christophe-simonis-at-tiny.blogspot.com/2008/08/new-new-domain-notation.html """ def __init__(self, cr, uid, exp, table, context): @@ -368,329 +578,301 @@ class expression(object): :param exp: expression (using domain ('foo', '=', 'bar' format)) :param table: root table object - :attr dict leaf_to_table: used to store the table to use for the - sql generation, according to the domain leaf. - structure: { [leaf index]: table object } - :attr set table_aliases: set of aliases. Previously this attribute - was a set of table objects; now that joins generation is included - into the expression parsing, it holds aliases, and a mapping - exist linking aliases to tables. - :attr dict table_aliases_mapping: mapping alias -> table object - :attr list joins: list of join conditions, such as (res_country_state."id" = res_partner."state_id") - :attr root_table: root table, set by parse() + :attr list result: list that will hold the result of the parsing + as a list of ExtendedLeaf + :attr list joins: list of join conditions, such as + (res_country_state."id" = res_partner."state_id") + :attr root_table: base table for the query + :attr list expression: the domain expression, that will be normalized + and prepared """ self.has_unaccent = openerp.modules.registry.RegistryManager.get(cr.dbname).has_unaccent - self.leaf_to_table = {} - self.table_aliases = set() - self.table_aliases_mapping = {} + self.result = [] self.joins = [] - self.root_table = None - # assign self.exp with the normalized, parsed domain. - self.parse(cr, uid, distribute_not(normalize(exp)), table, context) + self.root_table = table - # TDE note: this seems not to be used anymore, commenting - # # TODO used only for osv_memory - # @property - # def exp(self): - # return self.exp[:] + # normalize and prepare the expression for parsing + self.expression = distribute_not(normalize_domain(exp)) - def _has_table_alias(self, alias): - return alias in self.table_aliases + # parse the domain expression + self.parse(cr, uid, context=context) - def _get_table_from_alias(self, alias): - return self.table_aliases_mapping.get(alias) + # ---------------------------------------- + # Tools for domain manipulation + # ---------------------------------------- - def _get_full_alias(self, alias): - if not self._get_table_from_alias(alias): - return False - table_name = self._get_table_from_alias(alias)._table - if table_name == alias: - return '"%s"' % alias + def to_ids(self, cr, uid, value, relational_table, context=None, limit=None): + """ Normalize a single id or name, or a list of those, into a list of ids + :param {int,long,basestring,list,tuple} value: + if int, long -> return [value] + if basestring, convert it into a list of basestrings, then + if list of basestring -> + perform a name_search on relational_table for each name + return the list of related ids + """ + names = [] + if isinstance(value, basestring): + names = [value] + elif value and isinstance(value, (tuple, list)) and all(isinstance(item, basestring) for item in value): + names = value + elif isinstance(value, (int, long)): + return [value] + if names: + name_get_list = [name_get[0] for name in names for name_get in relational_table.name_search(cr, uid, name, [], 'ilike', context=context, limit=limit)] + return list(set(name_get_list)) + return list(value) + + def child_of_domain(self, cr, uid, left, ids, left_model, parent=None, prefix='', context=None): + """ Return a domain implementing the child_of operator for [(left,child_of,ids)], + either as a range using the parent_left/right tree lookup fields + (when available), or as an expanded [(left,in,child_ids)] """ + if left_model._parent_store and (not left_model.pool._init): + # TODO: Improve where joins are implemented for many with '.', replace by: + # doms += ['&',(prefix+'.parent_left','<',o.parent_right),(prefix+'.parent_left','>=',o.parent_left)] + doms = [] + for o in left_model.browse(cr, uid, ids, context=context): + if doms: + doms.insert(0, OR_OPERATOR) + doms += [AND_OPERATOR, ('parent_left', '<', o.parent_right), ('parent_left', '>=', o.parent_left)] + if prefix: + return [(left, 'in', left_model.search(cr, uid, doms, context=context))] + return doms else: - return '"%s" as %s' % (table_name, alias) + def recursive_children(ids, model, parent_field): + if not ids: + return [] + ids2 = model.search(cr, uid, [(parent_field, 'in', ids)], context=context) + return ids + recursive_children(ids2, model, parent_field) + return [(left, 'in', recursive_children(ids, left_model, parent or left_model._parent_name))] - def _add_table_alias(self, alias, table): - if not self._has_table_alias(alias): - self.table_aliases.add(alias) - self.table_aliases_mapping[alias] = table - else: - raise ValueError("Already existing alias %s for table %s, trying to set it for table %s" % (alias, self._get_table_from_alias(alias)._table, table._table)) + # ---------------------------------------- + # Internal structure + # ---------------------------------------- + + def _format_table_name(self, table_name): + return '"%s"' % (table_name) def get_tables(self): """ Returns the list of tables for SQL queries, like select from ... """ - return [self._get_full_alias(item) for item in self.table_aliases] + tables = [] + for leaf in self.result: + for table in leaf.get_tables(): + if table not in tables: + tables.append(table) + table_name = '"%s"' % self.root_table._table + if table_name not in tables: + tables.append(table_name) + return tables - def parse(self, cr, uid, exp, table, context): + # ---------------------------------------- + # Parsing + # ---------------------------------------- + + def parse(self, cr, uid, context): """ Transform the leaves of the expression - For each element in the expression - 1. validity check: operator or True / False or a valid leaf - 2. TDE FIXME: TO COMPLETE + The principle is to pop elements from the left of a leaf stack. Each + leaf is processed. The processing is a if/elif list of various cases + that appear in the leafs (many2one, function fields, ...). Two results + can appear at the end of a leaf processing: + - the leaf is modified or new leafs introduced in the domain: they + are added at the left of the stack, to be processed next + - the leaf is added to the result - Some var explanation for those who have 2 bytes of brain cache memory like me and that cannot remember 32^16 similar variable names. - :var obj working_table: table object, table containing the field (the name provided in the left operand) + Some var explanation: + :var obj working_table: table object, table containing the field + (the name provided in the left operand) :var list field_path: left operand seen as a path (foo.bar -> [foo, bar]) - :var string field_table: working_table._table :var obj relational_table: relational table of a field (field._obj) ex: res_partner.bank_ids -> res_partner_bank - :var obj field_obj: deleted var, now renamed to relational_table - - :param exp: expression (domain) - :param table: table object """ - self.exp = exp - self.root_table = table - self._add_table_alias(table._table, table) + result = [] + stack = [ExtendedLeaf(leaf, self.root_table) for leaf in self.expression] - def child_of_domain(left, ids, left_model, parent=None, prefix=''): - """Returns a domain implementing the child_of operator for [(left,child_of,ids)], - either as a range using the parent_left/right tree lookup fields (when available), - or as an expanded [(left,in,child_ids)]""" - if left_model._parent_store and (not left_model.pool._init): - # TODO: Improve where joins are implemented for many with '.', replace by: - # doms += ['&',(prefix+'.parent_left','<',o.parent_right),(prefix+'.parent_left','>=',o.parent_left)] - doms = [] - for o in left_model.browse(cr, uid, ids, context=context): - if doms: - doms.insert(0, OR_OPERATOR) - doms += [AND_OPERATOR, ('parent_left', '<', o.parent_right), ('parent_left', '>=', o.parent_left)] - if prefix: - return [(left, 'in', left_model.search(cr, uid, doms, context=context))] - return doms + while stack: + # Get the next leaf to process + leaf = stack.pop(0) + leafs_to_stack = [] + results_to_stack = [] + + # Get working variables + working_table = leaf.table + if leaf.is_operator(): + left, operator, right = leaf.leaf, None, None + elif leaf.is_true_leaf() or leaf.is_false_leaf(): + # because we consider left as a string + left, operator, right = ('%s' % leaf.leaf[0], leaf.leaf[1], leaf.leaf[2]) else: - def recursive_children(ids, model, parent_field): - if not ids: - return [] - ids2 = model.search(cr, uid, [(parent_field, 'in', ids)], context=context) - return ids + recursive_children(ids2, model, parent_field) - return [(left, 'in', recursive_children(ids, left_model, parent or left_model._parent_name))] - - def to_ids(value, relational_table): - """Normalize a single id or name, or a list of those, into a list of ids""" - names = [] - if isinstance(value, basestring): - names = [value] - if value and isinstance(value, (tuple, list)) and isinstance(value[0], basestring): - names = value - if names: - return flatten([[x[0] for x in relational_table.name_search(cr, uid, n, [], 'ilike', context=context, limit=None)] \ - for n in names]) - elif isinstance(value, (int, long)): - return [value] - return list(value) - - i = -1 - while i + 1 < len(self.exp): - i += 1 - - # 0 Preparation - # - Check validity of current element of expression (operator OR True/False leaf OR leaf) - # - Normalize the leaf's operator - # - Set working variables - - # check validity - e = self.exp[i] - if is_operator(e) or e == TRUE_LEAF or e == FALSE_LEAF: - continue - if not is_leaf(e): - raise ValueError("Invalid term %r in domain expression %r" % (e, exp)) - - # normalize the leaf's operator - e = normalize_leaf(*e) - self.exp[i] = e - left, operator, right = e - - # working variables - working_table = table # The table containing the field (the name provided in the left operand) + left, operator, right = leaf.leaf + # field_path = leaf.field_path + # field = leaf.field field_path = left.split('.', 1) - - # 1 Extract field - # - Try to directly extract the field - # - Handle inherits fields: replace by a join, find the final new - # working table and extract the field - field = working_table._columns.get(field_path[0]) - if field_path[0] in table._inherit_fields: - while not field: - next_table = working_table.pool.get(working_table._inherit_fields[field_path[0]][0]) - if not self._has_table_alias(next_table._table): - self.joins.append('%s."%s"=%s."%s"' % (next_table._table, 'id', working_table._table, working_table._inherits[next_table._name])) - self._add_table_alias(next_table._table, next_table) - working_table = next_table - field = working_table._columns.get(field_path[0]) - self.leaf_to_table[i] = working_table - # 2 Field not found - # - ('id', 'child_of', ids): replace the leaf by a computed domain - # after searching and continue the processing OR - # - field in magic columns (ex: id): continue the processing OR - # - raise an error + if field and field._obj: + relational_table = working_table.pool.get(field._obj) + else: + relational_table = None - if not field: - if left == 'id' and operator == 'child_of': - ids2 = to_ids(right, table) - dom = child_of_domain(left, ids2, working_table) - self.exp = self.exp[:i] + dom + self.exp[i + 1:] - else: - # field could not be found in model columns, it's probably invalid, unless - # it's one of the _log_access special fields - # TODO: make these fields explicitly available in self.columns instead! - if field_path[0] not in MAGIC_COLUMNS: - raise ValueError("Invalid field %r in domain expression %r" % (left, exp)) - continue + # ---------------------------------------- + # SIMPLE CASE + # 1. leaf is an operator + # 2. leaf is a true/false leaf + # -> add directly to result + # ---------------------------------------- - # 3 Field found - # - update working variables - # - get relational table that exists for relational fields - # - prepare the alias for tables, that can be the table name if only one-level - # - if domain is a path (ex: ('partner_id.name', '=', 'foo')): - # TDE TO ADD: once fully implemented, explain auto_join - # replace all the expression by a normalized equivalent domain - # - find the related ids: partner_id.name='foo' -> res_partner.search(('name', '=', 'foo'))) - # - many2one: leaf becomes directly ('partner_id', 'in', 'partner_ids') - # - one2many: - # - search on current table where partner_id is in partner_ids - # - leaf becomes ('id', 'in', ids) - # - get out of current leaf is field is not a property field - # - if domain is not a path: handle some leaf replacement / tweaking - # - handle function fields - # - handle one2many, many2many and many2one fields - # - other fields: handle datetime and translatable fields + if leaf.is_operator(): + results_to_stack.append(leaf) + elif leaf.is_true_leaf() or leaf.is_false_leaf(): + results_to_stack.append(leaf) - relational_table = table.pool.get(field._obj) - alias = working_table._table + # ---------------------------------------- + # FIELD NOT FOUND + # -> from inherits'd fields -> work on the related table, and add + # a join condition + # -> ('id', 'child_of', '..') -> use a 'to_ids' + # -> but is one on the _log_access special fields, add directly to + # result + # TODO: make these fields explicitly available in self.columns instead! + # -> else: crash + # ---------------------------------------- - while len(field_path) > 1 and field._auto_join: - assert field._type in ['many2one', 'one2many'], \ + elif not field and field_path[0] in working_table._inherit_fields: + # comments about inherits'd fields + # { 'field_name': ('parent_model', 'm2o_field_to_reach_parent', + # field_column_obj, origina_parent_model), ... } + next_table = working_table.pool.get(working_table._inherit_fields[field_path[0]][0]) + leaf.add_join_context(working_table._inherits[next_table._name], working_table._inherits[next_table._name], next_table, 'id') + leafs_to_stack.append(leaf) + + elif not field and left == 'id' and operator == 'child_of': + ids2 = self.to_ids(cr, uid, right, working_table, context) + dom = self.child_of_domain(cr, uid, left, ids2, working_table) + leafs_to_stack += [leaf.create_substitution_leaf(dom_leaf, working_table) for dom_leaf in dom] + + elif not field and field_path[0] in MAGIC_COLUMNS: + results_to_stack.append(leaf) + + elif not field: + raise ValueError("Invalid field %r in leaf %r" % (left, leaf)) + + # ---------------------------------------- + # PATH SPOTTED + # -> XX + # -> note: hack about fields.property should not be necessary anymore + # as after transforming the field, it will go through this loop once again + # ---------------------------------------- + + elif len(field_path) > 1 and field._type == 'many2one' and field._auto_join: + # res_partner.parent_id = res_partner.id + leaf.add_join_context(field_path[0], field_path[0], relational_table, 'id') + leafs_to_stack.append(leaf.create_substitution_leaf((field_path[1], operator, right), relational_table)) + + elif len(field_path) > 1 and field._type == 'one2many' and field._auto_join: + # res_partner.id = res_partner.parent_id + leaf.add_join_context(field_path[0], 'id', relational_table, field._fields_id) + leafs_to_stack.append(leaf.create_substitution_leaf((field_path[1], operator, right), relational_table)) + + elif len(field_path) > 1 and field._auto_join: + assert False, \ '_auto_join attribute on something else than a many2one or one2many is currently not supported' - previous_alias = alias - alias = alias + '__' + field_path[0] - if not self._get_table_from_alias(alias): - self._add_table_alias(alias, relational_table) - if field._type == 'many2one': - self.joins.append('%s."%s"=%s."%s"' % (alias, 'id', previous_alias, field_path[0])) - elif field._type == 'one2many': - self.joins.append('%s."%s"=%s."%s"' % (alias, field._fields_id, previous_alias, 'id')) - self.exp[i] = (alias + '.' + field_path[1], self.exp[i][1], self.exp[i][2]) + elif len(field_path) > 1 and field._type == 'many2one': + right_ids = relational_table.search(cr, uid, [(field_path[1], operator, right)], context=context) + leaf.leaf = (field_path[0], 'in', right_ids) + leafs_to_stack.append(leaf) - # udpate working variables - field_path = field_path[1].split('.', 1) - working_table = relational_table - field = working_table._columns.get(field_path[0]) + # Making search easier when there is a left operand as field.o2m or field.m2m + elif len(field_path) > 1 and field._type in ['many2many', 'one2many']: + right_ids = relational_table.search(cr, uid, [(field_path[1], operator, right)], context=context) + table_ids = working_table.search(cr, uid, [(field_path[0], 'in', right_ids)], context=dict(context, active_test=False)) + leaf.leaf = ('id', 'in', table_ids) + leafs_to_stack.append(leaf) - if not field: - if left == 'id' and operator == 'child_of': - ids2 = to_ids(right, table) - dom = child_of_domain(left, ids2, working_table) - self.exp = self.exp[:i] + dom + self.exp[i + 1:] - else: - # field could not be found in model columns, it's probably invalid, unless - # it's one of the _log_access special fields - # TODO: make these fields explicitly available in self.columns instead! - if field_path[0] not in MAGIC_COLUMNS: - raise ValueError("Invalid field %r in domain expression %r" % (left, exp)) - break + # ---------------------------------------- + # FUNCTION FIELD + # -> not stored, get the result of fnct_search + # ---------------------------------------- - # moved on top - relational_table = table.pool.get(field._obj) - - if not field: - continue - - if len(field_path) > 1: - if field._type == 'many2one': - right = relational_table.search(cr, uid, [(field_path[1], operator, right)], context=context) - self.exp[i] = (alias + '.' + field_path[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 = relational_table.search(cr, uid, [(field_path[1], operator, right)], context=context) - right1 = table.search(cr, uid, [(field_path[0], 'in', right)], context=dict(context, active_test=False)) - self.exp[i] = (alias + '.id', 'in', right1) - - if not isinstance(field, fields.property): - continue - - if field._properties and not field.store: + elif field._properties and not field.store and not field._fnct_search: # this is a function field that is not stored - if not field._fnct_search: - # the function field doesn't provide a search function and doesn't store - # values in the database, so we must ignore it : we generate a dummy leaf - self.exp[i] = TRUE_LEAF - _logger.error( - "The field '%s' (%s) can not be searched: non-stored " - "function field without fnct_search", - field.string, left) - # avoid compiling stack trace if not needed - if _logger.isEnabledFor(logging.DEBUG): - _logger.debug(''.join(traceback.format_stack())) + # the function field doesn't provide a search function and doesn't store + # values in the database, so we must ignore it : we generate a dummy leaf + leaf.leaf = TRUE_LEAF + _logger.error( + "The field '%s' (%s) can not be searched: non-stored " + "function field without fnct_search", + field.string, left) + # avoid compiling stack trace if not needed + if _logger.isEnabledFor(logging.DEBUG): + _logger.debug(''.join(traceback.format_stack())) + leafs_to_stack.append(leaf) + + elif field._properties and not field.store: + # this is a function field that is not stored + fct_domain = field.search(cr, uid, working_table, left, [leaf.leaf], context=context) + if not fct_domain: + leaf.leaf = TRUE_LEAF + leafs_to_stack.append(leaf) else: - assert working_table == table, \ - 'function fields can not be contained in paths using _auto_join' - subexp = field.search(cr, uid, table, left, [self.exp[i]], context=context) - if not subexp: - self.exp[i] = TRUE_LEAF - else: - # we assume that the expression is valid - # we create a dummy leaf for forcing the parsing of the resulting expression - self.exp[i] = AND_OPERATOR - self.exp.insert(i + 1, TRUE_LEAF) - for j, se in enumerate(subexp): - self.exp.insert(i + 2 + j, se) - # else, the value of the field is store in the database, so we search on it + # we assume that the expression is valid + # we create a dummy leaf for forcing the parsing of the resulting expression + leafs_to_stack.append(leaf.create_substitution_leaf(AND_OPERATOR, working_table)) + leafs_to_stack.append(leaf.create_substitution_leaf(TRUE_LEAF, working_table)) + for domain_element in fct_domain: + leafs_to_stack.append(leaf.create_substitution_leaf(domain_element, working_table)) + + # Applying recursivity on field(one2many) + elif field._type == 'one2many' and operator == 'child_of': + ids2 = self.to_ids(cr, uid, right, relational_table, context) + if field._obj != working_table._name: + dom = self.child_of_domain(cr, uid, left, ids2, relational_table, prefix=field._obj) + else: + dom = self.child_of_domain(cr, uid, 'id', ids2, working_table, parent=left) + leafs_to_stack += [leaf.create_substitution_leaf(dom_leaf, working_table) for dom_leaf in dom] elif field._type == 'one2many': - # Applying recursivity on field(one2many) - if operator == 'child_of': - ids2 = to_ids(right, relational_table) - if field._obj != working_table._name: - dom = child_of_domain(left, ids2, relational_table, prefix=field._obj) + call_null = True + + if right is not False: + if isinstance(right, basestring): + ids2 = [x[0] for x in relational_table.name_search(cr, uid, right, [], operator, context=context, limit=None)] + if ids2: + operator = 'in' else: - dom = child_of_domain('id', ids2, working_table, parent=left) - self.exp = self.exp[:i] + dom + self.exp[i + 1:] - - else: - call_null = True - - if right is not False: - if isinstance(right, basestring): - ids2 = [x[0] for x in relational_table.name_search(cr, uid, right, [], operator, context=context, limit=None)] - if ids2: - operator = 'in' + if not isinstance(right, list): + ids2 = [right] else: - if not isinstance(right, list): - ids2 = [right] - else: - ids2 = right - if not ids2: - if operator in ['like', 'ilike', 'in', '=']: - #no result found with given search criteria - call_null = False - self.exp[i] = FALSE_LEAF - else: - ids2 = select_from_where(cr, field._fields_id, relational_table._table, 'id', ids2, operator) - if ids2: - call_null = False - o2m_op = 'not in' if operator in NEGATIVE_TERM_OPERATORS else 'in' - self.exp[i] = ('id', o2m_op, ids2) + ids2 = right + if not ids2: + if operator in ['like', 'ilike', 'in', '=']: + #no result found with given search criteria + call_null = False + leafs_to_stack.append(leaf.create_substitution_leaf(FALSE_LEAF, working_table)) + else: + ids2 = select_from_where(cr, field._fields_id, relational_table._table, 'id', ids2, operator) + if ids2: + call_null = False + o2m_op = 'not in' if operator in NEGATIVE_TERM_OPERATORS else 'in' + leafs_to_stack.append(leaf.create_substitution_leaf(('id', o2m_op, ids2), working_table)) - if call_null: - o2m_op = 'in' if operator in NEGATIVE_TERM_OPERATORS else 'not in' - self.exp[i] = ('id', o2m_op, select_distinct_from_where_not_null(cr, field._fields_id, relational_table._table)) + if call_null: + o2m_op = 'in' if operator in NEGATIVE_TERM_OPERATORS else 'not in' + leafs_to_stack.append(leaf.create_substitution_leaf(('id', o2m_op, select_distinct_from_where_not_null(cr, field._fields_id, relational_table._table)), working_table)) elif field._type == 'many2many': rel_table, rel_id1, rel_id2 = field._sql_names(working_table) #FIXME if operator == 'child_of': def _rec_convert(ids): - if relational_table == table: + if relational_table == working_table: return ids return select_from_where(cr, rel_id1, rel_table, rel_id2, ids, operator) - ids2 = to_ids(right, relational_table) - dom = child_of_domain('id', ids2, relational_table) + ids2 = self.to_ids(cr, uid, right, relational_table, context) + dom = self.child_of_domain(cr, uid, 'id', ids2, relational_table) ids2 = relational_table.search(cr, uid, dom, context=context) - self.exp[i] = ('id', 'in', _rec_convert(ids2)) + leafs_to_stack.append(leaf.create_substitution_leaf(('id', 'in', _rec_convert(ids2)), working_table)) else: call_null_m2m = True if right is not False: @@ -707,26 +889,26 @@ class expression(object): if operator in ['like', 'ilike', 'in', '=']: #no result found with given search criteria call_null_m2m = False - self.exp[i] = FALSE_LEAF + leafs_to_stack.append(leaf.create_substitution_leaf(FALSE_LEAF, working_table)) else: operator = 'in' # operator changed because ids are directly related to main object else: call_null_m2m = False m2m_op = 'not in' if operator in NEGATIVE_TERM_OPERATORS else 'in' - self.exp[i] = ('id', m2m_op, select_from_where(cr, rel_id1, rel_table, rel_id2, res_ids, operator) or [0]) + leafs_to_stack.append(leaf.create_substitution_leaf(('id', m2m_op, select_from_where(cr, rel_id1, rel_table, rel_id2, res_ids, operator) or [0]), working_table)) if call_null_m2m: m2m_op = 'in' if operator in NEGATIVE_TERM_OPERATORS else 'not in' - self.exp[i] = ('id', m2m_op, select_distinct_from_where_not_null(cr, rel_id1, rel_table)) + leafs_to_stack.append(leaf.create_substitution_leaf(('id', m2m_op, select_distinct_from_where_not_null(cr, rel_id1, rel_table)), working_table)) elif field._type == 'many2one': if operator == 'child_of': - ids2 = to_ids(right, relational_table) + ids2 = self.to_ids(cr, uid, right, relational_table, context) if field._obj != working_table._name: - dom = child_of_domain(left, ids2, relational_table, prefix=field._obj) + dom = self.child_of_domain(cr, uid, left, ids2, relational_table, prefix=field._obj) else: - dom = child_of_domain('id', ids2, working_table, parent=left) - self.exp = self.exp[:i] + dom + self.exp[i + 1:] + dom = self.child_of_domain(cr, uid, 'id', ids2, working_table, parent=left) + leafs_to_stack += [leaf.create_substitution_leaf(dom_leaf, working_table) for dom_leaf in dom] else: def _get_expression(relational_table, cr, uid, left, right, operator, context=None): if context is None: @@ -750,26 +932,24 @@ class expression(object): # resolve string-based m2o criterion into IDs if isinstance(right, basestring) or \ right and isinstance(right, (tuple, list)) and all(isinstance(item, basestring) for item in right): - self.exp[i] = _get_expression(relational_table, cr, uid, left, right, operator, context=context) + leafs_to_stack.append(leaf.create_substitution_leaf(_get_expression(relational_table, cr, uid, left, right, operator, context=context), working_table)) else: # right == [] or right == False and all other cases are handled by __leaf_to_sql() - pass + results_to_stack.append(leaf) else: # other field type # add the time part to datetime field when it's not there: - if field._type == 'datetime' and self.exp[i][2] and len(self.exp[i][2]) == 10: - - self.exp[i] = list(self.exp[i]) + if field._type == 'datetime' and right and len(right) == 10: if operator in ('>', '>='): - self.exp[i][2] += ' 00:00:00' + right += ' 00:00:00' elif operator in ('<', '<='): - self.exp[i][2] += ' 23:59:59' + right += ' 23:59:59' - self.exp[i] = tuple(self.exp[i]) + leafs_to_stack.append(leaf.create_substitution_leaf((left, operator, right), working_table)) - if field.translate: + elif field.translate: need_wildcard = operator in ('like', 'ilike', 'not like', 'not ilike') sql_operator = {'=like': 'like', '=ilike': 'ilike'}.get(operator, operator) if need_wildcard: @@ -802,30 +982,49 @@ class expression(object): right, right, ] + leafs_to_stack.append(leaf.create_substitution_leaf(('id', 'inselect', (subselect, params)), working_table)) - self.exp[i] = ('id', 'inselect', (subselect, params)) + else: + results_to_stack.append(leaf) - def __leaf_to_sql(self, leaf, table): + # ---------------------------------------- + # END OF PROCESS OF CURRENT LEAF + # -> results_to_stack elements are added in result + # -> leafs_to_stack elements are inserted back in the processed + # stack to be immediately processed + # ---------------------------------------- + + leafs_to_stack.reverse() + for leaf in results_to_stack: + result.append(leaf) + for leaf in leafs_to_stack: + stack.insert(0, leaf) + + # ---------------------------------------- + # END OF PARSING FULL DOMAIN + # ---------------------------------------- + + self.result = result + + # Generate joins + joins = set() + for leaf in self.result: + joins |= set(leaf.get_join_conditions()) + self.joins = list(joins) + + def __leaf_to_sql(self, eleaf): + table = eleaf.table + leaf = eleaf.leaf left, operator, right = leaf # final sanity checks - should never fail assert operator in (TERM_OPERATORS + ('inselect',)), \ "Invalid operator %r in domain term %r" % (operator, leaf) assert leaf in (TRUE_LEAF, FALSE_LEAF) or left in table._all_columns \ - or left in MAGIC_COLUMNS \ - or ('.' in left and self._has_table_alias(left.split('.')[0])), \ + or left in MAGIC_COLUMNS, \ "Invalid field %r in domain term %r" % (left, leaf) - if not leaf in (TRUE_LEAF, FALSE_LEAF) and '.' in left: - # leaf still contains '.' -> should be aliases (alias.field) - # update table with alias, and left with field - leaf_path = left.split('.') - assert len(leaf_path) == 2, "Invalid leaf with alias %r in leaf %r" % (left, leaf) - table_alias = leaf_path[0] - table = self._get_table_from_alias(table_alias) - left = leaf_path[1] - else: - table_alias = table._table + table_alias = '"%s"' % (eleaf._generate_alias()) if leaf == TRUE_LEAF: query = 'TRUE' @@ -946,25 +1145,26 @@ class expression(object): stack = [] params = [] # Process the domain from right to left, using a stack, to generate a SQL expression. - for i, e in reverse_enumerate(self.exp): - if is_leaf(e, internal=True): - table = self.leaf_to_table.get(i, self.root_table) - q, p = self.__leaf_to_sql(e, table) + self.result.reverse() + for leaf in self.result: + if leaf.is_leaf(internal=True): + q, p = self.__leaf_to_sql(leaf) params.insert(0, p) stack.append(q) - elif e == NOT_OPERATOR: + elif leaf.leaf == NOT_OPERATOR: stack.append('(NOT (%s))' % (stack.pop(),)) else: ops = {AND_OPERATOR: ' AND ', OR_OPERATOR: ' OR '} q1 = stack.pop() q2 = stack.pop() - stack.append('(%s %s %s)' % (q1, ops[e], q2,)) + stack.append('(%s %s %s)' % (q1, ops[leaf.leaf], q2,)) assert len(stack) == 1 query = stack[0] joins = ' AND '.join(self.joins) if joins: query = '(%s) AND %s' % (joins, query) - return (query, flatten(params)) + + return (query, tools.flatten(params)) # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 7c5b0622db7..6c37b4fed8f 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -2721,11 +2721,10 @@ class BaseModel(object): parent_table_name = parent_model._table quoted_parent_table_name = '"%s"' % parent_table_name if quoted_parent_table_name not in query.tables: + print '--> _inheratis_join_add adding %s' % (quoted_parent_table_name) query.tables.append(quoted_parent_table_name) query.where_clause.append('(%s.%s = %s.id)' % (current_table._table, inherits_field, parent_table_name)) - - def _inherits_join_calc(self, field, query): """ Adds missing table select and join clause(s) to ``query`` for reaching @@ -4658,8 +4657,10 @@ class BaseModel(object): query.where_clause += added_clause query.where_clause_params += added_params for table in added_tables: - if table not in query.tables: - query.tables.append(table) + quoted_table_name = '%s' % (table) + if quoted_table_name not in query.tables: + print '--> apply_rule adding %s' % quoted_table_name + query.tables.append(quoted_table_name) return True return False diff --git a/openerp/osv/query.py b/openerp/osv/query.py index 252b301d531..d32ea73b29d 100644 --- a/openerp/osv/query.py +++ b/openerp/osv/query.py @@ -102,7 +102,7 @@ class Query(object): tables_to_process = list(self.tables) def add_joins_for_table(table, query_from): - for (dest_table, lhs_col, col, join) in self.joins.get(table,[]): + for (dest_table, lhs_col, col, join) in self.joins.get(table, []): tables_to_process.remove(dest_table) query_from += ' %s %s ON (%s."%s" = %s."%s")' % \ (join, dest_table, table, lhs_col, dest_table, col) @@ -114,7 +114,7 @@ class Query(object): if table in self.joins: query_from = add_joins_for_table(table, query_from) query_from += ',' - query_from = query_from[:-1] # drop last comma + query_from = query_from[:-1] # drop last comma return (query_from, " AND ".join(self.where_clause), self.where_clause_params) def __str__(self): diff --git a/openerp/tests/test_expression.py b/openerp/tests/test_expression.py index dd78e3fa88c..1c5678becf1 100644 --- a/openerp/tests/test_expression.py +++ b/openerp/tests/test_expression.py @@ -2,11 +2,12 @@ import unittest2 import openerp + class test_domain_normalization(unittest2.TestCase): def test_normalize_domain(self): expression = openerp.osv.expression - norm_domain = domain = ['&',(1,'=',1),('a','=','b')] - assert norm_domain == expression.normalize(domain), "Normalized domains should be left untouched" - domain = [('x','in',['y','z']),('a.v','=','e'),'|','|',('a','=','b'),'!',('c','>','d'),('e','!=','f'),('g','=','h')] - norm_domain = ['&','&','&'] + domain - assert norm_domain == expression.normalize(domain), "Non-normalized domains should be properly normalized" + norm_domain = domain = ['&', (1, '=', 1), ('a', '=', 'b')] + assert norm_domain == expression.normalize_domain(domain), "Normalized domains should be left untouched" + domain = [('x', 'in', ['y', 'z']), ('a.v', '=', 'e'), '|', '|', ('a', '=', 'b'), '!', ('c', '>', 'd'), ('e', '!=', 'f'), ('g', '=', 'h')] + norm_domain = ['&', '&', '&'] + domain + assert norm_domain == expression.normalize_domain(domain), "Non-normalized domains should be properly normalized" From 52bd6459eff3ce8a6e0db7206b3db8837f39f24e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 4 Dec 2012 15:56:08 +0100 Subject: [PATCH 33/80] [REV] ir_ui_menu: needaction on menus back to their previous state (as in current trunk as ok 2012/12/04; another branch exist for speeding them up, this one is for improving the parsing. bzr revid: tde@openerp.com-20121204145608-lah4uk40ok5litf0 --- openerp/addons/base/ir/ir_ui_menu.py | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/openerp/addons/base/ir/ir_ui_menu.py b/openerp/addons/base/ir/ir_ui_menu.py index cf0f07f42da..e286466b85c 100644 --- a/openerp/addons/base/ir/ir_ui_menu.py +++ b/openerp/addons/base/ir/ir_ui_menu.py @@ -265,18 +265,7 @@ class ir_ui_menu(osv.osv): return res - def _get_needaction_enabled(self, cr, uid, ids, field_names, args, context=None): - """ needaction_enabled: tell whether the menu has a related action - that uses the needaction mechanism. """ - res = dict.fromkeys(ids, False) - for menu in self.browse(cr, uid, ids, context=context): - if menu.action and menu.action.type in ('ir.actions.act_window', 'ir.actions.client') and menu.action.res_model: - obj = self.pool.get(menu.action.res_model) - if obj and obj._needaction: - res[menu.id] = True - return res - - def get_needaction_data(self, cr, uid, ids, context=None): + def get_needaction_data(self, cr, uid, ids, field_names, args, context=None): """ Return for each menu entry of ids : - if it uses the needaction mechanism (needaction_enabled) - the needaction counter of the related action, taking into account @@ -315,11 +304,15 @@ class ir_ui_menu(osv.osv): 'web_icon_hover': fields.char('Web Icon File (hover)', size=128), 'web_icon_data': fields.function(_get_image_icon, string='Web Icon Image', type='binary', readonly=True, store=True, multi='icon'), 'web_icon_hover_data': fields.function(_get_image_icon, string='Web Icon Image (hover)', type='binary', readonly=True, store=True, multi='icon'), - 'needaction_enabled': fields.function(_get_needaction_enabled, - type='boolean', + 'needaction_enabled': fields.function(get_needaction_data, + type='boolean', multi='get_needaction_data', store=True, string='Target model uses the need action mechanism', help='If the menu entry action is an act_window action, and if this action is related to a model that uses the need_action mechanism, this field is set to true. Otherwise, it is false.'), + 'needaction_counter': fields.function(get_needaction_data, + type='integer', multi='get_needaction_data', + string='Number of actions the user has to perform', + help='If the target model uses the need action mechanism, this field gives the number of actions the current user has to perform.'), 'action': fields.function(_action, fnct_inv=_action_inv, type='reference', string='Action', selection=[ From 83d1e7cdf22d99d1a2269b84d805c541d6f847c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 4 Dec 2012 15:56:45 +0100 Subject: [PATCH 34/80] [IMP] mail_message: notification_ids as _auto_join; mail_thread: mail_message as _auto_join. Updated search domain of function fields accordingly. bzr revid: tde@openerp.com-20121204145645-vea4i1p93zmwztqx --- addons/mail/mail_message.py | 16 +++------------- addons/mail/mail_thread.py | 13 ++----------- addons/mail/mail_thread_view.xml | 1 - 3 files changed, 5 insertions(+), 25 deletions(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index aec106f766b..95c34ad5c51 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -98,15 +98,7 @@ class mail_message(osv.Model): def _search_to_read(self, cr, uid, obj, name, domain, context=None): """ Search for messages to read by the current user. Condition is inversed because we search unread message on a read column. """ - if domain[0][2]: - read_cond = "(read = False OR read IS NULL)" - else: - read_cond = "read = True" - partner_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] - cr.execute("SELECT message_id FROM mail_notification "\ - "WHERE partner_id = %%s AND %s" % read_cond, - (partner_id,)) - return [('id', 'in', [r[0] for r in cr.fetchall()])] + return ['&', ('notification_ids.partner_id.user_ids', 'in', [uid]), ('notification_ids.read', '=', not domain[0][2])] def name_get(self, cr, uid, ids, context=None): # name_get may receive int id instead of an id list @@ -146,7 +138,7 @@ class mail_message(osv.Model): store=True, string='Message Record Name', help="Name get of the related document."), 'notification_ids': fields.one2many('mail.notification', 'message_id', - string='Notifications', + string='Notifications', _auto_join=True, help='Technical field holding the message notifications. Use notified_partner_ids to access notified partners.'), 'subject': fields.char('Subject'), 'date': fields.datetime('Date'), @@ -166,9 +158,7 @@ class mail_message(osv.Model): } def _needaction_domain_get(self, cr, uid, context=None): - if self._needaction: - return [('to_read', '=', True)] - return [] + return [('to_read', '=', True)] def _get_default_author(self, cr, uid, context=None): return self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 30954be4ed8..d308105d81e 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -122,17 +122,7 @@ class mail_thread(osv.AbstractModel): return res def _search_unread(self, cr, uid, obj=None, name=None, domain=None, context=None): - partner_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] - res = {} - notif_obj = self.pool.get('mail.notification') - notif_ids = notif_obj.search(cr, uid, [ - ('partner_id', '=', partner_id), - ('message_id.model', '=', self._name), - ('read', '=', False) - ], context=context) - for notif in notif_obj.browse(cr, uid, notif_ids, context=context): - res[notif.message_id.res_id] = True - return [('id', 'in', res.keys())] + return ['&', ('message_ids.model', '=', self._name), ('message_ids.notification_ids.to_read', '=', False)] def _get_followers(self, cr, uid, ids, name, arg, context=None): fol_obj = self.pool.get('mail.followers') @@ -207,6 +197,7 @@ class mail_thread(osv.AbstractModel): help="Comments and emails"), 'message_ids': fields.one2many('mail.message', 'res_id', domain=lambda self: [('model', '=', self._name)], + _auto_join=True, string='Messages', help="Messages and communication history"), 'message_unread': fields.function(_get_message_data, fnct_search=_search_unread, diff --git a/addons/mail/mail_thread_view.xml b/addons/mail/mail_thread_view.xml index 4106eb2e024..1ad0eee3406 100644 --- a/addons/mail/mail_thread_view.xml +++ b/addons/mail/mail_thread_view.xml @@ -11,7 +11,6 @@ } ' % (str(self.leaf), self.table._table, ','.join(self._get_context_debug())) - def create_substitution_leaf(self, new_leaf, new_table=None): - if new_table is None: - new_table = self.table - return ExtendedLeaf(new_leaf, new_table, self.context_stack) + # def create_substitution_leaf(self, new_leaf, new_table=None): + # if new_table is None: + # new_table = self.table + # return ExtendedLeaf(new_leaf, new_table, self.context_stack) - def create_sibling_leaf(self, new_leaf): - pass + # def create_sibling_leaf(self, new_leaf): + # pass # -------------------------------------------------- # Join / Context manipulation @@ -464,7 +464,7 @@ class ExtendedLeaf(object): # i.e.: many2one: res_country_state as res_partner__state_id # - join condition use aliases # i.e.: inherits: res_users.partner_id = res_users__partner_id.id - # i.e.: one2many: res_partner.id = res_partner__bank_ids.partner_id + # i.e.: one2many: res_partner.id = res_partner__bank_ids.parr_id # i.e.: many2one: res_partner.state_id = res_partner__state_id.id # Variables explanation: # - src_table: working table before the join @@ -646,12 +646,9 @@ class expression(object): return [(left, 'in', recursive_children(ids, left_model, parent or left_model._parent_name))] # ---------------------------------------- - # Internal structure + # Leafs management # ---------------------------------------- - def _format_table_name(self, table_name): - return '"%s"' % (table_name) - def get_tables(self): """ Returns the list of tables for SQL queries, like select from ... """ tables = [] @@ -664,6 +661,16 @@ class expression(object): tables.append(table_name) return tables + def create_substitution_leaf(self, leaf, new_elements, new_table=None): + if new_table is None: + new_table = leaf.table + new_context_stack = [tuple(context) for context in leaf.context_stack] + new_leaf = ExtendedLeaf(new_elements, new_table, context_stack=new_context_stack) + return new_leaf + + def create_sibling_leaf(self, leaf, new_elements): + pass + # ---------------------------------------- # Parsing # ---------------------------------------- @@ -697,6 +704,11 @@ class expression(object): # Get working variables working_table = leaf.table + # left, operator, right = leaf.elements + # field_path = leaf.field_path + # field = leaf.field + # relational_table = leaf.relational_table + if leaf.is_operator(): left, operator, right = leaf.leaf, None, None elif leaf.is_true_leaf() or leaf.is_false_leaf(): @@ -748,30 +760,43 @@ class expression(object): elif not field and left == 'id' and operator == 'child_of': ids2 = self.to_ids(cr, uid, right, working_table, context) dom = self.child_of_domain(cr, uid, left, ids2, working_table) - leafs_to_stack += [leaf.create_substitution_leaf(dom_leaf, working_table) for dom_leaf in dom] + leafs_to_stack += [self.create_substitution_leaf(leaf, dom_leaf, working_table) for dom_leaf in dom] elif not field and field_path[0] in MAGIC_COLUMNS: results_to_stack.append(leaf) elif not field: - raise ValueError("Invalid field %r in leaf %r" % (left, leaf)) + raise ValueError("Invalid field %r in leaf %r" % (left, str(leaf))) # ---------------------------------------- # PATH SPOTTED - # -> XX + # -> many2one or one2many with _auto_join: + # - add a join, then jump into linked field: field.remaining on + # src_table is replaced by remaining on dst_table, and set for re-evaluation + # - if a domain is defined on the field, add it into evaluation + # on the relational table + # -> many2one, many2many, one2many: replace by an equivalent computed + # domain, given by recursively searching on the remaining of the path # -> note: hack about fields.property should not be necessary anymore # as after transforming the field, it will go through this loop once again # ---------------------------------------- elif len(field_path) > 1 and field._type == 'many2one' and field._auto_join: - # res_partner.parent_id = res_partner.id + # res_partner.state_id = res_partner__state_id.id leaf.add_join_context(field_path[0], field_path[0], relational_table, 'id') - leafs_to_stack.append(leaf.create_substitution_leaf((field_path[1], operator, right), relational_table)) + leafs_to_stack.append(self.create_substitution_leaf(leaf, (field_path[1], operator, right), relational_table)) elif len(field_path) > 1 and field._type == 'one2many' and field._auto_join: - # res_partner.id = res_partner.parent_id + # res_partner.id = res_partner__bank_ids.partner_id leaf.add_join_context(field_path[0], 'id', relational_table, field._fields_id) - leafs_to_stack.append(leaf.create_substitution_leaf((field_path[1], operator, right), relational_table)) + domain = field._domain(working_table) if callable(field._domain) else field._domain + if domain: + domain = normalize_domain(domain) + leafs_to_stack.append(self.create_substitution_leaf(leaf, AND_OPERATOR, relational_table)) + for elem in domain: + leafs_to_stack.append(self.create_substitution_leaf(leaf, elem, relational_table)) + print '--> appending %s' % str(leafs_to_stack[-1]) + leafs_to_stack.append(self.create_substitution_leaf(leaf, (field_path[1], operator, right), relational_table)) elif len(field_path) > 1 and field._auto_join: assert False, \ @@ -817,10 +842,10 @@ class expression(object): else: # we assume that the expression is valid # we create a dummy leaf for forcing the parsing of the resulting expression - leafs_to_stack.append(leaf.create_substitution_leaf(AND_OPERATOR, working_table)) - leafs_to_stack.append(leaf.create_substitution_leaf(TRUE_LEAF, working_table)) + leafs_to_stack.append(self.create_substitution_leaf(leaf, AND_OPERATOR, working_table)) + leafs_to_stack.append(self.create_substitution_leaf(leaf, TRUE_LEAF, working_table)) for domain_element in fct_domain: - leafs_to_stack.append(leaf.create_substitution_leaf(domain_element, working_table)) + leafs_to_stack.append(self.create_substitution_leaf(leaf, domain_element, working_table)) # Applying recursivity on field(one2many) elif field._type == 'one2many' and operator == 'child_of': @@ -829,7 +854,7 @@ class expression(object): dom = self.child_of_domain(cr, uid, left, ids2, relational_table, prefix=field._obj) else: dom = self.child_of_domain(cr, uid, 'id', ids2, working_table, parent=left) - leafs_to_stack += [leaf.create_substitution_leaf(dom_leaf, working_table) for dom_leaf in dom] + leafs_to_stack += [self.create_substitution_leaf(leaf, dom_leaf, working_table) for dom_leaf in dom] elif field._type == 'one2many': call_null = True @@ -848,17 +873,17 @@ class expression(object): if operator in ['like', 'ilike', 'in', '=']: #no result found with given search criteria call_null = False - leafs_to_stack.append(leaf.create_substitution_leaf(FALSE_LEAF, working_table)) + leafs_to_stack.append(self.create_substitution_leaf(leaf, FALSE_LEAF, working_table)) else: ids2 = select_from_where(cr, field._fields_id, relational_table._table, 'id', ids2, operator) if ids2: call_null = False o2m_op = 'not in' if operator in NEGATIVE_TERM_OPERATORS else 'in' - leafs_to_stack.append(leaf.create_substitution_leaf(('id', o2m_op, ids2), working_table)) + leafs_to_stack.append(self.create_substitution_leaf(leaf, ('id', o2m_op, ids2), working_table)) if call_null: o2m_op = 'in' if operator in NEGATIVE_TERM_OPERATORS else 'not in' - leafs_to_stack.append(leaf.create_substitution_leaf(('id', o2m_op, select_distinct_from_where_not_null(cr, field._fields_id, relational_table._table)), working_table)) + leafs_to_stack.append(self.create_substitution_leaf(leaf, ('id', o2m_op, select_distinct_from_where_not_null(cr, field._fields_id, relational_table._table)), working_table)) elif field._type == 'many2many': rel_table, rel_id1, rel_id2 = field._sql_names(working_table) @@ -872,7 +897,7 @@ class expression(object): ids2 = self.to_ids(cr, uid, right, relational_table, context) dom = self.child_of_domain(cr, uid, 'id', ids2, relational_table) ids2 = relational_table.search(cr, uid, dom, context=context) - leafs_to_stack.append(leaf.create_substitution_leaf(('id', 'in', _rec_convert(ids2)), working_table)) + leafs_to_stack.append(self.create_substitution_leaf(leaf, ('id', 'in', _rec_convert(ids2)), working_table)) else: call_null_m2m = True if right is not False: @@ -889,17 +914,17 @@ class expression(object): if operator in ['like', 'ilike', 'in', '=']: #no result found with given search criteria call_null_m2m = False - leafs_to_stack.append(leaf.create_substitution_leaf(FALSE_LEAF, working_table)) + leafs_to_stack.append(self.create_substitution_leaf(leaf, FALSE_LEAF, working_table)) else: operator = 'in' # operator changed because ids are directly related to main object else: call_null_m2m = False m2m_op = 'not in' if operator in NEGATIVE_TERM_OPERATORS else 'in' - leafs_to_stack.append(leaf.create_substitution_leaf(('id', m2m_op, select_from_where(cr, rel_id1, rel_table, rel_id2, res_ids, operator) or [0]), working_table)) + leafs_to_stack.append(self.create_substitution_leaf(leaf, ('id', m2m_op, select_from_where(cr, rel_id1, rel_table, rel_id2, res_ids, operator) or [0]), working_table)) if call_null_m2m: m2m_op = 'in' if operator in NEGATIVE_TERM_OPERATORS else 'not in' - leafs_to_stack.append(leaf.create_substitution_leaf(('id', m2m_op, select_distinct_from_where_not_null(cr, rel_id1, rel_table)), working_table)) + leafs_to_stack.append(self.create_substitution_leaf(leaf, ('id', m2m_op, select_distinct_from_where_not_null(cr, rel_id1, rel_table)), working_table)) elif field._type == 'many2one': if operator == 'child_of': @@ -908,7 +933,7 @@ class expression(object): dom = self.child_of_domain(cr, uid, left, ids2, relational_table, prefix=field._obj) else: dom = self.child_of_domain(cr, uid, 'id', ids2, working_table, parent=left) - leafs_to_stack += [leaf.create_substitution_leaf(dom_leaf, working_table) for dom_leaf in dom] + leafs_to_stack += [self.create_substitution_leaf(leaf, dom_leaf, working_table) for dom_leaf in dom] else: def _get_expression(relational_table, cr, uid, left, right, operator, context=None): if context is None: @@ -932,7 +957,7 @@ class expression(object): # resolve string-based m2o criterion into IDs if isinstance(right, basestring) or \ right and isinstance(right, (tuple, list)) and all(isinstance(item, basestring) for item in right): - leafs_to_stack.append(leaf.create_substitution_leaf(_get_expression(relational_table, cr, uid, left, right, operator, context=context), working_table)) + leafs_to_stack.append(self.create_substitution_leaf(leaf, _get_expression(relational_table, cr, uid, left, right, operator, context=context), working_table)) else: # right == [] or right == False and all other cases are handled by __leaf_to_sql() results_to_stack.append(leaf) @@ -947,7 +972,7 @@ class expression(object): elif operator in ('<', '<='): right += ' 23:59:59' - leafs_to_stack.append(leaf.create_substitution_leaf((left, operator, right), working_table)) + leafs_to_stack.append(self.create_substitution_leaf(leaf, (left, operator, right), working_table)) elif field.translate: need_wildcard = operator in ('like', 'ilike', 'not like', 'not ilike') @@ -982,7 +1007,7 @@ class expression(object): right, right, ] - leafs_to_stack.append(leaf.create_substitution_leaf(('id', 'inselect', (subselect, params)), working_table)) + leafs_to_stack.append(self.create_substitution_leaf(leaf, ('id', 'inselect', (subselect, params)), working_table)) else: results_to_stack.append(leaf) From 3bb1475f6ed5a551a0bc1820aecec0ed39a1a67e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 5 Dec 2012 10:50:20 +0100 Subject: [PATCH 36/80] [FIX] mail_thread: fixed search domain on message_unread that was buggy. bzr revid: tde@openerp.com-20121205095020-932qhljgd5ll9nzb --- addons/mail/mail_message.py | 2 +- addons/mail/mail_thread.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 95c34ad5c51..1f588fc7663 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -63,7 +63,7 @@ class mail_message(osv.Model): # protection for `default_type` values leaking from menu action context (e.g. for invoices) if context and context.get('default_type') and context.get('default_type') not in self._columns['type'].selection: context = dict(context, default_type=None) - return super(mail_message, self).default_get(cr, uid, fields, context=context) + return super(mail_message, self).default_get(cr, uid, fields, context=context) def _shorten_name(self, name): if len(name) <= (self._message_record_name_length + 3): diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index d308105d81e..2d73bb16ea6 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -122,7 +122,7 @@ class mail_thread(osv.AbstractModel): return res def _search_unread(self, cr, uid, obj=None, name=None, domain=None, context=None): - return ['&', ('message_ids.model', '=', self._name), ('message_ids.notification_ids.to_read', '=', False)] + return [('message_ids.to_read', '=', False)] def _get_followers(self, cr, uid, ids, name, arg, context=None): fol_obj = self.pool.get('mail.followers') From 003752f7f1545b75e254e1585943bdfe1bb74de5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 5 Dec 2012 11:20:52 +0100 Subject: [PATCH 37/80] [REF] ir_ui_menu: needaction_enabled is now stored, because there are no reasons not to be (this is something mainly statically defined). needaction_counter is removed, but a method on the menu allows to have the information back, calling get_needaction_data. A web-side branch will call asynchronously this method with some menu ids, allowing to separate the menu loading from the needaction loading. bzr revid: tde@openerp.com-20121205102052-pn1uwhrl9mn46c4x --- openerp/addons/base/ir/ir_ui_menu.py | 43 ++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/openerp/addons/base/ir/ir_ui_menu.py b/openerp/addons/base/ir/ir_ui_menu.py index a8475d33552..9cafdbdd74a 100644 --- a/openerp/addons/base/ir/ir_ui_menu.py +++ b/openerp/addons/base/ir/ir_ui_menu.py @@ -265,17 +265,33 @@ class ir_ui_menu(osv.osv): return res - def _get_needaction(self, cr, uid, ids, field_names, args, context=None): + def _get_needaction_enabled(self, cr, uid, ids, field_names, args, context=None): + """ needaction_enabled: tell whether the menu has a related action + that uses the needaction mechanism. """ + res = dict.fromkeys(ids, False) + for menu in self.browse(cr, uid, ids, context=context): + if menu.action and menu.action.type in ('ir.actions.act_window', 'ir.actions.client') and menu.action.res_model: + obj = self.pool.get(menu.action.res_model) + if obj and obj._needaction: + res[menu.id] = True + return res + + def get_needaction_data(self, cr, uid, ids, context=None): + """ Return for each menu entry of ids : + - if it uses the needaction mechanism (needaction_enabled) + - the needaction counter of the related action, taking into account + the action domain + """ res = {} for menu in self.browse(cr, uid, ids, context=context): res[menu.id] = { 'needaction_enabled': False, 'needaction_counter': False, } - if menu.action and menu.action.type in ('ir.actions.act_window','ir.actions.client') and menu.action.res_model: + if menu.action and menu.action.type in ('ir.actions.act_window', 'ir.actions.client') and menu.action.res_model: obj = self.pool.get(menu.action.res_model) if obj and obj._needaction: - if menu.action.type=='ir.actions.act_window': + if menu.action.type == 'ir.actions.act_window': dom = menu.action.domain and eval(menu.action.domain, {'uid': uid}) or [] else: dom = eval(menu.action.params_store or '{}', {'uid': uid}).get('domain') @@ -286,7 +302,7 @@ class ir_ui_menu(osv.osv): _columns = { 'name': fields.char('Menu', size=64, required=True, translate=True), 'sequence': fields.integer('Sequence'), - 'child_id' : fields.one2many('ir.ui.menu', 'parent_id','Child IDs'), + 'child_id': fields.one2many('ir.ui.menu', 'parent_id', 'Child IDs'), 'parent_id': fields.many2one('ir.ui.menu', 'Parent Menu', select=True), 'groups_id': fields.many2many('res.groups', 'ir_ui_menu_group_rel', 'menu_id', 'gid', 'Groups', help="If you have groups, the visibility of this menu will be based on these groups. "\ @@ -296,11 +312,14 @@ class ir_ui_menu(osv.osv): 'icon': fields.selection(tools.icons, 'Icon', size=64), 'icon_pict': fields.function(_get_icon_pict, type='char', size=32), 'web_icon': fields.char('Web Icon File', size=128), - 'web_icon_hover':fields.char('Web Icon File (hover)', size=128), + 'web_icon_hover': fields.char('Web Icon File (hover)', size=128), 'web_icon_data': fields.function(_get_image_icon, string='Web Icon Image', type='binary', readonly=True, store=True, multi='icon'), - 'web_icon_hover_data':fields.function(_get_image_icon, string='Web Icon Image (hover)', type='binary', readonly=True, store=True, multi='icon'), - 'needaction_enabled': fields.function(_get_needaction, string='Target model uses the need action mechanism', type='boolean', help='If the menu entry action is an act_window action, and if this action is related to a model that uses the need_action mechanism, this field is set to true. Otherwise, it is false.', multi='_get_needaction'), - 'needaction_counter': fields.function(_get_needaction, string='Number of actions the user has to perform', type='integer', help='If the target model uses the need action mechanism, this field gives the number of actions the current user has to perform.', multi='_get_needaction'), + 'web_icon_hover_data': fields.function(_get_image_icon, string='Web Icon Image (hover)', type='binary', readonly=True, store=True, multi='icon'), + 'needaction_enabled': fields.function(_get_needaction_enabled, + type='boolean', + store=True, + string='Target model uses the need action mechanism', + help='If the menu entry action is an act_window action, and if this action is related to a model that uses the need_action mechanism, this field is set to true. Otherwise, it is false.'), 'action': fields.function(_action, fnct_inv=_action_inv, type='reference', string='Action', selection=[ @@ -317,12 +336,12 @@ class ir_ui_menu(osv.osv): return _('Error ! You can not create recursive Menu.') _constraints = [ - (osv.osv._check_recursion, _rec_message , ['parent_id']) + (osv.osv._check_recursion, _rec_message, ['parent_id']) ] _defaults = { - 'icon' : 'STOCK_OPEN', - 'icon_pict': ('stock', ('STOCK_OPEN','ICON_SIZE_MENU')), - 'sequence' : 10, + 'icon': 'STOCK_OPEN', + 'icon_pict': ('stock', ('STOCK_OPEN', 'ICON_SIZE_MENU')), + 'sequence': 10, } _order = "sequence,id" From 85d1f4fcb10c92112ea76e28b8ee4b449b044a6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 5 Dec 2012 15:05:39 +0100 Subject: [PATCH 38/80] [IMP] mail_thread: message_unread: fixed domain of fnct_search, unread message have to_read flag at True, not false. bzr revid: tde@openerp.com-20121205140539-5g6ebfx4j425xglr --- addons/mail/mail_thread.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 00173fd2d6b..4145fabd0e5 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -122,7 +122,7 @@ class mail_thread(osv.AbstractModel): return res def _search_message_unread(self, cr, uid, obj=None, name=None, domain=None, context=None): - return [('message_ids.to_read', '=', False)] + return [('message_ids.to_read', '=', True)] def _get_followers(self, cr, uid, ids, name, arg, context=None): fol_obj = self.pool.get('mail.followers') From a7650e0e451b5276bca7116f0163b19e488c744d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 5 Dec 2012 15:06:07 +0100 Subject: [PATCH 39/80] [IMP] mail_notification: updated the index on mail_notification, including newly added field starred. bzr revid: tde@openerp.com-20121205140607-f1hnhgu49tc2jwr5 --- addons/mail/mail_followers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/mail/mail_followers.py b/addons/mail/mail_followers.py index f02835e5b3b..9793e036351 100644 --- a/addons/mail/mail_followers.py +++ b/addons/mail/mail_followers.py @@ -74,9 +74,9 @@ class mail_notification(osv.Model): } def init(self, cr): - cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('mail_notification_partner_id_read_message_id',)) + cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('mail_notification_partner_id_read_starred_message_id',)) if not cr.fetchone(): - cr.execute('CREATE INDEX mail_notification_partner_id_read_message_id ON mail_notification (partner_id, read, message_id)') + cr.execute('CREATE INDEX mail_notification_partner_id_read_starred_message_id ON mail_notification (partner_id, read, starred, message_id)') def create(self, cr, uid, vals, context=None): """ Override of create to check that we can not create a notification From ae6f7b3a081b778f4e6924f299dc06e902a927d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 5 Dec 2012 15:07:26 +0100 Subject: [PATCH 40/80] [IMP] ir_needaction: search is now without count but with a limit (to 100), and a simplified order by. This allows much faster queries as postgresql does not have to worry about order and things like that. bzr revid: tde@openerp.com-20121205140726-z1o4ueclat7y96pl --- openerp/addons/base/ir/ir_needaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/addons/base/ir/ir_needaction.py b/openerp/addons/base/ir/ir_needaction.py index 57b324de3b9..898e0c3f473 100644 --- a/openerp/addons/base/ir/ir_needaction.py +++ b/openerp/addons/base/ir/ir_needaction.py @@ -61,4 +61,4 @@ class ir_needaction_mixin(osv.AbstractModel): dom = self._needaction_domain_get(cr, uid, context=context) if not dom: return 0 - return self.search(cr, uid, (domain or []) + dom, limit=100, context=context, count=True) + return self.search(cr, uid, (domain or []) + dom, limit=100, order='id DESC', context=context) From 37051bbe917dacd2a5e4f531cb69ecd117459285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 5 Dec 2012 15:32:38 +0100 Subject: [PATCH 41/80] [FIX] needaction: returns a count-like result. bzr revid: tde@openerp.com-20121205143238-xbna8akgqduap2t5 --- openerp/addons/base/ir/ir_needaction.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openerp/addons/base/ir/ir_needaction.py b/openerp/addons/base/ir/ir_needaction.py index 898e0c3f473..39432ca6792 100644 --- a/openerp/addons/base/ir/ir_needaction.py +++ b/openerp/addons/base/ir/ir_needaction.py @@ -61,4 +61,5 @@ class ir_needaction_mixin(osv.AbstractModel): dom = self._needaction_domain_get(cr, uid, context=context) if not dom: return 0 - return self.search(cr, uid, (domain or []) + dom, limit=100, order='id DESC', context=context) + res = self.search(cr, uid, (domain or []) + dom, limit=100, order='id DESC', context=context) + return len(res) From ea01dfe9ddfe02dae9a35812b868ad5e84614d58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 5 Dec 2012 16:34:10 +0100 Subject: [PATCH 42/80] [CLEAN] fields.py: added auto_join in one2many and many2one __init__, binded on _auto_join. Added some short explanations in fields.py about the attribute. bzr revid: tde@openerp.com-20121205153410-07vo3j1c641xgmda --- openerp/osv/fields.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/openerp/osv/fields.py b/openerp/osv/fields.py index b15cebd29f5..b9096b31d57 100644 --- a/openerp/osv/fields.py +++ b/openerp/osv/fields.py @@ -27,6 +27,9 @@ Fields Attributes: * _classic_read: is a classic sql fields * _type : field type + * _auto_join: for one2many and many2one fields, tells whether select + queries will join the relational table instead of replacing the + field condition by an equivalent-one based on a search. * readonly * required * size @@ -428,9 +431,10 @@ class many2one(_column): _symbol_f = lambda x: x or None _symbol_set = (_symbol_c, _symbol_f) - def __init__(self, obj, string='unknown', **args): + def __init__(self, obj, string='unknown', auto_join=False, **args): _column.__init__(self, string=string, **args) self._obj = obj + self._auto_join = auto_join def get(self, cr, obj, ids, name, user=None, context=None, values=None): if context is None: @@ -497,11 +501,12 @@ class one2many(_column): _prefetch = False _type = 'one2many' - def __init__(self, obj, fields_id, string='unknown', limit=None, **args): + def __init__(self, obj, fields_id, string='unknown', limit=None, auto_join=False, **args): _column.__init__(self, string=string, **args) self._obj = obj self._fields_id = fields_id self._limit = limit + self._auto_join = auto_join #one2many can't be used as condition for defaults assert(self.change_default != True) From 549262626369b000a22e2475ec32f41c67479676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 5 Dec 2012 16:34:49 +0100 Subject: [PATCH 43/80] [DOC] Updated the doc about auto_join: a bit more precise, and updated accordingly to last changes in implementation. bzr revid: tde@openerp.com-20121205153449-68uel0ne0od3jl9i --- doc/06_misc_auto_join.rst | 65 +++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/doc/06_misc_auto_join.rst b/doc/06_misc_auto_join.rst index dab24f1bf09..1dd2d0edb88 100644 --- a/doc/06_misc_auto_join.rst +++ b/doc/06_misc_auto_join.rst @@ -5,11 +5,11 @@ Perfoming joins in select .. versionadded:: 7.0 -Starting with OpenERP 7.0, an ``_auto_join`` attribute is added on *many2one* and +Starting with OpenERP 7.0, an ``auto_join`` attribute is added on *many2one* and *one2many* fields. The purpose is to allow the automatic generation of joins in select queries. This attribute is set to False by default, therefore not changing -the default behavior of those fields. It is not recommended to use this attribute -unless you understand the limitations of the feature. +the default behavior. Please note that we consider this feature as still experimental +and should be used only if you understand its limitations and targets. Without ``_auto_join``, the behavior of expression.parse() is the same as before. Leafs holding a path beginning with many2one or one2many fields perform a search @@ -19,42 +19,53 @@ For example, if you have on res.partner a domain like ``[('bank_ids.name', performed : - 1 on res_partner_bank, with domain ``[('name', '=', 'foo')]``, that returns a - list of (res.partner.bank) bids + list of res.partner.bank ids (bids) - 1 on res_partner, with a domain ``['bank_ids', 'in', bids)]``, that returns a - list of (res.partner) pids + list of res.partner ids (pids) - 1 on res_partner, with a domain ``[('id', 'in', pids)]`` -When the _auto_join attribute is True, it will perform a select on res_partner -as well as on res_partner_bank. +When the ``auto_join`` attribute is True on a relational field, the destination +table will be joined to produce only one query. -- the relational table will be accessed through an alias: ``'"res_partner_bank" - as res_partner__bank_ids`` -- the relational table will have a join condition on the main table: +- the relational table is accessed using an alias: ``'"res_partner_bank" + as res_partner__bank_ids``. The alias is generated using the relational field + name. This allows to have multiple joins with different join conditions on the + same table, depending on the domain. +- there is a join condition between the destination table and the main table: ``res_partner__bank_ids."partner_id"=res_partner."id"`` -- the condition will be written on the relational table: +- the condition is then written on the relational table: ``res_partner__bank_ids."name" = 'foo'`` -This job is performed in expression.parse(). For leafs containing a path, it -checks whether the first item of the path is a *many2one* or *one2many* field -with the ``auto_join`` attribute set. If set, it adds a join query and recursively -analyzes the remaining of the leaf, going back to the normal behavior when -not reaching an ``_auto_join`` field. The sql condition created from the leaf -will be updated to take into account the table aliases. +This manipulation is performed in expression.parse(). It checks leafs that +contain a path, i.e. any domain containing a '.'. It then checks whether the +first item of the path is a *many2one* or *one2many* field with the ``auto_join`` +attribute set. If set, it adds a join query and recursively analyzes the +remaining of the leaf, using the same behavior. If the remaining path also holds +a path with auto_join fields, it will add all tables and add every necessary +join conditions. -Chaining _auto_join allows to reduce the number of queries performed, and to -avoid having too long ``('id', 'in', ids)`` replacement leafs in domains. -However, severe limitations exist on this feature that limits its current use as -of version 7.0. **This feature is therefore considered as experimental, and used +Chaining joins allows to reduce the number of queries performed, and to avoid +having too long equivalent leaf replacement in domains. Indeed, the internal +queries produced by this behavior can be very costly, because they were generally +select queries without limit that could lead to huge ('id', 'in', [...]) +leafs to analyze and execute. + +Some limitations exist on this feature that limits its current use as of version +7.0. **This feature is therefore considered as experimental, and used to speedup some precise bottlenecks in OpenERP**. List of known issues and limitations: -- using _auto_join bypasses the business logic; no name search is performed, only - direct matches between ids using join conditions -- ir.rules are not taken into account when performing the _auto_join. -- support of active_test is not asserted -- support of translation is not asserted -- support of _auto_join leading to function fields +- using ``auto_join`` bypasses the business logic; no name search is performed, + only direct matches between ids using join conditions +- ir.rules are not taken into account when analyzing and adding the join + conditions + +List of already-supported corner cases : + +- one2many fields having a domain attribute. Static domains as well as dynamic + domain are supported +- auto_join leading to functional searchable fields Typical use in OpenERP 7.0: From 02c200844c66331b583e4f4821e9dd638eb7e282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 5 Dec 2012 16:35:35 +0100 Subject: [PATCH 44/80] [CLEAN] Removed some forgotten print statements. bzr revid: tde@openerp.com-20121205153535-lfs6yrokyk0wfm0w --- openerp/osv/orm.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index f37d1a2202d..a08126d12fc 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -2732,7 +2732,6 @@ class BaseModel(object): parent_table_name = parent_model._table quoted_parent_table_name = '"%s"' % parent_table_name if quoted_parent_table_name not in query.tables: - print '--> _inheratis_join_add adding %s' % (quoted_parent_table_name) query.tables.append(quoted_parent_table_name) query.where_clause.append('(%s.%s = %s.id)' % (current_table._table, inherits_field, parent_table_name)) @@ -4670,7 +4669,6 @@ class BaseModel(object): for table in added_tables: quoted_table_name = '%s' % (table) if quoted_table_name not in query.tables: - print '--> apply_rule adding %s' % quoted_table_name query.tables.append(quoted_table_name) return True return False From d8235eec3ce368c16a5893002c0530393ecccb11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 5 Dec 2012 16:36:09 +0100 Subject: [PATCH 45/80] [CLEAN] _auto_join -> auto_join. bzr revid: tde@openerp.com-20121205153609-tncmotw8jnoa53e7 --- addons/mail/mail_message.py | 2 +- addons/mail/mail_thread.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index fbcffff1e4b..a5080e48373 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -157,7 +157,7 @@ class mail_message(osv.Model): store=True, string='Message Record Name', help="Name get of the related document."), 'notification_ids': fields.one2many('mail.notification', 'message_id', - string='Notifications', _auto_join=True, + string='Notifications', auto_join=True, help='Technical field holding the message notifications. Use notified_partner_ids to access notified partners.'), 'subject': fields.char('Subject'), 'date': fields.datetime('Date'), diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 4145fabd0e5..e706ad66a33 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -193,7 +193,7 @@ class mail_thread(osv.AbstractModel): obj='res.partner', string='Followers', multi='_get_followers'), 'message_ids': fields.one2many('mail.message', 'res_id', domain=lambda self: [('model', '=', self._name)], - _auto_join=True, + auto_join=True, string='Messages', help="Messages and communication history"), 'message_unread': fields.function(_get_message_data, From df4665fcd2d52a12bf5a36955334e852d9195612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 6 Dec 2012 10:16:48 +0100 Subject: [PATCH 46/80] [IMP] [CLEAN] [REVIEW] field._properties -> isinstance(field, fields.function). bzr revid: tde@openerp.com-20121206091648-vmy2ghr293reoxn6 --- openerp/osv/expression.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/openerp/osv/expression.py b/openerp/osv/expression.py index 9078cf5c42c..c4c7f4400ea 100644 --- a/openerp/osv/expression.py +++ b/openerp/osv/expression.py @@ -137,6 +137,7 @@ import logging import traceback import openerp.modules +from openerp.osv import fields from openerp.osv.orm import MAGIC_COLUMNS import openerp.tools as tools @@ -814,12 +815,13 @@ class expression(object): leaf.leaf = ('id', 'in', table_ids) leafs_to_stack.append(leaf) - # ---------------------------------------- + # ------------------------------------------------- # FUNCTION FIELD - # -> not stored, get the result of fnct_search - # ---------------------------------------- + # -> not stored: error if no _fnct_search, otherwise handle the result domain + # -> stored: management done in the remaining of parsing + # ------------------------------------------------- - elif field._properties and not field.store and not field._fnct_search: + elif isinstance(field, fields.function) and not field.store and not field._fnct_search: # this is a function field that is not stored # the function field doesn't provide a search function and doesn't store # values in the database, so we must ignore it : we generate a dummy leaf @@ -833,7 +835,7 @@ class expression(object): _logger.debug(''.join(traceback.format_stack())) leafs_to_stack.append(leaf) - elif field._properties and not field.store: + elif isinstance(field, fields.function) and not field.store: # this is a function field that is not stored fct_domain = field.search(cr, uid, working_table, left, [leaf.leaf], context=context) if not fct_domain: From a0b8a58174a0c5f5d0104f99a8b07abac7ca2109 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 6 Dec 2012 10:27:45 +0100 Subject: [PATCH 47/80] [IMP] [REVIEW] _auto_join on many2many now raises a NotImplementedError; added a test case for that. bzr revid: tde@openerp.com-20121206092745-a8t62khs4x37db1g --- openerp/addons/base/tests/test_expression.py | 11 ++++++++++- openerp/osv/expression.py | 3 +-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/openerp/addons/base/tests/test_expression.py b/openerp/addons/base/tests/test_expression.py index 2bfd8ade1c9..a8b69ac8c06 100644 --- a/openerp/addons/base/tests/test_expression.py +++ b/openerp/addons/base/tests/test_expression.py @@ -126,9 +126,10 @@ class test_expression(common.TransactionCase): # Get test columns partner_state_id_col = partner_obj._columns.get('state_id') # many2one on res.partner to res.country.state partner_parent_id_col = partner_obj._columns.get('parent_id') # many2one on res.partner to res.partner + state_country_id_col = state_obj._columns.get('country_id') # many2one on res.country.state on res.country partner_child_ids_col = partner_obj._columns.get('child_ids') # one2many on res.partner to res.partner partner_bank_ids_col = partner_obj._columns.get('bank_ids') # one2many on res.partner to res.partner.bank - state_country_id_col = state_obj._columns.get('country_id') # many2one on res.country.state on res.country + category_id_col = partner_obj._columns.get('category_id') # many2many on res.partner to res.partner.category # Get the first bank account type to be able to create a res.partner.bank bank_type = bank_obj._bank_type_get(cr, uid)[0] @@ -146,6 +147,14 @@ class test_expression(common.TransactionCase): b_ab = bank_obj.create(cr, uid, {'name': '__bank_test_b', 'state': bank_type[0], 'partner_id': p_ab, 'acc_number': '5678'}) b_ba = bank_obj.create(cr, uid, {'name': '__bank_test_b', 'state': bank_type[0], 'partner_id': p_ba, 'acc_number': '9876'}) + # ---------------------------------------- + # Test1: basics about the attribute + # ---------------------------------------- + + category_id_col._auto_join = True + self.assertRaises(NotImplementedError, partner_obj.search, cr, uid, [('category_id.name', '=', 'foo')]) + category_id_col._auto_join = False + # ---------------------------------------- # Test1: one2many # ---------------------------------------- diff --git a/openerp/osv/expression.py b/openerp/osv/expression.py index c4c7f4400ea..383e70dccfa 100644 --- a/openerp/osv/expression.py +++ b/openerp/osv/expression.py @@ -800,8 +800,7 @@ class expression(object): leafs_to_stack.append(self.create_substitution_leaf(leaf, (field_path[1], operator, right), relational_table)) elif len(field_path) > 1 and field._auto_join: - assert False, \ - '_auto_join attribute on something else than a many2one or one2many is currently not supported' + raise NotImplementedError('_auto_join attribute not supported on many2many field %s' % (left)) elif len(field_path) > 1 and field._type == 'many2one': right_ids = relational_table.search(cr, uid, [(field_path[1], operator, right)], context=context) From bb57752f0a930647ee2205aef3759bac466e132d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 6 Dec 2012 17:21:28 +0100 Subject: [PATCH 48/80] [IMP] [CLEAN] [REVIEW] expression: udpated domain management. Added pop and push methods in parse to hide the internal plumbery. Cleaned some comments, deleted dead/unnecessary code, updated some comments. to_ids and childof_domain back in parse. Added generate_table_alias, method that should be used everywhere if possible when generating table name or aliases for sql conditions and things like that. bzr revid: tde@openerp.com-20121206162128-1qol201os1xztlz5 --- openerp/osv/expression.py | 352 ++++++++++++++++++-------------------- 1 file changed, 171 insertions(+), 181 deletions(-) diff --git a/openerp/osv/expression.py b/openerp/osv/expression.py index 383e70dccfa..8967d9f254a 100644 --- a/openerp/osv/expression.py +++ b/openerp/osv/expression.py @@ -313,6 +313,37 @@ def distribute_not(domain): # Generic leaf manipulation # -------------------------------------------------- +def _quote(to_quote): + if '"' not in to_quote: + return '"%s"' % to_quote + return to_quote + + +def generate_table_alias(src_model, joined_tables=[]): + """ Generate a standard table alias name. An alias is generated as following: + - the base is the source model table name + - then, each joined table is added in the alias using a 'link field name' + that is used to render unique aliases for a given path + Examples: + - src_model=res.users, join_tables=[]: + alias = '"res_users"' + - src_model=res.users, join_tables=[(res.partner, 'parent_id')] + alias = '"res_partner" as "res_users__parent_id"' + + :param model src_model: model source of the alias + :param list join_tables: list of tuples + (dst_model, link_field) + + :return tuple: (table alias, alias statement for from clause with quotes added) + """ + alias = src_model._table + if not joined_tables: + return ('%s' % alias, '%s' % _quote(alias)) + for link in joined_tables: + alias += '__' + link[1] + return ('%s' % alias, '%s as %s' % (_quote(joined_tables[-1][0]._table), _quote(alias))) + + def normalize_leaf(element): """ Change a term's operator to some canonical form, simplifying later processing. """ @@ -416,33 +447,12 @@ class ExtendedLeaf(object): for item in self.context_stack: self._tables.append(item[0]) self._tables.append(table) - if self.is_operator(): - self.elements = self.leaf, None, None - elif self.is_true_leaf() or self.is_false_leaf(): - # because we consider left as a string - self.elements = ('%s' % leaf[0], leaf[1], leaf[2]) - else: - self.elements = leaf - self.field_path = self.elements[0].split('.', 1) - self.field = self.table._columns.get(self.field_path[0]) - if self.field and self.field._obj: - self.relational_table = self.table.pool.get(self.field._obj) - else: - self.relational_table = None # check validity self.check_leaf() def __str__(self): return '' % (str(self.leaf), self.table._table, ','.join(self._get_context_debug())) - # def create_substitution_leaf(self, new_leaf, new_table=None): - # if new_table is None: - # new_table = self.table - # return ExtendedLeaf(new_leaf, new_table, self.context_stack) - - # def create_sibling_leaf(self, new_leaf): - # pass - # -------------------------------------------------- # Join / Context manipulation # running examples: @@ -488,42 +498,48 @@ class ExtendedLeaf(object): # i.e.: many2one: 'state_id': current field name # -------------------------------------------------- - def _generate_alias(self): - alias = self._tables[0]._table - for context in self.context_stack: - alias += '__' + context[4] + def generate_alias(self): + links = [(context[1], context[4]) for context in self.context_stack] + alias, alias_statement = generate_table_alias(self._tables[0], links) return alias - def add_join_context(self, context_field_name, src_table_link_name, dst_table, dst_table_link_name): - """ See above comments for more details. A join context is a tuple structure - holding the following elements: (src_table (self.table), src_table_link_name, - dst_table, dst_table_link_name, context_field_name) + def add_join_context(self, table, lhs_col, table_col, link): + """ See above comments for more details. A join context is a tuple like: + ``(lhs, table, lhs_col, col, link)`` + where + - lhs is the source table (self.table) + - table is the destination table + - lsh_col is the source table column name used for the condition + - table_col is the destination table column name used for the condition + - link is the field name source of the join used as context to + generate the destination table alias + After adding the join, the table of the current leaf is updated. """ - self.context_stack.append((self.table, src_table_link_name, dst_table, dst_table_link_name, context_field_name)) - self._tables.append(dst_table) - self.table = dst_table + self.context_stack.append((self.table, table, lhs_col, table_col, link)) + self._tables.append(table) + self.table = table def get_join_conditions(self): - names = [] + conditions = [] alias = self._tables[0]._table for context in self.context_stack: previous_alias = alias alias += '__' + context[4] - names.append('%s."%s"=%s."%s"' % (previous_alias, context[1], alias, context[3])) - return names + conditions.append('"%s"."%s"="%s"."%s"' % (previous_alias, context[2], alias, context[3])) + return conditions def get_tables(self): tables = set() alias = self._tables[0]._table for context in self.context_stack: alias += '__' + context[4] - table_full_alias = '"%s" as %s' % (context[2]._table, alias) + table_full_alias = '"%s" as "%s"' % (context[1]._table, alias) tables.add(table_full_alias) return tables def _get_context_debug(self): - names = ['%s."%s"=%s."%s" (%s)' % (table[0]._table, table[1], table[2]._table, table[3], table[4]) for table in self.context_stack] + names = ['"%s"."%s"="%s"."%s" (%s)' % (item[0]._table, item[2], item[1]._table, item[3], item[4]) for item in self.context_stack] return names # -------------------------------------------------- @@ -542,12 +558,6 @@ class ExtendedLeaf(object): if not is_operator(self.leaf) and not is_leaf(self.leaf, True): raise ValueError("Invalid leaf %s" % str(self.leaf)) - # if self.is_leaf() and not (self.is_true_leaf() or self.is_false_leaf()) \ - # and not self.field and not self.field_path[0] in self.table._inherit_fields \ - # and not (self.leaf[0] == 'id' and self.leaf[1] == 'child_of') \ - # and not (self.field_path[0] in MAGIC_COLUMNS): - # raise ValueError("Invalid field %r in leaf %r" % (self.leaf[0], self.leaf)) - def is_operator(self): return is_operator(self.leaf) @@ -598,54 +608,6 @@ class expression(object): # parse the domain expression self.parse(cr, uid, context=context) - # ---------------------------------------- - # Tools for domain manipulation - # ---------------------------------------- - - def to_ids(self, cr, uid, value, relational_table, context=None, limit=None): - """ Normalize a single id or name, or a list of those, into a list of ids - :param {int,long,basestring,list,tuple} value: - if int, long -> return [value] - if basestring, convert it into a list of basestrings, then - if list of basestring -> - perform a name_search on relational_table for each name - return the list of related ids - """ - names = [] - if isinstance(value, basestring): - names = [value] - elif value and isinstance(value, (tuple, list)) and all(isinstance(item, basestring) for item in value): - names = value - elif isinstance(value, (int, long)): - return [value] - if names: - name_get_list = [name_get[0] for name in names for name_get in relational_table.name_search(cr, uid, name, [], 'ilike', context=context, limit=limit)] - return list(set(name_get_list)) - return list(value) - - def child_of_domain(self, cr, uid, left, ids, left_model, parent=None, prefix='', context=None): - """ Return a domain implementing the child_of operator for [(left,child_of,ids)], - either as a range using the parent_left/right tree lookup fields - (when available), or as an expanded [(left,in,child_ids)] """ - if left_model._parent_store and (not left_model.pool._init): - # TODO: Improve where joins are implemented for many with '.', replace by: - # doms += ['&',(prefix+'.parent_left','<',o.parent_right),(prefix+'.parent_left','>=',o.parent_left)] - doms = [] - for o in left_model.browse(cr, uid, ids, context=context): - if doms: - doms.insert(0, OR_OPERATOR) - doms += [AND_OPERATOR, ('parent_left', '<', o.parent_right), ('parent_left', '>=', o.parent_left)] - if prefix: - return [(left, 'in', left_model.search(cr, uid, doms, context=context))] - return doms - else: - def recursive_children(ids, model, parent_field): - if not ids: - return [] - ids2 = model.search(cr, uid, [(parent_field, 'in', ids)], context=context) - return ids + recursive_children(ids2, model, parent_field) - return [(left, 'in', recursive_children(ids, left_model, parent or left_model._parent_name))] - # ---------------------------------------- # Leafs management # ---------------------------------------- @@ -662,16 +624,6 @@ class expression(object): tables.append(table_name) return tables - def create_substitution_leaf(self, leaf, new_elements, new_table=None): - if new_table is None: - new_table = leaf.table - new_context_stack = [tuple(context) for context in leaf.context_stack] - new_leaf = ExtendedLeaf(new_elements, new_table, context_stack=new_context_stack) - return new_leaf - - def create_sibling_leaf(self, leaf, new_elements): - pass - # ---------------------------------------- # Parsing # ---------------------------------------- @@ -694,22 +646,78 @@ class expression(object): :var obj relational_table: relational table of a field (field._obj) ex: res_partner.bank_ids -> res_partner_bank """ - result = [] - stack = [ExtendedLeaf(leaf, self.root_table) for leaf in self.expression] - while stack: + def to_ids(value, relational_table, context=None, limit=None): + """ Normalize a single id or name, or a list of those, into a list of ids + :param {int,long,basestring,list,tuple} value: + if int, long -> return [value] + if basestring, convert it into a list of basestrings, then + if list of basestring -> + perform a name_search on relational_table for each name + return the list of related ids + """ + names = [] + if isinstance(value, basestring): + names = [value] + elif value and isinstance(value, (tuple, list)) and all(isinstance(item, basestring) for item in value): + names = value + elif isinstance(value, (int, long)): + return [value] + if names: + name_get_list = [name_get[0] for name in names for name_get in relational_table.name_search(cr, uid, name, [], 'ilike', context=context, limit=limit)] + return list(set(name_get_list)) + return list(value) + + def child_of_domain(left, ids, left_model, parent=None, prefix='', context=None): + """ Return a domain implementing the child_of operator for [(left,child_of,ids)], + either as a range using the parent_left/right tree lookup fields + (when available), or as an expanded [(left,in,child_ids)] """ + if left_model._parent_store and (not left_model.pool._init): + # TODO: Improve where joins are implemented for many with '.', replace by: + # doms += ['&',(prefix+'.parent_left','<',o.parent_right),(prefix+'.parent_left','>=',o.parent_left)] + doms = [] + for o in left_model.browse(cr, uid, ids, context=context): + if doms: + doms.insert(0, OR_OPERATOR) + doms += [AND_OPERATOR, ('parent_left', '<', o.parent_right), ('parent_left', '>=', o.parent_left)] + if prefix: + return [(left, 'in', left_model.search(cr, uid, doms, context=context))] + return doms + else: + def recursive_children(ids, model, parent_field): + if not ids: + return [] + ids2 = model.search(cr, uid, [(parent_field, 'in', ids)], context=context) + return ids + recursive_children(ids2, model, parent_field) + return [(left, 'in', recursive_children(ids, left_model, parent or left_model._parent_name))] + + def create_substitution_leaf(leaf, new_elements, new_table=None): + if new_table is None: + new_table = leaf.table + new_context_stack = [tuple(context) for context in leaf.context_stack] + new_leaf = ExtendedLeaf(new_elements, new_table, context_stack=new_context_stack) + return new_leaf + + def pop(): + return self.stack.pop() + + def push(leaf): + self.stack.append(leaf) + + def push_result(leaf): + self.result.append(leaf) + + self.result = [] + self.stack = [ExtendedLeaf(leaf, self.root_table) for leaf in self.expression] + # process from right to left; expression is from left to right + self.stack.reverse() + + while self.stack: # Get the next leaf to process - leaf = stack.pop(0) - leafs_to_stack = [] - results_to_stack = [] + leaf = pop() # Get working variables working_table = leaf.table - # left, operator, right = leaf.elements - # field_path = leaf.field_path - # field = leaf.field - # relational_table = leaf.relational_table - if leaf.is_operator(): left, operator, right = leaf.leaf, None, None elif leaf.is_true_leaf() or leaf.is_false_leaf(): @@ -717,11 +725,8 @@ class expression(object): left, operator, right = ('%s' % leaf.leaf[0], leaf.leaf[1], leaf.leaf[2]) else: left, operator, right = leaf.leaf - # field_path = leaf.field_path - # field = leaf.field field_path = left.split('.', 1) field = working_table._columns.get(field_path[0]) - if field and field._obj: relational_table = working_table.pool.get(field._obj) else: @@ -734,10 +739,8 @@ class expression(object): # -> add directly to result # ---------------------------------------- - if leaf.is_operator(): - results_to_stack.append(leaf) - elif leaf.is_true_leaf() or leaf.is_false_leaf(): - results_to_stack.append(leaf) + if leaf.is_operator() or leaf.is_true_leaf() or leaf.is_false_leaf(): + push_result(leaf) # ---------------------------------------- # FIELD NOT FOUND @@ -755,16 +758,18 @@ class expression(object): # { 'field_name': ('parent_model', 'm2o_field_to_reach_parent', # field_column_obj, origina_parent_model), ... } next_table = working_table.pool.get(working_table._inherit_fields[field_path[0]][0]) - leaf.add_join_context(working_table._inherits[next_table._name], working_table._inherits[next_table._name], next_table, 'id') - leafs_to_stack.append(leaf) + leaf.add_join_context(next_table, working_table._inherits[next_table._name], 'id', working_table._inherits[next_table._name]) + push(leaf) elif not field and left == 'id' and operator == 'child_of': - ids2 = self.to_ids(cr, uid, right, working_table, context) - dom = self.child_of_domain(cr, uid, left, ids2, working_table) - leafs_to_stack += [self.create_substitution_leaf(leaf, dom_leaf, working_table) for dom_leaf in dom] + ids2 = to_ids(right, working_table, context) + dom = child_of_domain(left, ids2, working_table) + for dom_leaf in reversed(dom): + new_leaf = create_substitution_leaf(leaf, dom_leaf, working_table) + push(new_leaf) elif not field and field_path[0] in MAGIC_COLUMNS: - results_to_stack.append(leaf) + push_result(leaf) elif not field: raise ValueError("Invalid field %r in leaf %r" % (left, str(leaf))) @@ -784,20 +789,19 @@ class expression(object): elif len(field_path) > 1 and field._type == 'many2one' and field._auto_join: # res_partner.state_id = res_partner__state_id.id - leaf.add_join_context(field_path[0], field_path[0], relational_table, 'id') - leafs_to_stack.append(self.create_substitution_leaf(leaf, (field_path[1], operator, right), relational_table)) + leaf.add_join_context(relational_table, field_path[0], 'id', field_path[0]) + push(create_substitution_leaf(leaf, (field_path[1], operator, right), relational_table)) elif len(field_path) > 1 and field._type == 'one2many' and field._auto_join: # res_partner.id = res_partner__bank_ids.partner_id - leaf.add_join_context(field_path[0], 'id', relational_table, field._fields_id) + leaf.add_join_context(relational_table, 'id', field._fields_id, field_path[0]) domain = field._domain(working_table) if callable(field._domain) else field._domain + push(create_substitution_leaf(leaf, (field_path[1], operator, right), relational_table)) if domain: domain = normalize_domain(domain) - leafs_to_stack.append(self.create_substitution_leaf(leaf, AND_OPERATOR, relational_table)) - for elem in domain: - leafs_to_stack.append(self.create_substitution_leaf(leaf, elem, relational_table)) - print '--> appending %s' % str(leafs_to_stack[-1]) - leafs_to_stack.append(self.create_substitution_leaf(leaf, (field_path[1], operator, right), relational_table)) + for elem in reversed(domain): + push(create_substitution_leaf(leaf, elem, relational_table)) + push(create_substitution_leaf(leaf, AND_OPERATOR, relational_table)) elif len(field_path) > 1 and field._auto_join: raise NotImplementedError('_auto_join attribute not supported on many2many field %s' % (left)) @@ -805,14 +809,14 @@ class expression(object): elif len(field_path) > 1 and field._type == 'many2one': right_ids = relational_table.search(cr, uid, [(field_path[1], operator, right)], context=context) leaf.leaf = (field_path[0], 'in', right_ids) - leafs_to_stack.append(leaf) + push(leaf) # Making search easier when there is a left operand as field.o2m or field.m2m elif len(field_path) > 1 and field._type in ['many2many', 'one2many']: right_ids = relational_table.search(cr, uid, [(field_path[1], operator, right)], context=context) table_ids = working_table.search(cr, uid, [(field_path[0], 'in', right_ids)], context=dict(context, active_test=False)) leaf.leaf = ('id', 'in', table_ids) - leafs_to_stack.append(leaf) + push(leaf) # ------------------------------------------------- # FUNCTION FIELD @@ -832,30 +836,31 @@ class expression(object): # avoid compiling stack trace if not needed if _logger.isEnabledFor(logging.DEBUG): _logger.debug(''.join(traceback.format_stack())) - leafs_to_stack.append(leaf) + push(leaf) elif isinstance(field, fields.function) and not field.store: # this is a function field that is not stored fct_domain = field.search(cr, uid, working_table, left, [leaf.leaf], context=context) if not fct_domain: leaf.leaf = TRUE_LEAF - leafs_to_stack.append(leaf) + push(leaf) else: # we assume that the expression is valid # we create a dummy leaf for forcing the parsing of the resulting expression - leafs_to_stack.append(self.create_substitution_leaf(leaf, AND_OPERATOR, working_table)) - leafs_to_stack.append(self.create_substitution_leaf(leaf, TRUE_LEAF, working_table)) - for domain_element in fct_domain: - leafs_to_stack.append(self.create_substitution_leaf(leaf, domain_element, working_table)) + for domain_element in reversed(fct_domain): + push(create_substitution_leaf(leaf, domain_element, working_table)) + # self.push(create_substitution_leaf(leaf, TRUE_LEAF, working_table)) + # self.push(create_substitution_leaf(leaf, AND_OPERATOR, working_table)) # Applying recursivity on field(one2many) elif field._type == 'one2many' and operator == 'child_of': - ids2 = self.to_ids(cr, uid, right, relational_table, context) + ids2 = to_ids(right, relational_table, context) if field._obj != working_table._name: - dom = self.child_of_domain(cr, uid, left, ids2, relational_table, prefix=field._obj) + dom = child_of_domain(left, ids2, relational_table, prefix=field._obj) else: - dom = self.child_of_domain(cr, uid, 'id', ids2, working_table, parent=left) - leafs_to_stack += [self.create_substitution_leaf(leaf, dom_leaf, working_table) for dom_leaf in dom] + dom = child_of_domain('id', ids2, working_table, parent=left) + for dom_leaf in reversed(dom): + push(create_substitution_leaf(leaf, dom_leaf, working_table)) elif field._type == 'one2many': call_null = True @@ -874,17 +879,17 @@ class expression(object): if operator in ['like', 'ilike', 'in', '=']: #no result found with given search criteria call_null = False - leafs_to_stack.append(self.create_substitution_leaf(leaf, FALSE_LEAF, working_table)) + push(create_substitution_leaf(leaf, FALSE_LEAF, working_table)) else: ids2 = select_from_where(cr, field._fields_id, relational_table._table, 'id', ids2, operator) if ids2: call_null = False o2m_op = 'not in' if operator in NEGATIVE_TERM_OPERATORS else 'in' - leafs_to_stack.append(self.create_substitution_leaf(leaf, ('id', o2m_op, ids2), working_table)) + push(create_substitution_leaf(leaf, ('id', o2m_op, ids2), working_table)) if call_null: o2m_op = 'in' if operator in NEGATIVE_TERM_OPERATORS else 'not in' - leafs_to_stack.append(self.create_substitution_leaf(leaf, ('id', o2m_op, select_distinct_from_where_not_null(cr, field._fields_id, relational_table._table)), working_table)) + push(create_substitution_leaf(leaf, ('id', o2m_op, select_distinct_from_where_not_null(cr, field._fields_id, relational_table._table)), working_table)) elif field._type == 'many2many': rel_table, rel_id1, rel_id2 = field._sql_names(working_table) @@ -895,10 +900,10 @@ class expression(object): return ids return select_from_where(cr, rel_id1, rel_table, rel_id2, ids, operator) - ids2 = self.to_ids(cr, uid, right, relational_table, context) - dom = self.child_of_domain(cr, uid, 'id', ids2, relational_table) + ids2 = to_ids(right, relational_table, context) + dom = child_of_domain('id', ids2, relational_table) ids2 = relational_table.search(cr, uid, dom, context=context) - leafs_to_stack.append(self.create_substitution_leaf(leaf, ('id', 'in', _rec_convert(ids2)), working_table)) + push(create_substitution_leaf(leaf, ('id', 'in', _rec_convert(ids2)), working_table)) else: call_null_m2m = True if right is not False: @@ -915,26 +920,27 @@ class expression(object): if operator in ['like', 'ilike', 'in', '=']: #no result found with given search criteria call_null_m2m = False - leafs_to_stack.append(self.create_substitution_leaf(leaf, FALSE_LEAF, working_table)) + push(create_substitution_leaf(leaf, FALSE_LEAF, working_table)) else: operator = 'in' # operator changed because ids are directly related to main object else: call_null_m2m = False m2m_op = 'not in' if operator in NEGATIVE_TERM_OPERATORS else 'in' - leafs_to_stack.append(self.create_substitution_leaf(leaf, ('id', m2m_op, select_from_where(cr, rel_id1, rel_table, rel_id2, res_ids, operator) or [0]), working_table)) + push(create_substitution_leaf(leaf, ('id', m2m_op, select_from_where(cr, rel_id1, rel_table, rel_id2, res_ids, operator) or [0]), working_table)) if call_null_m2m: m2m_op = 'in' if operator in NEGATIVE_TERM_OPERATORS else 'not in' - leafs_to_stack.append(self.create_substitution_leaf(leaf, ('id', m2m_op, select_distinct_from_where_not_null(cr, rel_id1, rel_table)), working_table)) + push(create_substitution_leaf(leaf, ('id', m2m_op, select_distinct_from_where_not_null(cr, rel_id1, rel_table)), working_table)) elif field._type == 'many2one': if operator == 'child_of': - ids2 = self.to_ids(cr, uid, right, relational_table, context) + ids2 = to_ids(right, relational_table, context) if field._obj != working_table._name: - dom = self.child_of_domain(cr, uid, left, ids2, relational_table, prefix=field._obj) + dom = child_of_domain(left, ids2, relational_table, prefix=field._obj) else: - dom = self.child_of_domain(cr, uid, 'id', ids2, working_table, parent=left) - leafs_to_stack += [self.create_substitution_leaf(leaf, dom_leaf, working_table) for dom_leaf in dom] + dom = child_of_domain('id', ids2, working_table, parent=left) + for dom_leaf in reversed(dom): + push(create_substitution_leaf(leaf, dom_leaf, working_table)) else: def _get_expression(relational_table, cr, uid, left, right, operator, context=None): if context is None: @@ -958,10 +964,10 @@ class expression(object): # resolve string-based m2o criterion into IDs if isinstance(right, basestring) or \ right and isinstance(right, (tuple, list)) and all(isinstance(item, basestring) for item in right): - leafs_to_stack.append(self.create_substitution_leaf(leaf, _get_expression(relational_table, cr, uid, left, right, operator, context=context), working_table)) + push(create_substitution_leaf(leaf, _get_expression(relational_table, cr, uid, left, right, operator, context=context), working_table)) else: # right == [] or right == False and all other cases are handled by __leaf_to_sql() - results_to_stack.append(leaf) + push_result(leaf) else: # other field type @@ -973,7 +979,7 @@ class expression(object): elif operator in ('<', '<='): right += ' 23:59:59' - leafs_to_stack.append(self.create_substitution_leaf(leaf, (left, operator, right), working_table)) + push(create_substitution_leaf(leaf, (left, operator, right), working_table)) elif field.translate: need_wildcard = operator in ('like', 'ilike', 'not like', 'not ilike') @@ -1008,30 +1014,15 @@ class expression(object): right, right, ] - leafs_to_stack.append(self.create_substitution_leaf(leaf, ('id', 'inselect', (subselect, params)), working_table)) + push(create_substitution_leaf(leaf, ('id', 'inselect', (subselect, params)), working_table)) else: - results_to_stack.append(leaf) - - # ---------------------------------------- - # END OF PROCESS OF CURRENT LEAF - # -> results_to_stack elements are added in result - # -> leafs_to_stack elements are inserted back in the processed - # stack to be immediately processed - # ---------------------------------------- - - leafs_to_stack.reverse() - for leaf in results_to_stack: - result.append(leaf) - for leaf in leafs_to_stack: - stack.insert(0, leaf) + push_result(leaf) # ---------------------------------------- # END OF PARSING FULL DOMAIN # ---------------------------------------- - self.result = result - # Generate joins joins = set() for leaf in self.result: @@ -1047,10 +1038,9 @@ class expression(object): assert operator in (TERM_OPERATORS + ('inselect',)), \ "Invalid operator %r in domain term %r" % (operator, leaf) assert leaf in (TRUE_LEAF, FALSE_LEAF) or left in table._all_columns \ - or left in MAGIC_COLUMNS, \ - "Invalid field %r in domain term %r" % (left, leaf) + or left in MAGIC_COLUMNS, "Invalid field %r in domain term %r" % (left, leaf) - table_alias = '"%s"' % (eleaf._generate_alias()) + table_alias = '"%s"' % (eleaf.generate_alias()) if leaf == TRUE_LEAF: query = 'TRUE' From d588388ae4a33ee836e7ebbd0a764e3081d0bba5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 6 Dec 2012 17:22:19 +0100 Subject: [PATCH 49/80] [IMP] [CLEAN] expression.parse: updated and cleaned a bit the tests. Removed print statements. bzr revid: tde@openerp.com-20121206162219-n1c48dkjghc4nu63 --- openerp/addons/base/tests/test_expression.py | 98 ++++++-------------- 1 file changed, 30 insertions(+), 68 deletions(-) diff --git a/openerp/addons/base/tests/test_expression.py b/openerp/addons/base/tests/test_expression.py index a8b69ac8c06..37fa24f5b1c 100644 --- a/openerp/addons/base/tests/test_expression.py +++ b/openerp/addons/base/tests/test_expression.py @@ -147,26 +147,23 @@ class test_expression(common.TransactionCase): b_ab = bank_obj.create(cr, uid, {'name': '__bank_test_b', 'state': bank_type[0], 'partner_id': p_ab, 'acc_number': '5678'}) b_ba = bank_obj.create(cr, uid, {'name': '__bank_test_b', 'state': bank_type[0], 'partner_id': p_ba, 'acc_number': '9876'}) - # ---------------------------------------- + # -------------------------------------------------- # Test1: basics about the attribute - # ---------------------------------------- + # -------------------------------------------------- category_id_col._auto_join = True self.assertRaises(NotImplementedError, partner_obj.search, cr, uid, [('category_id.name', '=', 'foo')]) category_id_col._auto_join = False - # ---------------------------------------- - # Test1: one2many - # ---------------------------------------- + # -------------------------------------------------- + # Test2: one2many + # -------------------------------------------------- name_test = 'test_a' # Do: one2many without _auto_join self._reinit_mock() partner_ids = partner_obj.search(cr, uid, [('bank_ids.name', 'like', name_test)]) - for query in self.query_list: - print query - print '----------------------' # Test result self.assertEqual(set(partner_ids), set([p_aa]), "_auto_join off: ('bank_ids.name', 'like', '..'): incorrect result") @@ -191,9 +188,6 @@ class test_expression(common.TransactionCase): # Do: cascaded one2many without _auto_join self._reinit_mock() partner_ids = partner_obj.search(cr, uid, [('child_ids.bank_ids.id', 'in', [b_aa, b_ba])]) - for query in self.query_list: - print query - print '----------------------' # Test result self.assertEqual(set(partner_ids), set([p_a, p_b]), "_auto_join off: ('child_ids.bank_ids.id', 'in', [..]): incorrect result") @@ -205,22 +199,20 @@ class test_expression(common.TransactionCase): partner_bank_ids_col._auto_join = True self._reinit_mock() partner_ids = partner_obj.search(cr, uid, [('bank_ids.name', 'like', 'test_a')]) - # for query in self.query_list: - # print query # Test result self.assertEqual(set(partner_ids), set([p_aa]), "_auto_join on: ('bank_ids.name', 'like', '..') incorrect result") - # # Test produced queries + # Test produced queries self.assertEqual(len(self.query_list), 1, "_auto_join on: ('bank_ids.name', 'like', '..') should produce 1 query") sql_query = self.query_list[0].get_sql() self.assertIn('"res_partner"', sql_query[0], "_auto_join on: ('bank_ids.name', 'like', '..') query incorrect main table") - self.assertIn('"res_partner_bank" as res_partner__bank_ids', sql_query[0], + self.assertIn('"res_partner_bank" as "res_partner__bank_ids"', sql_query[0], "_auto_join on: ('bank_ids.name', 'like', '..') query incorrect join") self.assertIn('"res_partner__bank_ids"."name" like %s', sql_query[1], "_auto_join on: ('bank_ids.name', 'like', '..') query incorrect where condition") - self.assertIn('res_partner."id"=res_partner__bank_ids."partner_id"', sql_query[1], + self.assertIn('"res_partner"."id"="res_partner__bank_ids"."partner_id"', sql_query[1], "_auto_join on: ('bank_ids.name', 'like', '..') query incorrect join condition") self.assertEqual(set(['%' + name_test + '%']), set(sql_query[2]), "_auto_join on: ('bank_ids.name', 'like', '..') query incorrect parameter") @@ -228,24 +220,17 @@ class test_expression(common.TransactionCase): # Do: one2many with _auto_join, test final leaf is an id self._reinit_mock() partner_ids = partner_obj.search(cr, uid, [('bank_ids.id', 'in', [b_aa, b_ab])]) - for query in self.query_list: - print query - print '----------------------' # Test result self.assertEqual(set(partner_ids), set([p_aa, p_ab]), "_auto_join on: ('bank_ids.id', 'in', [..]) incorrect result") - # # Test produced queries + # Test produced queries self.assertEqual(len(self.query_list), 1, "_auto_join on: ('bank_ids.id', 'in', [..]) should produce 1 query") sql_query = self.query_list[0].get_sql() self.assertIn('"res_partner"', sql_query[0], "_auto_join on: ('bank_ids.id', 'in', [..]) query incorrect main table") - self.assertIn('"res_partner_bank" as res_partner__bank_ids', sql_query[0], - "_auto_join on: ('bank_ids.id', 'in', [..]) query incorrect join") self.assertIn('"res_partner__bank_ids"."id" in (%s,%s)', sql_query[1], "_auto_join on: ('bank_ids.id', 'in', [..]) query incorrect where condition") - self.assertIn('res_partner."id"=res_partner__bank_ids."partner_id"', sql_query[1], - "_auto_join on: ('bank_ids.id', 'in', [..]) query incorrect join condition") self.assertEqual(set([b_aa, b_ab]), set(sql_query[2]), "_auto_join on: ('bank_ids.id', 'in', [..]) query incorrect parameter") @@ -253,9 +238,6 @@ class test_expression(common.TransactionCase): partner_child_ids_col._auto_join = True self._reinit_mock() partner_ids = partner_obj.search(cr, uid, [('child_ids.bank_ids.id', 'in', [b_aa, b_ba])]) - for query in self.query_list: - print query - print '----------------------' # Test result self.assertEqual(set(partner_ids), set([p_a, p_b]), "_auto_join on: ('child_ids.bank_ids.id', 'not in', [..]): incorrect result") @@ -265,31 +247,28 @@ class test_expression(common.TransactionCase): sql_query = self.query_list[0].get_sql() self.assertIn('"res_partner"', sql_query[0], "_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) incorrect main table") - self.assertIn('"res_partner" as res_partner__child_ids', sql_query[0], + self.assertIn('"res_partner" as "res_partner__child_ids"', sql_query[0], "_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect join") - self.assertIn('"res_partner_bank" as res_partner__child_ids__bank_ids', sql_query[0], + self.assertIn('"res_partner_bank" as "res_partner__child_ids__bank_ids"', sql_query[0], "_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect join") self.assertIn('"res_partner__child_ids__bank_ids"."id" in (%s,%s)', sql_query[1], "_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect where condition") - self.assertIn('res_partner."id"=res_partner__child_ids."parent_id"', sql_query[1], + self.assertIn('"res_partner"."id"="res_partner__child_ids"."parent_id"', sql_query[1], "_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect join condition") - self.assertIn('res_partner__child_ids."id"=res_partner__child_ids__bank_ids."partner_id"', sql_query[1], + self.assertIn('"res_partner__child_ids"."id"="res_partner__child_ids__bank_ids"."partner_id"', sql_query[1], "_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect join condition") self.assertEqual(set([b_aa, b_ba]), set(sql_query[2]), "_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect parameter") - # ---------------------------------------- - # Test2: many2one - # ---------------------------------------- + # -------------------------------------------------- + # Test3: many2one + # -------------------------------------------------- name_test = 'US' # Do: many2one without _auto_join self._reinit_mock() partner_ids = partner_obj.search(cr, uid, [('state_id.country_id.code', 'like', name_test)]) - for query in self.query_list: - print query - print '----------------------' # Test result: at least our added data + demo data self.assertTrue(set([p_a, p_b, p_aa, p_ab, p_ba]).issubset(set(partner_ids)), "_auto_join off: ('state_id.country_id.code', 'like', '..') incorrect result") @@ -301,9 +280,6 @@ class test_expression(common.TransactionCase): partner_state_id_col._auto_join = True self._reinit_mock() partner_ids = partner_obj.search(cr, uid, [('state_id.country_id.code', 'like', name_test)]) - for query in self.query_list: - print query - print '----------------------' # Test result: at least our added data + demo data self.assertTrue(set([p_a, p_b, p_aa, p_ab, p_ba]).issubset(set(partner_ids)), "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') incorrect result") @@ -320,11 +296,11 @@ class test_expression(common.TransactionCase): sql_query = self.query_list[1].get_sql() self.assertIn('"res_partner"', sql_query[0], "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect main table") - self.assertIn('"res_country_state" as res_partner__state_id', sql_query[0], + self.assertIn('"res_country_state" as "res_partner__state_id"', sql_query[0], "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect join") self.assertIn('"res_partner__state_id"."country_id" in (%s)', sql_query[1], "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect where condition") - self.assertIn('res_partner."state_id"=res_partner__state_id."id"', sql_query[1], + self.assertIn('"res_partner"."state_id"="res_partner__state_id"."id"', sql_query[1], "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect join condition") # Do: many2one with 1 _auto_join on the second many2one @@ -332,9 +308,6 @@ class test_expression(common.TransactionCase): state_country_id_col._auto_join = True self._reinit_mock() partner_ids = partner_obj.search(cr, uid, [('state_id.country_id.code', 'like', name_test)]) - for query in self.query_list: - print query - print '----------------------' # Test result: at least our added data + demo data self.assertTrue(set([p_a, p_b, p_aa, p_ab, p_ba]).issubset(set(partner_ids)), "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') incorrect result") @@ -345,11 +318,11 @@ class test_expression(common.TransactionCase): sql_query = self.query_list[0].get_sql() self.assertIn('"res_country_state"', sql_query[0], "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect main table") - self.assertIn('"res_country" as res_country_state__country_id', sql_query[0], + self.assertIn('"res_country" as "res_country_state__country_id"', sql_query[0], "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect join") self.assertIn('"res_country_state__country_id"."code" like %s', sql_query[1], "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect where condition") - self.assertIn('res_country_state."country_id"=res_country_state__country_id."id"', sql_query[1], + self.assertIn('"res_country_state"."country_id"="res_country_state__country_id"."id"', sql_query[1], "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect join condition") self.assertEqual(['%' + name_test + '%'], sql_query[2], "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect parameter") @@ -365,8 +338,6 @@ class test_expression(common.TransactionCase): state_country_id_col._auto_join = True self._reinit_mock() partner_ids = partner_obj.search(cr, uid, [('state_id.country_id.code', 'like', name_test)]) - for query in self.query_list: - print query # Test result: at least our added data + demo data self.assertTrue(set([p_a, p_b, p_aa, p_ab, p_ba]).issubset(set(partner_ids)), "_auto_join on: ('state_id.country_id.code', 'like', '..') incorrect result") @@ -376,22 +347,22 @@ class test_expression(common.TransactionCase): sql_query = self.query_list[0].get_sql() self.assertIn('"res_partner"', sql_query[0], "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect main table") - self.assertIn('"res_country_state" as res_partner__state_id', sql_query[0], + self.assertIn('"res_country_state" as "res_partner__state_id"', sql_query[0], "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join") - self.assertIn('"res_country" as res_partner__state_id__country_id', sql_query[0], + self.assertIn('"res_country" as "res_partner__state_id__country_id"', sql_query[0], "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join") self.assertIn('"res_partner__state_id__country_id"."code" like %s', sql_query[1], "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect where condition") - self.assertIn('res_partner."state_id"=res_partner__state_id."id"', sql_query[1], + self.assertIn('"res_partner"."state_id"="res_partner__state_id"."id"', sql_query[1], "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join condition") - self.assertIn('res_partner__state_id."country_id"=res_partner__state_id__country_id."id"', sql_query[1], + self.assertIn('"res_partner__state_id"."country_id"="res_partner__state_id__country_id"."id"', sql_query[1], "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join condition") self.assertEqual(['%' + name_test + '%'], sql_query[2], "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect parameter") - # ---------------------------------------- - # Test3: domain attribute on one2many fields - # ---------------------------------------- + # -------------------------------------------------- + # Test4: domain attribute on one2many fields + # -------------------------------------------------- partner_child_ids_col._auto_join = True partner_bank_ids_col._auto_join = True @@ -400,10 +371,6 @@ class test_expression(common.TransactionCase): # Do: 2 cascaded one2many with _auto_join, test final leaf is an id self._reinit_mock() partner_ids = partner_obj.search(cr, uid, ['&', (1, '=', 1), ('child_ids.bank_ids.id', 'in', [b_aa, b_ba])]) - for query in self.query_list: - print query - print partner_ids - print '--------------------' # Test result: at least one of our added data self.assertTrue(set([p_a]).issubset(set(partner_ids)), "_auto_join on one2many with domains incorrect result") @@ -414,7 +381,7 @@ class test_expression(common.TransactionCase): self.assertIn('"res_partner__child_ids__bank_ids"."acc_number" like %s', sql_query[1], "_auto_join on one2many with domains incorrect result") # TDE TODO: check first domain has a correct table name - self.assertIn('"res_partner__child_ids__bank_ids"."acc_number" like %s', sql_query[1], + self.assertIn('"res_partner__child_ids"."name" = %s', sql_query[1], "_auto_join on one2many with domains incorrect result") partner_child_ids_col._domain = lambda self: [('name', '=', '__%s' % self._name)] @@ -425,7 +392,7 @@ class test_expression(common.TransactionCase): "_auto_join on one2many with domains incorrect result") # ---------------------------------------- - # Test4: result-based tests + # Test5: result-based tests # ---------------------------------------- partner_bank_ids_col._auto_join = False @@ -439,9 +406,6 @@ class test_expression(common.TransactionCase): # Do: ('child_ids.state_id.country_id.code', 'like', '..') without _auto_join self._reinit_mock() partner_ids = partner_obj.search(cr, uid, [('child_ids.state_id.country_id.code', 'like', name_test)]) - for query in self.query_list: - print query - print '----------------------' # Test result: at least our added data + demo data self.assertTrue(set([p_a, p_b]).issubset(set(partner_ids)), "_auto_join off: ('child_ids.state_id.country_id.code', 'like', '..') incorrect result") @@ -455,9 +419,6 @@ class test_expression(common.TransactionCase): state_country_id_col._auto_join = True self._reinit_mock() partner_ids = partner_obj.search(cr, uid, [('child_ids.state_id.country_id.code', 'like', name_test)]) - for query in self.query_list: - print query - print '----------------------' # Test result: at least our added data + demo data self.assertTrue(set([p_a, p_b]).issubset(set(partner_ids)), "_auto_join on: ('child_ids.state_id.country_id.code', 'like', '..') incorrect result") @@ -469,6 +430,7 @@ class test_expression(common.TransactionCase): partner_bank_ids_col._auto_join = False partner_child_ids_col._auto_join = False partner_state_id_col._auto_join = False + partner_parent_id_col._auto_join = False state_country_id_col._auto_join = False BaseModel._where_calc = self._base_model_where_calc From e36b44e82fd10f87fae2840630ee533478533cf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 7 Dec 2012 12:54:24 +0100 Subject: [PATCH 50/80] [IMP] [WIP] order_by: now using aliases in _generate_order_by, _generate_o2m_order_by, _inherits_join_add, ... added some tests. Next comits will clean a bit the code, because currently it is a bit messy. bzr revid: tde@openerp.com-20121207115424-x8gkjcqpi8dz96g2 --- openerp/addons/base/tests/test_expression.py | 1 - openerp/addons/base/tests/test_search.py | 49 ++++-- openerp/osv/expression.py | 22 +-- openerp/osv/orm.py | 78 ++++++--- openerp/osv/query.py | 159 +++++++++++++++++-- openerp/test/test_osv.py | 12 +- 6 files changed, 253 insertions(+), 68 deletions(-) diff --git a/openerp/addons/base/tests/test_expression.py b/openerp/addons/base/tests/test_expression.py index 37fa24f5b1c..c43dda85e9b 100644 --- a/openerp/addons/base/tests/test_expression.py +++ b/openerp/addons/base/tests/test_expression.py @@ -432,7 +432,6 @@ class test_expression(common.TransactionCase): partner_state_id_col._auto_join = False partner_parent_id_col._auto_join = False state_country_id_col._auto_join = False - BaseModel._where_calc = self._base_model_where_calc if __name__ == '__main__': unittest2.main() diff --git a/openerp/addons/base/tests/test_search.py b/openerp/addons/base/tests/test_search.py index dc554408667..a64bfcb3e94 100644 --- a/openerp/addons/base/tests/test_search.py +++ b/openerp/addons/base/tests/test_search.py @@ -2,16 +2,17 @@ import unittest2 import openerp.tests.common as common -class test_expression(common.TransactionCase): - def test_search_order(self): +class test_search(common.TransactionCase): + + def test_00_search_order(self): registry, cr, uid = self.registry, self.cr, self.uid - # Create 6 partners with a given name, and a given creation order to - # ensure the order of their ID. Some are set as unactive to verify they - # are by default excluded from the searches and to provide a second - # `order` argument. + # Create 6 partners with a given name, and a given creation order to + # ensure the order of their ID. Some are set as unactive to verify they + # are by default excluded from the searches and to provide a second + # `order` argument. partners = registry('res.partner') c = partners.create(cr, uid, {'name': 'test_search_order_C'}) @@ -23,9 +24,9 @@ class test_expression(common.TransactionCase): # The tests. - # The basic searches should exclude records that have active = False. - # The order of the returned ids should be given by the `order` - # parameter of search(). + # The basic searches should exclude records that have active = False. + # The order of the returned ids should be given by the `order` + # parameter of search(). name_asc = partners.search(cr, uid, [('name', 'like', 'test_search_order%')], order="name asc") self.assertEqual([a, ab, b, c], name_asc, "Search with 'NAME ASC' order failed.") @@ -36,9 +37,9 @@ class test_expression(common.TransactionCase): id_desc = partners.search(cr, uid, [('name', 'like', 'test_search_order%')], order="id desc") self.assertEqual([ab, b, a, c], id_desc, "Search with 'ID DESC' order failed.") - # The inactive records shouldn't be excluded as soon as a condition on - # that field is present in the domain. The `order` parameter of - # search() should support any legal coma-separated values. + # The inactive records shouldn't be excluded as soon as a condition on + # that field is present in the domain. The `order` parameter of + # search() should support any legal coma-separated values. active_asc_id_asc = partners.search(cr, uid, [('name', 'like', 'test_search_order%'), '|', ('active', '=', True), ('active', '=', False)], order="active asc, id asc") self.assertEqual([d, e, c, a, b, ab], active_asc_id_asc, "Search with 'ACTIVE ASC, ID ASC' order failed.") @@ -57,4 +58,28 @@ class test_expression(common.TransactionCase): id_desc_active_desc = partners.search(cr, uid, [('name', 'like', 'test_search_order%'), '|', ('active', '=', True), ('active', '=', False)], order="id desc, active desc") self.assertEqual([e, ab, b, a, d, c], id_desc_active_desc, "Search with 'ID DESC, ACTIVE DESC' order failed.") + def test_10_m2order(self): + registry, cr, uid = self.registry, self.cr, self.uid + users_obj = registry('res.users') + # Find Employee group + group_employee_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'base', 'group_user') + group_employee_id = group_employee_ref and group_employee_ref[1] or False + + search_user = users_obj.create(cr, uid, {'name': '__search', 'login': '__search', 'groups_id': [(6, 0, [group_employee_id])]}) + a = users_obj.create(cr, uid, {'name': '__test_A', 'login': '__z_test_A'}) + b = users_obj.create(cr, uid, {'name': '__test_B', 'login': '__a_test_B'}) + + # Do: search on res.users, order on a field on res.partner, then res.users + print '\n\n' + user_ids = users_obj.search(cr, search_user, [], order='name asc, login desc') + self.assertTrue(set([search_user, a, b]).issubset(set(user_ids)), 'cacaprout') + + # DO: order on a many2one + print '\n\n' + user_ids = users_obj.search(cr, search_user, [], order='state_id asc, country_id desc, name asc, login desc') + print user_ids[0:25] + # self.assertTrue(set([search_user, a, b]).issubset(set(user_ids)), 'cacaprout') + +if __name__ == '__main__': + unittest2.main() diff --git a/openerp/osv/expression.py b/openerp/osv/expression.py index 8967d9f254a..526b09ccda2 100644 --- a/openerp/osv/expression.py +++ b/openerp/osv/expression.py @@ -319,16 +319,18 @@ def _quote(to_quote): return to_quote -def generate_table_alias(src_model, joined_tables=[]): +def generate_table_alias(src_table_alias, joined_tables=[]): """ Generate a standard table alias name. An alias is generated as following: - - the base is the source model table name + - the base is the source table name (that can already be an alias) - then, each joined table is added in the alias using a 'link field name' that is used to render unique aliases for a given path + - returns a tuple composed of the alias, and the full table alias to be + added in a from condition Examples: - - src_model=res.users, join_tables=[]: - alias = '"res_users"' - - src_model=res.users, join_tables=[(res.partner, 'parent_id')] - alias = '"res_partner" as "res_users__parent_id"' + - src_table_alias='res_users', join_tables=[]: + alias = ('res_users','"res_users"') + - src_model='res_users', join_tables=[(res.partner, 'parent_id')] + alias = ('res_users__parent_id', '"res_partner" as "res_users__parent_id"') :param model src_model: model source of the alias :param list join_tables: list of tuples @@ -336,12 +338,12 @@ def generate_table_alias(src_model, joined_tables=[]): :return tuple: (table alias, alias statement for from clause with quotes added) """ - alias = src_model._table + alias = src_table_alias if not joined_tables: return ('%s' % alias, '%s' % _quote(alias)) for link in joined_tables: alias += '__' + link[1] - return ('%s' % alias, '%s as %s' % (_quote(joined_tables[-1][0]._table), _quote(alias))) + return ('%s' % alias, '%s as %s' % (_quote(joined_tables[-1][0]), _quote(alias))) def normalize_leaf(element): @@ -499,8 +501,8 @@ class ExtendedLeaf(object): # -------------------------------------------------- def generate_alias(self): - links = [(context[1], context[4]) for context in self.context_stack] - alias, alias_statement = generate_table_alias(self._tables[0], links) + links = [(context[1]._table, context[4]) for context in self.context_stack] + alias, alias_statement = generate_table_alias(self._tables[0]._table, links) return alias def add_join_context(self, table, lhs_col, table_col, link): diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index a08126d12fc..d2c0a0f5682 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -58,7 +58,6 @@ import types import psycopg2 from lxml import etree -import warnings import fields import openerp @@ -2720,20 +2719,23 @@ class BaseModel(object): return result - def _inherits_join_add(self, current_table, parent_model_name, query): + def _inherits_join_add(self, current_model, parent_model_name, query): """ Add missing table SELECT and JOIN clause to ``query`` for reaching the parent table (no duplicates) - :param current_table: current model object + :param current_model: current model object :param parent_model_name: name of the parent model for which the clauses should be added :param query: query object on which the JOIN should be added """ - inherits_field = current_table._inherits[parent_model_name] + inherits_field = current_model._inherits[parent_model_name] parent_model = self.pool.get(parent_model_name) - parent_table_name = parent_model._table - quoted_parent_table_name = '"%s"' % parent_table_name - if quoted_parent_table_name not in query.tables: - query.tables.append(quoted_parent_table_name) - query.where_clause.append('(%s.%s = %s.id)' % (current_table._table, inherits_field, parent_table_name)) + parent_alias = query.add_implicit_join((current_model, parent_model, inherits_field, 'id', inherits_field)) + return parent_alias + # table_alias = expression.generate_table_alias(current_model, [(parent_model, inherits_field)]) + # print '\t... _inherits_join_add trying to add %s in %s' % (table_alias, query.tables) + # # query.add_table(table_alias) + # if table_alias not in query.tables: + # query.tables.append(table_alias) + # query.where_clause.append('("%s".%s = %s.id)' % (current_model._table, inherits_field, table_alias)) def _inherits_join_calc(self, field, query): """ @@ -2744,13 +2746,15 @@ class BaseModel(object): :param query: query object on which the JOIN should be added :return: qualified name of field, to be used in SELECT clause """ + print '\t--> _inherits_join_calc' current_table = self + parent_alias = current_table._table while field in current_table._inherit_fields and not field in current_table._columns: parent_model_name = current_table._inherit_fields[field][0] parent_table = self.pool.get(parent_model_name) - self._inherits_join_add(current_table, parent_model_name, query) + parent_alias = self._inherits_join_add(current_table, parent_model_name, query) current_table = parent_table - return '"%s".%s' % (current_table._table, field) + return '%s."%s"' % (parent_alias, field) def _parent_store_compute(self, cr): if not self._parent_store: @@ -4659,28 +4663,49 @@ class BaseModel(object): :param query: the current query object """ def apply_rule(added_clause, added_params, added_tables, parent_model=None, child_object=None): + """ :param string parent_model: string of the parent model + :param model child_object: model object, base of the rule application + """ if added_clause: + print '--> _apply_ir_rules.apply_rule,', added_clause, added_params, added_tables, parent_model, child_object if parent_model and child_object: + print '\t... calling inherits-Join_add with parent_model %s, child_object %s' % (parent_model, child_object) # as inherited rules are being applied, we need to add the missing JOIN # to reach the parent table (if it was not JOINed yet in the query) - child_object._inherits_join_add(child_object, parent_model, query) + parent_alias = child_object._inherits_join_add(child_object, parent_model, query) + # inherited rules are applied on the external table -> need to get the alias and replace + parent_table = self.pool.get(parent_model)._table + print parent_table, parent_alias + added_clause = [clause.replace('"%s"' % parent_table, '"%s"' % parent_alias) for clause in added_clause] + # not sure of myself here (in the ORM, this statment is quite cool) + new_tables = [] + for table in added_tables: + if table == '"%s"' % (parent_table): + new_table = '"%s" as "%s"' % (parent_table, parent_alias) + else: + new_table = table.replace('"%s"' % parent_table, '"%s"' % parent_alias) + new_tables.append(new_table) + added_tables = new_tables + print '\t... adding tables %s, clause %s (params %s)' % (added_tables, added_clause, added_params) query.where_clause += added_clause query.where_clause_params += added_params for table in added_tables: - quoted_table_name = '%s' % (table) - if quoted_table_name not in query.tables: - query.tables.append(quoted_table_name) + print '\t... adding table %s in %s' % (table, query.tables) + if table not in query.tables: + query.tables.append(table) return True return False # apply main rules on the object rule_obj = self.pool.get('ir.rule') - apply_rule(*rule_obj.domain_get(cr, uid, self._name, mode, context=context)) + rule_where_clause, rule_where_clause_params, rule_tables = rule_obj.domain_get(cr, uid, self._name, mode, context=context) + apply_rule(rule_where_clause, rule_where_clause_params, rule_tables) # apply ir.rules from the parents (through _inherits) for inherited_model in self._inherits: - kwargs = dict(parent_model=inherited_model, child_object=self) #workaround for python2.5 - apply_rule(*rule_obj.domain_get(cr, uid, inherited_model, mode, context=context), **kwargs) + rule_where_clause, rule_where_clause_params, rule_tables = rule_obj.domain_get(cr, uid, inherited_model, mode, context=context) + apply_rule(rule_where_clause, rule_where_clause_params, rule_tables, + parent_model=inherited_model, child_object=self) def _generate_m2o_order_by(self, order_field, query): """ @@ -4690,6 +4715,7 @@ class BaseModel(object): :return: the qualified field name to use in an ORDER BY clause to sort by ``order_field`` """ + print '_generate_m2o_order_by' if order_field not in self._columns and order_field in self._inherit_fields: # also add missing joins for reaching the table containing the m2o field qualified_field = self._inherits_join_calc(order_field, query) @@ -4715,17 +4741,16 @@ class BaseModel(object): # extract the field names, to be able to qualify them and add desc/asc m2o_order_list = [] for order_part in m2o_order.split(","): - m2o_order_list.append(order_part.strip().split(" ",1)[0].strip()) + m2o_order_list.append(order_part.strip().split(" ", 1)[0].strip()) m2o_order = m2o_order_list # Join the dest m2o table if it's not joined yet. We use [LEFT] OUTER join here # as we don't want to exclude results that have NULL values for the m2o - src_table, src_field = qualified_field.replace('"','').split('.', 1) - query.join((src_table, dest_model._table, src_field, 'id'), outer=True) - qualify = lambda field: '"%s"."%s"' % (dest_model._table, field) + src_table, src_field = qualified_field.replace('"', '').split('.', 1) + dst_alias = query.add_join((src_table, dest_model._table, src_field, 'id'), outer=True) + qualify = lambda field: '"%s"."%s"' % (dst_alias, field) return map(qualify, m2o_order) if isinstance(m2o_order, list) else qualify(m2o_order) - def _generate_order_by(self, order_spec, query): """ Attempt to consruct an appropriate ORDER BY clause based on order_spec, which must be @@ -4742,9 +4767,9 @@ class BaseModel(object): subelems.append(' ') order_list.append('"%s"."%s" %s' % (table, subelems[0], subelems[1])) return order_list - order_by_clause = ','.join(_split_order(self._order, self._table)) if order_spec: + print '-->_generate_order_by beginning' order_by_elements = [] self._check_qorder(order_spec) for order_part in order_spec.split(','): @@ -4761,7 +4786,7 @@ class BaseModel(object): elif order_column._type == 'many2one': inner_clause = self._generate_m2o_order_by(order_field, query) else: - continue # ignore non-readable or "non-joinable" fields + continue # ignore non-readable or "non-joinable" fields elif order_field in self._inherit_fields: parent_obj = self.pool.get(self._inherit_fields[order_field][3]) order_column = parent_obj._columns[order_field] @@ -4770,7 +4795,7 @@ class BaseModel(object): elif order_column._type == 'many2one': inner_clause = self._generate_m2o_order_by(order_field, query) else: - continue # ignore non-readable or "non-joinable" fields + continue # ignore non-readable or "non-joinable" fields if inner_clause: if isinstance(inner_clause, list): for clause in inner_clause: @@ -4779,6 +4804,7 @@ class BaseModel(object): order_by_elements.append("%s %s" % (inner_clause, order_direction)) if order_by_elements: order_by_clause = ",".join(order_by_elements) + print '-->_generate_order_by ending' return order_by_clause and (' ORDER BY %s ' % order_by_clause) or '' diff --git a/openerp/osv/query.py b/openerp/osv/query.py index d32ea73b29d..a07def36a31 100644 --- a/openerp/osv/query.py +++ b/openerp/osv/query.py @@ -21,12 +21,21 @@ #.apidoc title: Query object + def _quote(to_quote): if '"' not in to_quote: return '"%s"' % to_quote return to_quote +def _get_alias_from_statement(string): + if len(string.split(' as ')) > 1: + alias = string.split(' as ')[1].replace('"', '') + else: + alias = string.replace('"', '') + return alias + + class Query(object): """ Dumb implementation of a Query object, using 3 string lists so far @@ -44,6 +53,9 @@ class Query(object): # holds the list of tables joined using default JOIN. # the table names are stored double-quoted (backwards compatibility) self.tables = tables or [] + # holds a mapping of table aliases: + # self._table_alias_mapping = {'alias_1': 'table_name'} + self._table_alias_mapping = {} # holds the list of WHERE clause elements, to be joined with # 'AND' when generating the final query @@ -67,7 +79,110 @@ class Query(object): # LEFT JOIN "table_c" ON ("table_a"."table_a_col2" = "table_c"."table_c_col") self.joins = joins or {} - def join(self, connection, outer=False): + def _add_table_alias(self, table_alias): + pass + + def _get_table_aliases(self): + aliases = [] + for table in self.tables: + if len(table.split(' as ')) > 1: + aliases.append(table.split(' as ')[1].replace('"', '')) + else: + aliases.append(table.replace('"', '')) + # print '--', aliases + return aliases + + def _get_alias_mapping(self): + mapping = {} + aliases = self._get_table_aliases() + for alias in aliases: + for table in self.tables: + if '"%s"' % (alias) in table: + mapping.setdefault(alias, table) + return mapping + + def add_new_join(self, connection, implicit=True, outer=False): + """ Join a destination table to the current table. + + :param implicit: False if the join is an explicit join. This allows + to fall back on the previous implementation of ``join`` before + OpenERP 7.0. It therefore adds the JOIN specified in ``connection`` + If True, the join is done implicitely, by adding the table alias + in the from clause and the join condition in the where clause + of the query. + :param connection: a tuple ``(lhs, table, lhs_col, col, link)``. + The join corresponds to the SQL equivalent of:: + + (lhs.lhs_col = table.col) + + Note that all connection elements are strings. Please refer to expression.py for more details about joins. + + :param outer: True if a LEFT OUTER JOIN should be used, if possible + (no promotion to OUTER JOIN is supported in case the JOIN + was already present in the query, as for the moment + implicit INNER JOINs are only connected from NON-NULL + columns so it would not be correct (e.g. for + ``_inherits`` or when a domain criterion explicitly + adds filtering) + """ + from openerp.osv.expression import generate_table_alias + (lhs, table, lhs_col, col, link) = connection + alias, alias_statement = generate_table_alias(lhs._table, [(table._table, link)]) + + if implicit: + print '\t\t... Query: trying to add %s in %s (received %s)' % (alias_statement, self.tables, connection) + if alias_statement not in self.tables: + self.tables.append(alias_statement) + condition = '("%s"."%s" = "%s"."%s")' % (lhs._table, lhs_col, alias, col) + print '\t\t... added %s' % (condition) + self.where_clause.append(condition) + return alias + else: + (lhs, table, lhs_col, col) = connection + lhs = _quote(lhs) + table = _quote(table) + print connection + aliases = [] + for table in self.tables: + if len(table.split(' as ')) > 1: + aliases.append(table.split(' as ')[1]) + else: + aliases.append(table) + print '--', aliases + aliases = [table.split(' as ') for table in self.tables] + assert lhs in self.aliases, "Left-hand-side table %s must already be part of the query tables %s!" % (lhs, str(self.tables)) + if table in self.tables: + # already joined, must ignore (promotion to outer and multiple joins not supported yet) + pass + else: + # add JOIN + self.tables.append(table) + self.joins.setdefault(lhs, []).append((table, lhs_col, col, outer and 'LEFT JOIN' or 'JOIN')) + return self + + def add_implicit_join(self, connection): + """ Adds an implicit join. This means that left-hand table is added to the + Query.tables (adding a table in the from clause), and that a join + condition is added in Query.where_clause. + + Implicit joins use expression.generate_table_alias to generate the + alias the the joined table. + + :param connection: a tuple``(lhs, table, lhs_col, col, link)`` Please + refer to expression.py for more details about joins. + """ + from openerp.osv.expression import generate_table_alias + (lhs, table, lhs_col, col, link) = connection + alias, alias_statement = generate_table_alias(lhs._table, [(table._table, link)]) + print '\t\t... Query: trying to add %s in %s (received %s)' % (alias_statement, self.tables, connection) + if alias_statement not in self.tables: + self.tables.append(alias_statement) + condition = '("%s"."%s" = "%s"."%s")' % (lhs._table, lhs_col, alias, col) + print '\t\t... added %s' % (condition) + self.where_clause.append(condition) + return alias + + def add_join(self, connection, outer=False): """Adds the JOIN specified in ``connection``. :param connection: a tuple ``(lhs, table, lhs_col, col)``. @@ -75,6 +190,8 @@ class Query(object): (lhs.lhs_col = table.col) + Note that all connection elements are strings. + :param outer: True if a LEFT OUTER JOIN should be used, if possible (no promotion to OUTER JOIN is supported in case the JOIN was already present in the query, as for the moment @@ -83,36 +200,52 @@ class Query(object): ``_inherits`` or when a domain criterion explicitly adds filtering) """ + from openerp.osv.expression import generate_table_alias (lhs, table, lhs_col, col) = connection - lhs = _quote(lhs) - table = _quote(table) - assert lhs in self.tables, "Left-hand-side table must already be part of the query!" - if table in self.tables: + # lhs = _quote(lhs) + # table = _quote(table) + print '\t\t... Query.add_join(): adding connection %s' % str(connection) + + aliases = self._get_table_aliases() + + assert lhs in aliases, "Left-hand-side table %s must already be part of the query tables %s!" % (lhs, str(self.tables)) + + rhs, rhs_statement = generate_table_alias(lhs, [(connection[1], connection[2])]) + print rhs, rhs_statement + + if rhs_statement in self.tables: # already joined, must ignore (promotion to outer and multiple joins not supported yet) pass else: # add JOIN - self.tables.append(table) - self.joins.setdefault(lhs, []).append((table, lhs_col, col, outer and 'LEFT JOIN' or 'JOIN')) - return self + self.tables.append(rhs_statement) + self.joins.setdefault(lhs, []).append((rhs, lhs_col, col, outer and 'LEFT JOIN' or 'JOIN')) + return rhs def get_sql(self): """Returns (query_from, query_where, query_params)""" query_from = '' tables_to_process = list(self.tables) + alias_mapping = self._get_alias_mapping() + + # print 'tables_to_process %s' % (tables_to_process) + # print 'self.joins %s' % (self.joins) + # print 'alias_mapping %s' % (alias_mapping) + def add_joins_for_table(table, query_from): for (dest_table, lhs_col, col, join) in self.joins.get(table, []): - tables_to_process.remove(dest_table) - query_from += ' %s %s ON (%s."%s" = %s."%s")' % \ - (join, dest_table, table, lhs_col, dest_table, col) + # print dest_table + tables_to_process.remove(alias_mapping[dest_table]) + query_from += ' %s %s ON ("%s"."%s" = "%s"."%s")' % \ + (join, alias_mapping[dest_table], table, lhs_col, dest_table, col) query_from = add_joins_for_table(dest_table, query_from) return query_from for table in tables_to_process: query_from += table - if table in self.joins: - query_from = add_joins_for_table(table, query_from) + if _get_alias_from_statement(table) in self.joins: + query_from = add_joins_for_table(_get_alias_from_statement(table), query_from) query_from += ',' query_from = query_from[:-1] # drop last comma return (query_from, " AND ".join(self.where_clause), self.where_clause_params) diff --git a/openerp/test/test_osv.py b/openerp/test/test_osv.py index d3ff75c03d5..2db9cec1cbd 100644 --- a/openerp/test/test_osv.py +++ b/openerp/test/test_osv.py @@ -28,8 +28,8 @@ class QueryTestCase(unittest.TestCase): query = Query() query.tables.extend(['"product_product"','"product_template"']) query.where_clause.append("product_product.template_id = product_template.id") - query.join(("product_template", "product_category", "categ_id", "id"), outer=False) # add normal join - query.join(("product_product", "res_user", "user_id", "id"), outer=True) # outer join + query.add_join(("product_template", "product_category", "categ_id", "id"), outer=False) # add normal join + query.add_join(("product_product", "res_user", "user_id", "id"), outer=True) # outer join self.assertEquals(query.get_sql()[0].strip(), """"product_product" LEFT JOIN "res_user" ON ("product_product"."user_id" = "res_user"."id"),"product_template" JOIN "product_category" ON ("product_template"."categ_id" = "product_category"."id") """.strip()) self.assertEquals(query.get_sql()[1].strip(), """product_product.template_id = product_template.id""".strip()) @@ -38,8 +38,8 @@ class QueryTestCase(unittest.TestCase): query = Query() query.tables.extend(['"product_product"','"product_template"']) query.where_clause.append("product_product.template_id = product_template.id") - query.join(("product_template", "product_category", "categ_id", "id"), outer=False) # add normal join - query.join(("product_category", "res_user", "user_id", "id"), outer=True) # CHAINED outer join + query.add_join(("product_template", "product_category", "categ_id", "id"), outer=False) # add normal join + query.add_join(("product_category", "res_user", "user_id", "id"), outer=True) # CHAINED outer join self.assertEquals(query.get_sql()[0].strip(), """"product_product","product_template" JOIN "product_category" ON ("product_template"."categ_id" = "product_category"."id") LEFT JOIN "res_user" ON ("product_category"."user_id" = "res_user"."id")""".strip()) self.assertEquals(query.get_sql()[1].strip(), """product_product.template_id = product_template.id""".strip()) @@ -48,8 +48,8 @@ class QueryTestCase(unittest.TestCase): query = Query() query.tables.extend(['"product_product"','"product_template"']) query.where_clause.append("product_product.template_id = product_template.id") - query.join(("product_template", "product_category", "categ_id", "id"), outer=False) # add normal join - query.join(("product_category", "res_user", "user_id", "id"), outer=True) # CHAINED outer join + query.add_join(("product_template", "product_category", "categ_id", "id"), outer=False) # add normal join + query.add_join(("product_category", "res_user", "user_id", "id"), outer=True) # CHAINED outer join query.tables.append('"account.account"') query.where_clause.append("product_category.expense_account_id = account_account.id") # additional implicit join self.assertEquals(query.get_sql()[0].strip(), From 390ff1540fb8eef5228e10a74d9aa2c88daa886a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 7 Dec 2012 13:37:27 +0100 Subject: [PATCH 51/80] [CLEAN] expression.py: cleaned and added comments, var names (table -> model notably). bzr revid: tde@openerp.com-20121207123727-md65da41863y827e --- openerp/osv/expression.py | 331 ++++++++++++++++++++------------------ 1 file changed, 173 insertions(+), 158 deletions(-) diff --git a/openerp/osv/expression.py b/openerp/osv/expression.py index 526b09ccda2..703b7503387 100644 --- a/openerp/osv/expression.py +++ b/openerp/osv/expression.py @@ -325,7 +325,7 @@ def generate_table_alias(src_table_alias, joined_tables=[]): - then, each joined table is added in the alias using a 'link field name' that is used to render unique aliases for a given path - returns a tuple composed of the alias, and the full table alias to be - added in a from condition + added in a from condition with quoting done Examples: - src_table_alias='res_users', join_tables=[]: alias = ('res_users','"res_users"') @@ -336,7 +336,7 @@ def generate_table_alias(src_table_alias, joined_tables=[]): :param list join_tables: list of tuples (dst_model, link_field) - :return tuple: (table alias, alias statement for from clause with quotes added) + :return tuple: (table_alias, alias statement for from clause with quotes added) """ alias = src_table_alias if not joined_tables: @@ -366,11 +366,10 @@ def normalize_leaf(element): def is_operator(element): - """Test whether an object is a valid domain operator. """ + """ Test whether an object is a valid domain operator. """ return isinstance(element, basestring) and element in DOMAIN_OPERATORS -# TODO change the share wizard to use this function. def is_leaf(element, internal=False): """ Test whether an object is a valid domain term: - is a list or tuple @@ -380,6 +379,8 @@ def is_leaf(element, internal=False): :param tuple element: a leaf in form (left, operator, right) :param boolean internal: allow or not the 'inselect' internal operator in the term. This should be always left to False. + + Note: OLD TODO change the share wizard to use this function. """ INTERNAL_OPS = TERM_OPERATORS + ('<>',) if internal: @@ -421,39 +422,10 @@ def select_distinct_from_where_not_null(cr, select_field, from_table): # ------------------------------------------------- class ExtendedLeaf(object): - - def __init__(self, leaf, table, context_stack=None): - """ Initialize the ExtendedLeaf - - :attr [string, tuple] leaf: operator or tuple-formatted domain - expression - :attr object table: table object - :attr list _tables: list of chained table objects, updated when - adding joins - :attr tuple elements: manipulation-friendly leaf - :attr object field: field obj, taken from table, not necessarily - found (inherits, 'id') - :attr list field_path: exploded left of elements - (partner_id.name -> ['partner_id', 'name']) - :attr object relational_table: distant table for relational fields - """ - assert table, 'Invalid leaf creation without table' - self.context_stack = context_stack or [] - # validate the leaf - self.leaf = leaf - # normalize the leaf's operator - self.normalize_leaf() - # set working variables; handle the context stack and previous tables - self.table = table - self._tables = [] - for item in self.context_stack: - self._tables.append(item[0]) - self._tables.append(table) - # check validity - self.check_leaf() - - def __str__(self): - return '' % (str(self.leaf), self.table._table, ','.join(self._get_context_debug())) + """ Class wrapping a domain leaf, and giving some services and management + features on it. In particular it managed join contexts to be able to + construct queries through multiple models. + """ # -------------------------------------------------- # Join / Context manipulation @@ -500,32 +472,61 @@ class ExtendedLeaf(object): # i.e.: many2one: 'state_id': current field name # -------------------------------------------------- + def __init__(self, leaf, model, join_context=None): + """ Initialize the ExtendedLeaf + + :attr [string, tuple] leaf: operator or tuple-formatted domain + expression + :attr obj model: current working model + :attr list _models: list of chained models, updated when + adding joins + :attr list join_context: list of join contexts. This is a list of + tuples like ``(lhs, table, lhs_col, col, link)`` + :param obj lhs: source (left hand) model + :param obj model: destination (right hand) model + :param string lhs_col: source model column for join condition + :param string col: destination model column for join condition + :param link: link column between source and destination model + that is not necessarily (but generally) a real column used + in the condition (i.e. in many2one); this link is used to + compute aliases + """ + assert model, 'Invalid leaf creation without table' + self.join_context = join_context or [] + self.leaf = leaf + # normalize the leaf's operator + self.normalize_leaf() + # set working variables; handle the context stack and previous tables + self.model = model + self._models = [] + for item in self.join_context: + self._models.append(item[0]) + self._models.append(model) + # check validity + self.check_leaf() + + def __str__(self): + return '' % (str(self.leaf), self.model._table, ','.join(self._get_context_debug())) + def generate_alias(self): - links = [(context[1]._table, context[4]) for context in self.context_stack] - alias, alias_statement = generate_table_alias(self._tables[0]._table, links) + links = [(context[1]._table, context[4]) for context in self.join_context] + alias, alias_statement = generate_table_alias(self._models[0]._table, links) return alias - def add_join_context(self, table, lhs_col, table_col, link): + def add_join_context(self, model, lhs_col, table_col, link): """ See above comments for more details. A join context is a tuple like: - ``(lhs, table, lhs_col, col, link)`` - where - - lhs is the source table (self.table) - - table is the destination table - - lsh_col is the source table column name used for the condition - - table_col is the destination table column name used for the condition - - link is the field name source of the join used as context to - generate the destination table alias + ``(lhs, model, lhs_col, col, link)`` - After adding the join, the table of the current leaf is updated. + After adding the join, the model of the current leaf is updated. """ - self.context_stack.append((self.table, table, lhs_col, table_col, link)) - self._tables.append(table) - self.table = table + self.join_context.append((self.model, model, lhs_col, table_col, link)) + self._models.append(model) + self.model = model def get_join_conditions(self): conditions = [] - alias = self._tables[0]._table - for context in self.context_stack: + alias = self._models[0]._table + for context in self.join_context: previous_alias = alias alias += '__' + context[4] conditions.append('"%s"."%s"="%s"."%s"' % (previous_alias, context[2], alias, context[3])) @@ -533,15 +534,15 @@ class ExtendedLeaf(object): def get_tables(self): tables = set() - alias = self._tables[0]._table - for context in self.context_stack: - alias += '__' + context[4] - table_full_alias = '"%s" as "%s"' % (context[1]._table, alias) - tables.add(table_full_alias) + links = [] + for context in self.join_context: + links.append((context[1]._table, context[4])) + alias, alias_statement = generate_table_alias(self._models[0]._table, links) + tables.add(alias_statement) return tables def _get_context_debug(self): - names = ['"%s"."%s"="%s"."%s" (%s)' % (item[0]._table, item[2], item[1]._table, item[3], item[4]) for item in self.context_stack] + names = ['"%s"."%s"="%s"."%s" (%s)' % (item[0]._table, item[2], item[1]._table, item[3], item[4]) for item in self.join_context] return names # -------------------------------------------------- @@ -589,20 +590,19 @@ class expression(object): right after initialization. :param exp: expression (using domain ('foo', '=', 'bar' format)) - :param table: root table object + :param table: root model :attr list result: list that will hold the result of the parsing as a list of ExtendedLeaf :attr list joins: list of join conditions, such as (res_country_state."id" = res_partner."state_id") - :attr root_table: base table for the query + :attr root_model: base model for the query :attr list expression: the domain expression, that will be normalized and prepared """ self.has_unaccent = openerp.modules.registry.RegistryManager.get(cr.dbname).has_unaccent - self.result = [] self.joins = [] - self.root_table = table + self.root_model = table # normalize and prepare the expression for parsing self.expression = distribute_not(normalize_domain(exp)) @@ -621,7 +621,7 @@ class expression(object): for table in leaf.get_tables(): if table not in tables: tables.append(table) - table_name = '"%s"' % self.root_table._table + table_name = _quote(self.root_model._table) if table_name not in tables: tables.append(table_name) return tables @@ -633,29 +633,30 @@ class expression(object): def parse(self, cr, uid, context): """ Transform the leaves of the expression - The principle is to pop elements from the left of a leaf stack. Each - leaf is processed. The processing is a if/elif list of various cases - that appear in the leafs (many2one, function fields, ...). Two results - can appear at the end of a leaf processing: - - the leaf is modified or new leafs introduced in the domain: they - are added at the left of the stack, to be processed next + The principle is to pop elements from a leaf stack one at a time. + Each leaf is processed. The processing is a if/elif list of various + cases that appear in the leafs (many2one, function fields, ...). + Two things can happen as a processing result: + - the leaf has been modified and/or new leafs have to be introduced + in the expression; they are pushed into the leaf stack, to be + processed right after - the leaf is added to the result - Some var explanation: - :var obj working_table: table object, table containing the field + Some internal var explanation: + :var obj working_model: model object, model containing the field (the name provided in the left operand) :var list field_path: left operand seen as a path (foo.bar -> [foo, bar]) - :var obj relational_table: relational table of a field (field._obj) - ex: res_partner.bank_ids -> res_partner_bank + :var obj relational_model: relational model of a field (field._obj) + ex: res_partner.bank_ids -> res.partner.bank """ - def to_ids(value, relational_table, context=None, limit=None): + def to_ids(value, relational_model, context=None, limit=None): """ Normalize a single id or name, or a list of those, into a list of ids :param {int,long,basestring,list,tuple} value: if int, long -> return [value] if basestring, convert it into a list of basestrings, then if list of basestring -> - perform a name_search on relational_table for each name + perform a name_search on relational_model for each name return the list of related ids """ names = [] @@ -666,7 +667,7 @@ class expression(object): elif isinstance(value, (int, long)): return [value] if names: - name_get_list = [name_get[0] for name in names for name_get in relational_table.name_search(cr, uid, name, [], 'ilike', context=context, limit=limit)] + name_get_list = [name_get[0] for name in names for name_get in relational_model.name_search(cr, uid, name, [], 'ilike', context=context, limit=limit)] return list(set(name_get_list)) return list(value) @@ -693,24 +694,31 @@ class expression(object): return ids + recursive_children(ids2, model, parent_field) return [(left, 'in', recursive_children(ids, left_model, parent or left_model._parent_name))] - def create_substitution_leaf(leaf, new_elements, new_table=None): - if new_table is None: - new_table = leaf.table - new_context_stack = [tuple(context) for context in leaf.context_stack] - new_leaf = ExtendedLeaf(new_elements, new_table, context_stack=new_context_stack) + def create_substitution_leaf(leaf, new_elements, new_model=None): + """ From a leaf, create a new leaf (based on the new_elements tuple + and new_model), that will have the same join context. Used to + insert equivalent leafs in the processing stack. """ + if new_model is None: + new_model = leaf.model + new_join_context = [tuple(context) for context in leaf.join_context] + new_leaf = ExtendedLeaf(new_elements, new_model, join_context=new_join_context) return new_leaf def pop(): + """ Pop a leaf to process. """ return self.stack.pop() def push(leaf): + """ Push a leaf to be processed right after. """ self.stack.append(leaf) def push_result(leaf): + """ Push a leaf to the results. This leaf has been fully processed + and validated. """ self.result.append(leaf) self.result = [] - self.stack = [ExtendedLeaf(leaf, self.root_table) for leaf in self.expression] + self.stack = [ExtendedLeaf(leaf, self.root_model) for leaf in self.expression] # process from right to left; expression is from left to right self.stack.reverse() @@ -719,7 +727,7 @@ class expression(object): leaf = pop() # Get working variables - working_table = leaf.table + working_model = leaf.model if leaf.is_operator(): left, operator, right = leaf.leaf, None, None elif leaf.is_true_leaf() or leaf.is_false_leaf(): @@ -728,11 +736,11 @@ class expression(object): else: left, operator, right = leaf.leaf field_path = left.split('.', 1) - field = working_table._columns.get(field_path[0]) + field = working_model._columns.get(field_path[0]) if field and field._obj: - relational_table = working_table.pool.get(field._obj) + relational_model = working_model.pool.get(field._obj) else: - relational_table = None + relational_model = None # ---------------------------------------- # SIMPLE CASE @@ -746,7 +754,7 @@ class expression(object): # ---------------------------------------- # FIELD NOT FOUND - # -> from inherits'd fields -> work on the related table, and add + # -> from inherits'd fields -> work on the related model, and add # a join condition # -> ('id', 'child_of', '..') -> use a 'to_ids' # -> but is one on the _log_access special fields, add directly to @@ -755,19 +763,19 @@ class expression(object): # -> else: crash # ---------------------------------------- - elif not field and field_path[0] in working_table._inherit_fields: + elif not field and field_path[0] in working_model._inherit_fields: # comments about inherits'd fields # { 'field_name': ('parent_model', 'm2o_field_to_reach_parent', # field_column_obj, origina_parent_model), ... } - next_table = working_table.pool.get(working_table._inherit_fields[field_path[0]][0]) - leaf.add_join_context(next_table, working_table._inherits[next_table._name], 'id', working_table._inherits[next_table._name]) + next_model = working_model.pool.get(working_model._inherit_fields[field_path[0]][0]) + leaf.add_join_context(next_model, working_model._inherits[next_model._name], 'id', working_model._inherits[next_model._name]) push(leaf) elif not field and left == 'id' and operator == 'child_of': - ids2 = to_ids(right, working_table, context) - dom = child_of_domain(left, ids2, working_table) + ids2 = to_ids(right, working_model, context) + dom = child_of_domain(left, ids2, working_model) for dom_leaf in reversed(dom): - new_leaf = create_substitution_leaf(leaf, dom_leaf, working_table) + new_leaf = create_substitution_leaf(leaf, dom_leaf, working_model) push(new_leaf) elif not field and field_path[0] in MAGIC_COLUMNS: @@ -791,32 +799,32 @@ class expression(object): elif len(field_path) > 1 and field._type == 'many2one' and field._auto_join: # res_partner.state_id = res_partner__state_id.id - leaf.add_join_context(relational_table, field_path[0], 'id', field_path[0]) - push(create_substitution_leaf(leaf, (field_path[1], operator, right), relational_table)) + leaf.add_join_context(relational_model, field_path[0], 'id', field_path[0]) + push(create_substitution_leaf(leaf, (field_path[1], operator, right), relational_model)) elif len(field_path) > 1 and field._type == 'one2many' and field._auto_join: # res_partner.id = res_partner__bank_ids.partner_id - leaf.add_join_context(relational_table, 'id', field._fields_id, field_path[0]) - domain = field._domain(working_table) if callable(field._domain) else field._domain - push(create_substitution_leaf(leaf, (field_path[1], operator, right), relational_table)) + leaf.add_join_context(relational_model, 'id', field._fields_id, field_path[0]) + domain = field._domain(working_model) if callable(field._domain) else field._domain + push(create_substitution_leaf(leaf, (field_path[1], operator, right), relational_model)) if domain: domain = normalize_domain(domain) for elem in reversed(domain): - push(create_substitution_leaf(leaf, elem, relational_table)) - push(create_substitution_leaf(leaf, AND_OPERATOR, relational_table)) + push(create_substitution_leaf(leaf, elem, relational_model)) + push(create_substitution_leaf(leaf, AND_OPERATOR, relational_model)) elif len(field_path) > 1 and field._auto_join: raise NotImplementedError('_auto_join attribute not supported on many2many field %s' % (left)) elif len(field_path) > 1 and field._type == 'many2one': - right_ids = relational_table.search(cr, uid, [(field_path[1], operator, right)], context=context) + right_ids = relational_model.search(cr, uid, [(field_path[1], operator, right)], context=context) leaf.leaf = (field_path[0], 'in', right_ids) push(leaf) # Making search easier when there is a left operand as field.o2m or field.m2m elif len(field_path) > 1 and field._type in ['many2many', 'one2many']: - right_ids = relational_table.search(cr, uid, [(field_path[1], operator, right)], context=context) - table_ids = working_table.search(cr, uid, [(field_path[0], 'in', right_ids)], context=dict(context, active_test=False)) + right_ids = relational_model.search(cr, uid, [(field_path[1], operator, right)], context=context) + table_ids = working_model.search(cr, uid, [(field_path[0], 'in', right_ids)], context=dict(context, active_test=False)) leaf.leaf = ('id', 'in', table_ids) push(leaf) @@ -842,7 +850,7 @@ class expression(object): elif isinstance(field, fields.function) and not field.store: # this is a function field that is not stored - fct_domain = field.search(cr, uid, working_table, left, [leaf.leaf], context=context) + fct_domain = field.search(cr, uid, working_model, left, [leaf.leaf], context=context) if not fct_domain: leaf.leaf = TRUE_LEAF push(leaf) @@ -850,26 +858,30 @@ class expression(object): # we assume that the expression is valid # we create a dummy leaf for forcing the parsing of the resulting expression for domain_element in reversed(fct_domain): - push(create_substitution_leaf(leaf, domain_element, working_table)) - # self.push(create_substitution_leaf(leaf, TRUE_LEAF, working_table)) - # self.push(create_substitution_leaf(leaf, AND_OPERATOR, working_table)) + push(create_substitution_leaf(leaf, domain_element, working_model)) + # self.push(create_substitution_leaf(leaf, TRUE_LEAF, working_model)) + # self.push(create_substitution_leaf(leaf, AND_OPERATOR, working_model)) + + # ------------------------------------------------- + # RELATIONAL FIELDS + # ------------------------------------------------- # Applying recursivity on field(one2many) elif field._type == 'one2many' and operator == 'child_of': - ids2 = to_ids(right, relational_table, context) - if field._obj != working_table._name: - dom = child_of_domain(left, ids2, relational_table, prefix=field._obj) + ids2 = to_ids(right, relational_model, context) + if field._obj != working_model._name: + dom = child_of_domain(left, ids2, relational_model, prefix=field._obj) else: - dom = child_of_domain('id', ids2, working_table, parent=left) + dom = child_of_domain('id', ids2, working_model, parent=left) for dom_leaf in reversed(dom): - push(create_substitution_leaf(leaf, dom_leaf, working_table)) + push(create_substitution_leaf(leaf, dom_leaf, working_model)) elif field._type == 'one2many': call_null = True if right is not False: if isinstance(right, basestring): - ids2 = [x[0] for x in relational_table.name_search(cr, uid, right, [], operator, context=context, limit=None)] + ids2 = [x[0] for x in relational_model.name_search(cr, uid, right, [], operator, context=context, limit=None)] if ids2: operator = 'in' else: @@ -881,36 +893,36 @@ class expression(object): if operator in ['like', 'ilike', 'in', '=']: #no result found with given search criteria call_null = False - push(create_substitution_leaf(leaf, FALSE_LEAF, working_table)) + push(create_substitution_leaf(leaf, FALSE_LEAF, working_model)) else: - ids2 = select_from_where(cr, field._fields_id, relational_table._table, 'id', ids2, operator) + ids2 = select_from_where(cr, field._fields_id, relational_model._table, 'id', ids2, operator) if ids2: call_null = False o2m_op = 'not in' if operator in NEGATIVE_TERM_OPERATORS else 'in' - push(create_substitution_leaf(leaf, ('id', o2m_op, ids2), working_table)) + push(create_substitution_leaf(leaf, ('id', o2m_op, ids2), working_model)) if call_null: o2m_op = 'in' if operator in NEGATIVE_TERM_OPERATORS else 'not in' - push(create_substitution_leaf(leaf, ('id', o2m_op, select_distinct_from_where_not_null(cr, field._fields_id, relational_table._table)), working_table)) + push(create_substitution_leaf(leaf, ('id', o2m_op, select_distinct_from_where_not_null(cr, field._fields_id, relational_model._table)), working_model)) elif field._type == 'many2many': - rel_table, rel_id1, rel_id2 = field._sql_names(working_table) + rel_table, rel_id1, rel_id2 = field._sql_names(working_model) #FIXME if operator == 'child_of': def _rec_convert(ids): - if relational_table == working_table: + if relational_model == working_model: return ids return select_from_where(cr, rel_id1, rel_table, rel_id2, ids, operator) - ids2 = to_ids(right, relational_table, context) - dom = child_of_domain('id', ids2, relational_table) - ids2 = relational_table.search(cr, uid, dom, context=context) - push(create_substitution_leaf(leaf, ('id', 'in', _rec_convert(ids2)), working_table)) + ids2 = to_ids(right, relational_model, context) + dom = child_of_domain('id', ids2, relational_model) + ids2 = relational_model.search(cr, uid, dom, context=context) + push(create_substitution_leaf(leaf, ('id', 'in', _rec_convert(ids2)), working_model)) else: call_null_m2m = True if right is not False: if isinstance(right, basestring): - res_ids = [x[0] for x in relational_table.name_search(cr, uid, right, [], operator, context=context)] + res_ids = [x[0] for x in relational_model.name_search(cr, uid, right, [], operator, context=context)] if res_ids: operator = 'in' else: @@ -922,29 +934,29 @@ class expression(object): if operator in ['like', 'ilike', 'in', '=']: #no result found with given search criteria call_null_m2m = False - push(create_substitution_leaf(leaf, FALSE_LEAF, working_table)) + push(create_substitution_leaf(leaf, FALSE_LEAF, working_model)) else: operator = 'in' # operator changed because ids are directly related to main object else: call_null_m2m = False m2m_op = 'not in' if operator in NEGATIVE_TERM_OPERATORS else 'in' - push(create_substitution_leaf(leaf, ('id', m2m_op, select_from_where(cr, rel_id1, rel_table, rel_id2, res_ids, operator) or [0]), working_table)) + push(create_substitution_leaf(leaf, ('id', m2m_op, select_from_where(cr, rel_id1, rel_table, rel_id2, res_ids, operator) or [0]), working_model)) if call_null_m2m: m2m_op = 'in' if operator in NEGATIVE_TERM_OPERATORS else 'not in' - push(create_substitution_leaf(leaf, ('id', m2m_op, select_distinct_from_where_not_null(cr, rel_id1, rel_table)), working_table)) + push(create_substitution_leaf(leaf, ('id', m2m_op, select_distinct_from_where_not_null(cr, rel_id1, rel_table)), working_model)) elif field._type == 'many2one': if operator == 'child_of': - ids2 = to_ids(right, relational_table, context) - if field._obj != working_table._name: - dom = child_of_domain(left, ids2, relational_table, prefix=field._obj) + ids2 = to_ids(right, relational_model, context) + if field._obj != working_model._name: + dom = child_of_domain(left, ids2, relational_model, prefix=field._obj) else: - dom = child_of_domain('id', ids2, working_table, parent=left) + dom = child_of_domain('id', ids2, working_model, parent=left) for dom_leaf in reversed(dom): - push(create_substitution_leaf(leaf, dom_leaf, working_table)) + push(create_substitution_leaf(leaf, dom_leaf, working_model)) else: - def _get_expression(relational_table, cr, uid, left, right, operator, context=None): + def _get_expression(relational_model, cr, uid, left, right, operator, context=None): if context is None: context = {} c = context.copy() @@ -959,29 +971,32 @@ class expression(object): operator = dict_op[operator] elif isinstance(right, list) and operator in ['!=', '=']: # for domain (FIELD,'=',['value1','value2']) operator = dict_op[operator] - res_ids = [x[0] for x in relational_table.name_search(cr, uid, right, [], operator, limit=None, context=c)] + res_ids = [x[0] for x in relational_model.name_search(cr, uid, right, [], operator, limit=None, context=c)] if operator in NEGATIVE_TERM_OPERATORS: res_ids.append(False) # TODO this should not be appended if False was in 'right' return (left, 'in', res_ids) # resolve string-based m2o criterion into IDs if isinstance(right, basestring) or \ right and isinstance(right, (tuple, list)) and all(isinstance(item, basestring) for item in right): - push(create_substitution_leaf(leaf, _get_expression(relational_table, cr, uid, left, right, operator, context=context), working_table)) + push(create_substitution_leaf(leaf, _get_expression(relational_model, cr, uid, left, right, operator, context=context), working_model)) else: # right == [] or right == False and all other cases are handled by __leaf_to_sql() push_result(leaf) - else: - # other field type - # add the time part to datetime field when it's not there: - if field._type == 'datetime' and right and len(right) == 10: + # ------------------------------------------------- + # OTHER FIELDS + # -> datetime fields: manage time part of the datetime + # field when it is not there + # -> manage translatable fields + # ------------------------------------------------- + else: + if field._type == 'datetime' and right and len(right) == 10: if operator in ('>', '>='): right += ' 00:00:00' elif operator in ('<', '<='): right += ' 23:59:59' - - push(create_substitution_leaf(leaf, (left, operator, right), working_table)) + push(create_substitution_leaf(leaf, (left, operator, right), working_model)) elif field.translate: need_wildcard = operator in ('like', 'ilike', 'not like', 'not ilike') @@ -1001,45 +1016,45 @@ class expression(object): subselect += ' AND value ' + sql_operator + ' ' + " (" + instr + ")" \ ') UNION (' \ ' SELECT id' \ - ' FROM "' + working_table._table + '"' \ + ' FROM "' + working_model._table + '"' \ ' WHERE "' + left + '" ' + sql_operator + ' ' + " (" + instr + "))" else: subselect += ' AND value ' + sql_operator + instr + \ ') UNION (' \ ' SELECT id' \ - ' FROM "' + working_table._table + '"' \ + ' FROM "' + working_model._table + '"' \ ' WHERE "' + left + '" ' + sql_operator + instr + ")" - params = [working_table._name + ',' + left, + params = [working_model._name + ',' + left, context.get('lang', False) or 'en_US', 'model', right, right, ] - push(create_substitution_leaf(leaf, ('id', 'inselect', (subselect, params)), working_table)) + push(create_substitution_leaf(leaf, ('id', 'inselect', (subselect, params)), working_model)) else: push_result(leaf) # ---------------------------------------- # END OF PARSING FULL DOMAIN + # -> generate joins # ---------------------------------------- - # Generate joins joins = set() for leaf in self.result: joins |= set(leaf.get_join_conditions()) self.joins = list(joins) def __leaf_to_sql(self, eleaf): - table = eleaf.table + model = eleaf.model leaf = eleaf.leaf left, operator, right = leaf # final sanity checks - should never fail assert operator in (TERM_OPERATORS + ('inselect',)), \ "Invalid operator %r in domain term %r" % (operator, leaf) - assert leaf in (TRUE_LEAF, FALSE_LEAF) or left in table._all_columns \ + assert leaf in (TRUE_LEAF, FALSE_LEAF) or left in model._all_columns \ or left in MAGIC_COLUMNS, "Invalid field %r in domain term %r" % (left, leaf) table_alias = '"%s"' % (eleaf.generate_alias()) @@ -1079,7 +1094,7 @@ class expression(object): if left == 'id': instr = ','.join(['%s'] * len(params)) else: - instr = ','.join([table._columns[left]._symbol_set[0]] * len(params)) + instr = ','.join([model._columns[left]._symbol_set[0]] * len(params)) query = '(%s."%s" %s (%s))' % (table_alias, left, operator, instr) else: # The case for (left, 'in', []) or (left, 'not in', []). @@ -1094,7 +1109,7 @@ class expression(object): else: # Must not happen raise ValueError("Invalid domain term %r" % (leaf,)) - elif right == False and (left in table._columns) and table._columns[left]._type == "boolean" and (operator == '='): + elif right == False and (left in model._columns) and model._columns[left]._type == "boolean" and (operator == '='): query = '(%s."%s" IS NULL or %s."%s" = false )' % (table_alias, left, table_alias, left) params = [] @@ -1102,7 +1117,7 @@ class expression(object): query = '%s."%s" IS NULL ' % (table_alias, left) params = [] - elif right == False and (left in table._columns) and table._columns[left]._type == "boolean" and (operator == '!='): + elif right == False and (left in model._columns) and model._columns[left]._type == "boolean" and (operator == '!='): query = '(%s."%s" IS NOT NULL and %s."%s" != false)' % (table_alias, left, table_alias, left) params = [] @@ -1117,7 +1132,7 @@ class expression(object): params = [] else: # '=?' behaves like '=' in other cases - query, params = self.__leaf_to_sql((left, '=', right), table) + query, params = self.__leaf_to_sql((left, '=', right), model) elif left == 'id': query = '%s.id %s %%s' % (table_alias, operator) @@ -1127,8 +1142,8 @@ class expression(object): need_wildcard = operator in ('like', 'ilike', 'not like', 'not ilike') sql_operator = {'=like': 'like', '=ilike': 'ilike'}.get(operator, operator) - if left in table._columns: - format = need_wildcard and '%s' or table._columns[left]._symbol_set[0] + if left in model._columns: + format = need_wildcard and '%s' or model._columns[left]._symbol_set[0] if self.has_unaccent and sql_operator in ('ilike', 'not ilike'): query = '(unaccent(%s."%s") %s unaccent(%s))' % (table_alias, left, sql_operator, format) else: @@ -1149,8 +1164,8 @@ class expression(object): str_utf8 = str(right) params = '%%%s%%' % str_utf8 add_null = not str_utf8 - elif left in table._columns: - params = table._columns[left]._symbol_set[1](right) + elif left in model._columns: + params = model._columns[left]._symbol_set[1](right) if add_null: query = '(%s OR %s."%s" IS NULL)' % (query, table_alias, left) From 1ca2c45d418884505462d257b75aebdffbfc3d48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 7 Dec 2012 14:49:46 +0100 Subject: [PATCH 52/80] [IMP] [TESTS] Cleaned and updated tests about order_by with inherits and m2o fields. bzr revid: tde@openerp.com-20121207134946-h0p1em7iq80ux0js --- openerp/addons/base/tests/test_search.py | 35 +++++++++++++++++------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/openerp/addons/base/tests/test_search.py b/openerp/addons/base/tests/test_search.py index a64bfcb3e94..98dfefd4933 100644 --- a/openerp/addons/base/tests/test_search.py +++ b/openerp/addons/base/tests/test_search.py @@ -58,7 +58,7 @@ class test_search(common.TransactionCase): id_desc_active_desc = partners.search(cr, uid, [('name', 'like', 'test_search_order%'), '|', ('active', '=', True), ('active', '=', False)], order="id desc, active desc") self.assertEqual([e, ab, b, a, d, c], id_desc_active_desc, "Search with 'ID DESC, ACTIVE DESC' order failed.") - def test_10_m2order(self): + def test_10_unherits_m2order(self): registry, cr, uid = self.registry, self.cr, self.uid users_obj = registry('res.users') @@ -66,20 +66,35 @@ class test_search(common.TransactionCase): group_employee_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'base', 'group_user') group_employee_id = group_employee_ref and group_employee_ref[1] or False + # Get country/state data + country_us_id = registry('res.country').search(cr, uid, [('code', 'like', 'US')])[0] + state_ids = registry('res.country.state').search(cr, uid, [('country_id', '=', country_us_id)], limit=2) + country_be_id = registry('res.country').search(cr, uid, [('code', 'like', 'BE')])[0] + + # Create test users search_user = users_obj.create(cr, uid, {'name': '__search', 'login': '__search', 'groups_id': [(6, 0, [group_employee_id])]}) - a = users_obj.create(cr, uid, {'name': '__test_A', 'login': '__z_test_A'}) - b = users_obj.create(cr, uid, {'name': '__test_B', 'login': '__a_test_B'}) + a = users_obj.create(cr, uid, {'name': '__test_A', 'login': '__test_A', 'country_id': country_be_id, 'state_id': country_be_id}) + b = users_obj.create(cr, uid, {'name': '__test_B', 'login': '__a_test_B', 'country_id': country_us_id, 'state_id': state_ids[1]}) + c = users_obj.create(cr, uid, {'name': '__test_B', 'login': '__z_test_B', 'country_id': country_us_id, 'state_id': state_ids[0]}) - # Do: search on res.users, order on a field on res.partner, then res.users - print '\n\n' + # Do: search on res.users, order on a field on res.partner to try inherits'd fields, then res.users user_ids = users_obj.search(cr, search_user, [], order='name asc, login desc') - self.assertTrue(set([search_user, a, b]).issubset(set(user_ids)), 'cacaprout') + expected_ids = [search_user, a, c, b] + test_user_ids = filter(lambda x: x in expected_ids, user_ids) + self.assertEqual(test_user_ids, expected_ids, 'search on res_users did not provide expected ids or expected order') - # DO: order on a many2one - print '\n\n' + # Do: order on many2one and inherits'd fields user_ids = users_obj.search(cr, search_user, [], order='state_id asc, country_id desc, name asc, login desc') - print user_ids[0:25] - # self.assertTrue(set([search_user, a, b]).issubset(set(user_ids)), 'cacaprout') + expected_ids = [c, b, a, search_user] + test_user_ids = filter(lambda x: x in expected_ids, user_ids) + self.assertEqual(test_user_ids, expected_ids, 'search on res_users did not provide expected ids or expected order') + + # Do: order on many2one and inherits'd fields + user_ids = users_obj.search(cr, search_user, [], order='country_id desc, state_id desc, name asc, login desc') + expected_ids = [search_user, b, c, a] + test_user_ids = filter(lambda x: x in expected_ids, user_ids) + self.assertEqual(test_user_ids, expected_ids, 'search on res_users did not provide expected ids or expected order') + if __name__ == '__main__': unittest2.main() From efc6e78d25e71fdb90dc8a0e3fd03827b72f8603 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 7 Dec 2012 15:29:31 +0100 Subject: [PATCH 53/80] [IMP] [MOV] Moved test_osv and test_translate from unchecked test directory to tests, to be now effectively run. Updated test_osv to matche the new alias coding style. bzr revid: tde@openerp.com-20121207142931-6ll5yduitdk3ijes --- openerp/test/__init__.py | 25 ------------------- openerp/tests/__init__.py | 7 ++++-- openerp/{test => tests}/test_osv.py | 30 +++++++++++------------ openerp/{test => tests}/test_translate.py | 0 4 files changed, 20 insertions(+), 42 deletions(-) delete mode 100644 openerp/test/__init__.py rename openerp/{test => tests}/test_osv.py (62%) rename openerp/{test => tests}/test_translate.py (100%) diff --git a/openerp/test/__init__.py b/openerp/test/__init__.py deleted file mode 100644 index 7a6c167d85b..00000000000 --- a/openerp/test/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2010 OpenERP S.A. http://www.openerp.com -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from test_osv import * -from test_translate import * - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/tests/__init__.py b/openerp/tests/__init__.py index d036ccfc751..92a40ab306c 100644 --- a/openerp/tests/__init__.py +++ b/openerp/tests/__init__.py @@ -8,9 +8,10 @@ Tests can be explicitely added to the `fast_suite` or `checks` lists or not. See the :ref:`test-framework` section in the :ref:`features` list. """ -from . import test_expression, test_mail, test_ir_sequence, test_orm,\ +from . import test_expression, test_mail, test_ir_sequence, test_orm, \ test_fields, test_basecase, \ - test_view_validation, test_uninstall, test_misc, test_db_cursor + test_view_validation, test_uninstall, test_misc, test_db_cursor, \ + test_osv, test_translate from . import test_ir_filters fast_suite = [ @@ -27,6 +28,8 @@ checks = [ test_basecase, test_view_validation, test_misc, + test_osv, + test_translate, ] # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/test/test_osv.py b/openerp/tests/test_osv.py similarity index 62% rename from openerp/test/test_osv.py rename to openerp/tests/test_osv.py index 2db9cec1cbd..80be8160b61 100644 --- a/openerp/test/test_osv.py +++ b/openerp/tests/test_osv.py @@ -22,45 +22,45 @@ import unittest from openerp.osv.query import Query + class QueryTestCase(unittest.TestCase): def test_basic_query(self): query = Query() - query.tables.extend(['"product_product"','"product_template"']) + query.tables.extend(['"product_product"', '"product_template"']) query.where_clause.append("product_product.template_id = product_template.id") - query.add_join(("product_template", "product_category", "categ_id", "id"), outer=False) # add normal join - query.add_join(("product_product", "res_user", "user_id", "id"), outer=True) # outer join + query.add_join(("product_template", "product_category", "categ_id", "id"), outer=False) # add normal join + query.add_join(("product_product", "res_user", "user_id", "id"), outer=True) # outer join self.assertEquals(query.get_sql()[0].strip(), - """"product_product" LEFT JOIN "res_user" ON ("product_product"."user_id" = "res_user"."id"),"product_template" JOIN "product_category" ON ("product_template"."categ_id" = "product_category"."id") """.strip()) + """"product_product" LEFT JOIN "res_user" as "product_product__user_id" ON ("product_product"."user_id" = "product_product__user_id"."id"),"product_template" JOIN "product_category" as "product_template__categ_id" ON ("product_template"."categ_id" = "product_template__categ_id"."id") """.strip()) self.assertEquals(query.get_sql()[1].strip(), """product_product.template_id = product_template.id""".strip()) def test_query_chained_explicit_joins(self): query = Query() - query.tables.extend(['"product_product"','"product_template"']) + query.tables.extend(['"product_product"', '"product_template"']) query.where_clause.append("product_product.template_id = product_template.id") - query.add_join(("product_template", "product_category", "categ_id", "id"), outer=False) # add normal join - query.add_join(("product_category", "res_user", "user_id", "id"), outer=True) # CHAINED outer join + query.add_join(("product_template", "product_category", "categ_id", "id"), outer=False) # add normal join + query.add_join(("product_template__categ_id", "res_user", "user_id", "id"), outer=True) # CHAINED outer join self.assertEquals(query.get_sql()[0].strip(), - """"product_product","product_template" JOIN "product_category" ON ("product_template"."categ_id" = "product_category"."id") LEFT JOIN "res_user" ON ("product_category"."user_id" = "res_user"."id")""".strip()) + """"product_product","product_template" JOIN "product_category" as "product_template__categ_id" ON ("product_template"."categ_id" = "product_template__categ_id"."id") LEFT JOIN "res_user" as "product_template__categ_id__user_id" ON ("product_template__categ_id"."user_id" = "product_template__categ_id__user_id"."id")""".strip()) self.assertEquals(query.get_sql()[1].strip(), """product_product.template_id = product_template.id""".strip()) def test_mixed_query_chained_explicit_implicit_joins(self): query = Query() - query.tables.extend(['"product_product"','"product_template"']) + query.tables.extend(['"product_product"', '"product_template"']) query.where_clause.append("product_product.template_id = product_template.id") - query.add_join(("product_template", "product_category", "categ_id", "id"), outer=False) # add normal join - query.add_join(("product_category", "res_user", "user_id", "id"), outer=True) # CHAINED outer join + query.add_join(("product_template", "product_category", "categ_id", "id"), outer=False) # add normal join + query.add_join(("product_template__categ_id", "res_user", "user_id", "id"), outer=True) # CHAINED outer join query.tables.append('"account.account"') - query.where_clause.append("product_category.expense_account_id = account_account.id") # additional implicit join + query.where_clause.append("product_category.expense_account_id = account_account.id") # additional implicit join self.assertEquals(query.get_sql()[0].strip(), - """"product_product","product_template" JOIN "product_category" ON ("product_template"."categ_id" = "product_category"."id") LEFT JOIN "res_user" ON ("product_category"."user_id" = "res_user"."id"),"account.account" """.strip()) + """"product_product","product_template" JOIN "product_category" as "product_template__categ_id" ON ("product_template"."categ_id" = "product_template__categ_id"."id") LEFT JOIN "res_user" as "product_template__categ_id__user_id" ON ("product_template__categ_id"."user_id" = "product_template__categ_id__user_id"."id"),"account.account" """.strip()) self.assertEquals(query.get_sql()[1].strip(), """product_product.template_id = product_template.id AND product_category.expense_account_id = account_account.id""".strip()) - def test_raise_missing_lhs(self): query = Query() query.tables.append('"product_product"') - self.assertRaises(AssertionError, query.join, ("product_template", "product_category", "categ_id", "id"), outer=False) + self.assertRaises(AssertionError, query.add_join, ("product_template", "product_category", "categ_id", "id"), outer=False) # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/test/test_translate.py b/openerp/tests/test_translate.py similarity index 100% rename from openerp/test/test_translate.py rename to openerp/tests/test_translate.py From 50cf6d4008f61eff4f7ed17e46a21dcfe337099b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 7 Dec 2012 16:42:04 +0100 Subject: [PATCH 54/80] [CLEAN] Query: cleaned a bit the code. All joins now goes through the same method, either implicit or explicit. Will have to be upgraded in future versions, but at least this is a bit centralized. Updated ORM accordingly. Updated tests. Added a get_alias_from_query method in expression that find the table and the alias from a 'full alias' statement. bzr revid: tde@openerp.com-20121207154204-mx036lpj3vdclu77 --- openerp/osv/expression.py | 14 ++++ openerp/osv/orm.py | 19 +----- openerp/osv/query.py | 140 ++++++++------------------------------ openerp/tests/test_osv.py | 14 ++-- 4 files changed, 50 insertions(+), 137 deletions(-) diff --git a/openerp/osv/expression.py b/openerp/osv/expression.py index 703b7503387..8535213b4de 100644 --- a/openerp/osv/expression.py +++ b/openerp/osv/expression.py @@ -346,6 +346,20 @@ def generate_table_alias(src_table_alias, joined_tables=[]): return ('%s' % alias, '%s as %s' % (_quote(joined_tables[-1][0]), _quote(alias))) +def get_alias_from_query(from_query): + """ :param string from_query: is something like : + - '"res_partner"' OR + - '"res_partner" as "res_users__partner_id"'' + :param tuple result: (unquoted table name, unquoted alias) + i.e. (res_partners, res_partner) OR (res_partner, res_users__partner_id) + """ + from_splitted = from_query.split(' as ') + if len(from_splitted) > 1: + return (from_splitted[0].replace('"', ''), from_splitted[1].replace('"', '')) + else: + return (from_splitted[0].replace('"', ''), from_splitted[0].replace('"', '')) + + def normalize_leaf(element): """ Change a term's operator to some canonical form, simplifying later processing. """ diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index d2c0a0f5682..30746bdd7fc 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -2728,14 +2728,8 @@ class BaseModel(object): """ inherits_field = current_model._inherits[parent_model_name] parent_model = self.pool.get(parent_model_name) - parent_alias = query.add_implicit_join((current_model, parent_model, inherits_field, 'id', inherits_field)) + parent_alias, parent_alias_statement = query.add_join((current_model._table, parent_model._table, inherits_field, 'id', inherits_field), implicit=True) return parent_alias - # table_alias = expression.generate_table_alias(current_model, [(parent_model, inherits_field)]) - # print '\t... _inherits_join_add trying to add %s in %s' % (table_alias, query.tables) - # # query.add_table(table_alias) - # if table_alias not in query.tables: - # query.tables.append(table_alias) - # query.where_clause.append('("%s".%s = %s.id)' % (current_model._table, inherits_field, table_alias)) def _inherits_join_calc(self, field, query): """ @@ -2746,7 +2740,6 @@ class BaseModel(object): :param query: query object on which the JOIN should be added :return: qualified name of field, to be used in SELECT clause """ - print '\t--> _inherits_join_calc' current_table = self parent_alias = current_table._table while field in current_table._inherit_fields and not field in current_table._columns: @@ -4667,15 +4660,12 @@ class BaseModel(object): :param model child_object: model object, base of the rule application """ if added_clause: - print '--> _apply_ir_rules.apply_rule,', added_clause, added_params, added_tables, parent_model, child_object if parent_model and child_object: - print '\t... calling inherits-Join_add with parent_model %s, child_object %s' % (parent_model, child_object) # as inherited rules are being applied, we need to add the missing JOIN # to reach the parent table (if it was not JOINed yet in the query) parent_alias = child_object._inherits_join_add(child_object, parent_model, query) # inherited rules are applied on the external table -> need to get the alias and replace parent_table = self.pool.get(parent_model)._table - print parent_table, parent_alias added_clause = [clause.replace('"%s"' % parent_table, '"%s"' % parent_alias) for clause in added_clause] # not sure of myself here (in the ORM, this statment is quite cool) new_tables = [] @@ -4686,11 +4676,9 @@ class BaseModel(object): new_table = table.replace('"%s"' % parent_table, '"%s"' % parent_alias) new_tables.append(new_table) added_tables = new_tables - print '\t... adding tables %s, clause %s (params %s)' % (added_tables, added_clause, added_params) query.where_clause += added_clause query.where_clause_params += added_params for table in added_tables: - print '\t... adding table %s in %s' % (table, query.tables) if table not in query.tables: query.tables.append(table) return True @@ -4715,7 +4703,6 @@ class BaseModel(object): :return: the qualified field name to use in an ORDER BY clause to sort by ``order_field`` """ - print '_generate_m2o_order_by' if order_field not in self._columns and order_field in self._inherit_fields: # also add missing joins for reaching the table containing the m2o field qualified_field = self._inherits_join_calc(order_field, query) @@ -4747,7 +4734,7 @@ class BaseModel(object): # Join the dest m2o table if it's not joined yet. We use [LEFT] OUTER join here # as we don't want to exclude results that have NULL values for the m2o src_table, src_field = qualified_field.replace('"', '').split('.', 1) - dst_alias = query.add_join((src_table, dest_model._table, src_field, 'id'), outer=True) + dst_alias, dst_alias_statement = query.add_join((src_table, dest_model._table, src_field, 'id', src_field), implicit=False, outer=True) qualify = lambda field: '"%s"."%s"' % (dst_alias, field) return map(qualify, m2o_order) if isinstance(m2o_order, list) else qualify(m2o_order) @@ -4769,7 +4756,6 @@ class BaseModel(object): return order_list order_by_clause = ','.join(_split_order(self._order, self._table)) if order_spec: - print '-->_generate_order_by beginning' order_by_elements = [] self._check_qorder(order_spec) for order_part in order_spec.split(','): @@ -4804,7 +4790,6 @@ class BaseModel(object): order_by_elements.append("%s %s" % (inner_clause, order_direction)) if order_by_elements: order_by_clause = ",".join(order_by_elements) - print '-->_generate_order_by ending' return order_by_clause and (' ORDER BY %s ' % order_by_clause) or '' diff --git a/openerp/osv/query.py b/openerp/osv/query.py index a07def36a31..7f1bd930c91 100644 --- a/openerp/osv/query.py +++ b/openerp/osv/query.py @@ -53,9 +53,6 @@ class Query(object): # holds the list of tables joined using default JOIN. # the table names are stored double-quoted (backwards compatibility) self.tables = tables or [] - # holds a mapping of table aliases: - # self._table_alias_mapping = {'alias_1': 'table_name'} - self._table_alias_mapping = {} # holds the list of WHERE clause elements, to be joined with # 'AND' when generating the final query @@ -79,29 +76,19 @@ class Query(object): # LEFT JOIN "table_c" ON ("table_a"."table_a_col2" = "table_c"."table_c_col") self.joins = joins or {} - def _add_table_alias(self, table_alias): - pass - def _get_table_aliases(self): - aliases = [] - for table in self.tables: - if len(table.split(' as ')) > 1: - aliases.append(table.split(' as ')[1].replace('"', '')) - else: - aliases.append(table.replace('"', '')) - # print '--', aliases - return aliases + from openerp.osv.expression import get_alias_from_query + return [get_alias_from_query(from_statement)[1] for from_statement in self.tables] def _get_alias_mapping(self): + from openerp.osv.expression import get_alias_from_query mapping = {} - aliases = self._get_table_aliases() - for alias in aliases: - for table in self.tables: - if '"%s"' % (alias) in table: - mapping.setdefault(alias, table) + for table in self.tables: + alias, statement = get_alias_from_query(table) + mapping[statement] = table return mapping - def add_new_join(self, connection, implicit=True, outer=False): + def add_join(self, connection, implicit=True, outer=False): """ Join a destination table to the current table. :param implicit: False if the join is an explicit join. This allows @@ -127,115 +114,41 @@ class Query(object): """ from openerp.osv.expression import generate_table_alias (lhs, table, lhs_col, col, link) = connection - alias, alias_statement = generate_table_alias(lhs._table, [(table._table, link)]) + alias, alias_statement = generate_table_alias(lhs, [(table, link)]) if implicit: - print '\t\t... Query: trying to add %s in %s (received %s)' % (alias_statement, self.tables, connection) if alias_statement not in self.tables: self.tables.append(alias_statement) - condition = '("%s"."%s" = "%s"."%s")' % (lhs._table, lhs_col, alias, col) - print '\t\t... added %s' % (condition) + condition = '("%s"."%s" = "%s"."%s")' % (lhs, lhs_col, alias, col) + # print '\t\t... Query: added %s in %s (received %s)' % (alias_statement, self.tables, connection) self.where_clause.append(condition) - return alias + else: + # print '\t\t... Query: not added %s in %s (received %s)' % (alias_statement, self.tables, connection) + # already joined + pass + return alias, alias_statement else: - (lhs, table, lhs_col, col) = connection - lhs = _quote(lhs) - table = _quote(table) - print connection - aliases = [] - for table in self.tables: - if len(table.split(' as ')) > 1: - aliases.append(table.split(' as ')[1]) - else: - aliases.append(table) - print '--', aliases - aliases = [table.split(' as ') for table in self.tables] - assert lhs in self.aliases, "Left-hand-side table %s must already be part of the query tables %s!" % (lhs, str(self.tables)) - if table in self.tables: + aliases = self._get_table_aliases() + assert lhs in aliases, "Left-hand-side table %s must already be part of the query tables %s!" % (lhs, str(self.tables)) + if alias_statement in self.tables: # already joined, must ignore (promotion to outer and multiple joins not supported yet) + # print '\t\t... Query: not added %s in %s (received %s)' % (alias_statement, self.tables, connection) pass else: # add JOIN - self.tables.append(table) - self.joins.setdefault(lhs, []).append((table, lhs_col, col, outer and 'LEFT JOIN' or 'JOIN')) - return self - - def add_implicit_join(self, connection): - """ Adds an implicit join. This means that left-hand table is added to the - Query.tables (adding a table in the from clause), and that a join - condition is added in Query.where_clause. - - Implicit joins use expression.generate_table_alias to generate the - alias the the joined table. - - :param connection: a tuple``(lhs, table, lhs_col, col, link)`` Please - refer to expression.py for more details about joins. - """ - from openerp.osv.expression import generate_table_alias - (lhs, table, lhs_col, col, link) = connection - alias, alias_statement = generate_table_alias(lhs._table, [(table._table, link)]) - print '\t\t... Query: trying to add %s in %s (received %s)' % (alias_statement, self.tables, connection) - if alias_statement not in self.tables: - self.tables.append(alias_statement) - condition = '("%s"."%s" = "%s"."%s")' % (lhs._table, lhs_col, alias, col) - print '\t\t... added %s' % (condition) - self.where_clause.append(condition) - return alias - - def add_join(self, connection, outer=False): - """Adds the JOIN specified in ``connection``. - - :param connection: a tuple ``(lhs, table, lhs_col, col)``. - The join corresponds to the SQL equivalent of:: - - (lhs.lhs_col = table.col) - - Note that all connection elements are strings. - - :param outer: True if a LEFT OUTER JOIN should be used, if possible - (no promotion to OUTER JOIN is supported in case the JOIN - was already present in the query, as for the moment - implicit INNER JOINs are only connected from NON-NULL - columns so it would not be correct (e.g. for - ``_inherits`` or when a domain criterion explicitly - adds filtering) - """ - from openerp.osv.expression import generate_table_alias - (lhs, table, lhs_col, col) = connection - # lhs = _quote(lhs) - # table = _quote(table) - print '\t\t... Query.add_join(): adding connection %s' % str(connection) - - aliases = self._get_table_aliases() - - assert lhs in aliases, "Left-hand-side table %s must already be part of the query tables %s!" % (lhs, str(self.tables)) - - rhs, rhs_statement = generate_table_alias(lhs, [(connection[1], connection[2])]) - print rhs, rhs_statement - - if rhs_statement in self.tables: - # already joined, must ignore (promotion to outer and multiple joins not supported yet) - pass - else: - # add JOIN - self.tables.append(rhs_statement) - self.joins.setdefault(lhs, []).append((rhs, lhs_col, col, outer and 'LEFT JOIN' or 'JOIN')) - return rhs + self.tables.append(alias_statement) + self.joins.setdefault(lhs, []).append((alias, lhs_col, col, outer and 'LEFT JOIN' or 'JOIN')) + return alias, alias_statement def get_sql(self): - """Returns (query_from, query_where, query_params)""" + """ Returns (query_from, query_where, query_params). """ + from openerp.osv.expression import get_alias_from_query query_from = '' tables_to_process = list(self.tables) - alias_mapping = self._get_alias_mapping() - # print 'tables_to_process %s' % (tables_to_process) - # print 'self.joins %s' % (self.joins) - # print 'alias_mapping %s' % (alias_mapping) - def add_joins_for_table(table, query_from): for (dest_table, lhs_col, col, join) in self.joins.get(table, []): - # print dest_table tables_to_process.remove(alias_mapping[dest_table]) query_from += ' %s %s ON ("%s"."%s" = "%s"."%s")' % \ (join, alias_mapping[dest_table], table, lhs_col, dest_table, col) @@ -244,8 +157,9 @@ class Query(object): for table in tables_to_process: query_from += table - if _get_alias_from_statement(table) in self.joins: - query_from = add_joins_for_table(_get_alias_from_statement(table), query_from) + table_alias = get_alias_from_query(table)[1] + if table_alias in self.joins: + query_from = add_joins_for_table(table_alias, query_from) query_from += ',' query_from = query_from[:-1] # drop last comma return (query_from, " AND ".join(self.where_clause), self.where_clause_params) diff --git a/openerp/tests/test_osv.py b/openerp/tests/test_osv.py index 80be8160b61..e36495fc7c6 100644 --- a/openerp/tests/test_osv.py +++ b/openerp/tests/test_osv.py @@ -29,8 +29,8 @@ class QueryTestCase(unittest.TestCase): query = Query() query.tables.extend(['"product_product"', '"product_template"']) query.where_clause.append("product_product.template_id = product_template.id") - query.add_join(("product_template", "product_category", "categ_id", "id"), outer=False) # add normal join - query.add_join(("product_product", "res_user", "user_id", "id"), outer=True) # outer join + query.add_join(("product_template", "product_category", "categ_id", "id", "categ_id"), implicit=False, outer=False) # add normal join + query.add_join(("product_product", "res_user", "user_id", "id", "user_id"), implicit=False, outer=True) # outer join self.assertEquals(query.get_sql()[0].strip(), """"product_product" LEFT JOIN "res_user" as "product_product__user_id" ON ("product_product"."user_id" = "product_product__user_id"."id"),"product_template" JOIN "product_category" as "product_template__categ_id" ON ("product_template"."categ_id" = "product_template__categ_id"."id") """.strip()) self.assertEquals(query.get_sql()[1].strip(), """product_product.template_id = product_template.id""".strip()) @@ -39,8 +39,8 @@ class QueryTestCase(unittest.TestCase): query = Query() query.tables.extend(['"product_product"', '"product_template"']) query.where_clause.append("product_product.template_id = product_template.id") - query.add_join(("product_template", "product_category", "categ_id", "id"), outer=False) # add normal join - query.add_join(("product_template__categ_id", "res_user", "user_id", "id"), outer=True) # CHAINED outer join + query.add_join(("product_template", "product_category", "categ_id", "id", "categ_id"), implicit=False, outer=False) # add normal join + query.add_join(("product_template__categ_id", "res_user", "user_id", "id", "user_id"), implicit=False, outer=True) # CHAINED outer join self.assertEquals(query.get_sql()[0].strip(), """"product_product","product_template" JOIN "product_category" as "product_template__categ_id" ON ("product_template"."categ_id" = "product_template__categ_id"."id") LEFT JOIN "res_user" as "product_template__categ_id__user_id" ON ("product_template__categ_id"."user_id" = "product_template__categ_id__user_id"."id")""".strip()) self.assertEquals(query.get_sql()[1].strip(), """product_product.template_id = product_template.id""".strip()) @@ -49,8 +49,8 @@ class QueryTestCase(unittest.TestCase): query = Query() query.tables.extend(['"product_product"', '"product_template"']) query.where_clause.append("product_product.template_id = product_template.id") - query.add_join(("product_template", "product_category", "categ_id", "id"), outer=False) # add normal join - query.add_join(("product_template__categ_id", "res_user", "user_id", "id"), outer=True) # CHAINED outer join + query.add_join(("product_template", "product_category", "categ_id", "id", "categ_id"), implicit=False, outer=False) # add normal join + query.add_join(("product_template__categ_id", "res_user", "user_id", "id", "user_id"), implicit=False, outer=True) # CHAINED outer join query.tables.append('"account.account"') query.where_clause.append("product_category.expense_account_id = account_account.id") # additional implicit join self.assertEquals(query.get_sql()[0].strip(), @@ -60,7 +60,7 @@ class QueryTestCase(unittest.TestCase): def test_raise_missing_lhs(self): query = Query() query.tables.append('"product_product"') - self.assertRaises(AssertionError, query.add_join, ("product_template", "product_category", "categ_id", "id"), outer=False) + self.assertRaises(AssertionError, query.add_join, ("product_template", "product_category", "categ_id", "id", "categ_id"), implicit=False, outer=False) # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: From a786f8406b2a75f0ff5127908722b354361eada6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 7 Dec 2012 17:05:39 +0100 Subject: [PATCH 55/80] [REV] ir_ui_menu: coding-style changes reverted, because not necessary. Only a store=True on needaction_enabled is sufficient. bzr revid: tde@openerp.com-20121207160539-6pqj932lvznxofd4 --- openerp/addons/base/ir/ir_ui_menu.py | 30 +++++++++------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/openerp/addons/base/ir/ir_ui_menu.py b/openerp/addons/base/ir/ir_ui_menu.py index e286466b85c..cd0512fff74 100644 --- a/openerp/addons/base/ir/ir_ui_menu.py +++ b/openerp/addons/base/ir/ir_ui_menu.py @@ -249,7 +249,7 @@ class ir_ui_menu(osv.osv): icon_image = False if icon_path: try: - icon_file = tools.file_open(icon_path, 'rb') + icon_file = tools.file_open(icon_path,'rb') icon_image = base64.encodestring(icon_file.read()) finally: icon_file.close() @@ -265,22 +265,17 @@ class ir_ui_menu(osv.osv): return res - def get_needaction_data(self, cr, uid, ids, field_names, args, context=None): - """ Return for each menu entry of ids : - - if it uses the needaction mechanism (needaction_enabled) - - the needaction counter of the related action, taking into account - the action domain - """ + def _get_needaction(self, cr, uid, ids, field_names, args, context=None): res = {} for menu in self.browse(cr, uid, ids, context=context): res[menu.id] = { 'needaction_enabled': False, 'needaction_counter': False, } - if menu.action and menu.action.type in ('ir.actions.act_window', 'ir.actions.client') and menu.action.res_model: + if menu.action and menu.action.type in ('ir.actions.act_window','ir.actions.client') and menu.action.res_model: obj = self.pool.get(menu.action.res_model) if obj and obj._needaction: - if menu.action.type == 'ir.actions.act_window': + if menu.action.type=='ir.actions.act_window': dom = menu.action.domain and eval(menu.action.domain, {'uid': uid}) or [] else: dom = eval(menu.action.params_store or '{}', {'uid': uid}).get('domain') @@ -291,7 +286,7 @@ class ir_ui_menu(osv.osv): _columns = { 'name': fields.char('Menu', size=64, required=True, translate=True), 'sequence': fields.integer('Sequence'), - 'child_id': fields.one2many('ir.ui.menu', 'parent_id', 'Child IDs'), + 'child_id' : fields.one2many('ir.ui.menu', 'parent_id','Child IDs'), 'parent_id': fields.many2one('ir.ui.menu', 'Parent Menu', select=True), 'groups_id': fields.many2many('res.groups', 'ir_ui_menu_group_rel', 'menu_id', 'gid', 'Groups', help="If you have groups, the visibility of this menu will be based on these groups. "\ @@ -301,18 +296,11 @@ class ir_ui_menu(osv.osv): 'icon': fields.selection(tools.icons, 'Icon', size=64), 'icon_pict': fields.function(_get_icon_pict, type='char', size=32), 'web_icon': fields.char('Web Icon File', size=128), - 'web_icon_hover': fields.char('Web Icon File (hover)', size=128), + 'web_icon_hover':fields.char('Web Icon File (hover)', size=128), 'web_icon_data': fields.function(_get_image_icon, string='Web Icon Image', type='binary', readonly=True, store=True, multi='icon'), - 'web_icon_hover_data': fields.function(_get_image_icon, string='Web Icon Image (hover)', type='binary', readonly=True, store=True, multi='icon'), - 'needaction_enabled': fields.function(get_needaction_data, - type='boolean', multi='get_needaction_data', - store=True, - string='Target model uses the need action mechanism', - help='If the menu entry action is an act_window action, and if this action is related to a model that uses the need_action mechanism, this field is set to true. Otherwise, it is false.'), - 'needaction_counter': fields.function(get_needaction_data, - type='integer', multi='get_needaction_data', - string='Number of actions the user has to perform', - help='If the target model uses the need action mechanism, this field gives the number of actions the current user has to perform.'), + 'web_icon_hover_data':fields.function(_get_image_icon, string='Web Icon Image (hover)', type='binary', readonly=True, store=True, multi='icon'), + 'needaction_enabled': fields.function(_get_needaction, string='Target model uses the need action mechanism', store=True, type='boolean', help='If the menu entry action is an act_window action, and if this action is related to a model that uses the need_action mechanism, this field is set to true. Otherwise, it is false.', multi='_get_needaction'), + 'needaction_counter': fields.function(_get_needaction, string='Number of actions the user has to perform', type='integer', help='If the target model uses the need action mechanism, this field gives the number of actions the current user has to perform.', multi='_get_needaction'), 'action': fields.function(_action, fnct_inv=_action_inv, type='reference', string='Action', selection=[ From 9381a827158e085b035b8f497467f09ff5f6a5e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 7 Dec 2012 17:09:33 +0100 Subject: [PATCH 56/80] [IMP] Added an assert on the alias length. bzr revid: tde@openerp.com-20121207160933-1ivbkkv6tgg5a7x3 --- openerp/osv/expression.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openerp/osv/expression.py b/openerp/osv/expression.py index 8535213b4de..e14e013d536 100644 --- a/openerp/osv/expression.py +++ b/openerp/osv/expression.py @@ -343,6 +343,7 @@ def generate_table_alias(src_table_alias, joined_tables=[]): return ('%s' % alias, '%s' % _quote(alias)) for link in joined_tables: alias += '__' + link[1] + assert len(alias) < 64, 'Table alias name %s is longer than the 64 characters size accepted by default in postgresql.' % (alias) return ('%s' % alias, '%s as %s' % (_quote(joined_tables[-1][0]), _quote(alias))) From bb598cd1052709c4bee4a31405201c4609ff6377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 7 Dec 2012 17:20:19 +0100 Subject: [PATCH 57/80] [CLEAN] orm: added quote around a forgottent table name; cleaned a bit some code and added comments, removed dead code. bzr revid: tde@openerp.com-20121207162019-ec1x7a38vw2rrqze --- openerp/osv/orm.py | 11 ++++++----- openerp/osv/query.py | 8 -------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 54e349b5234..0a28052e51d 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -2741,7 +2741,7 @@ class BaseModel(object): :return: qualified name of field, to be used in SELECT clause """ current_table = self - parent_alias = current_table._table + parent_alias = '"%s"' % current_table._table while field in current_table._inherit_fields and not field in current_table._columns: parent_model_name = current_table._inherit_fields[field][0] parent_table = self.pool.get(parent_model_name) @@ -4667,14 +4667,15 @@ class BaseModel(object): # inherited rules are applied on the external table -> need to get the alias and replace parent_table = self.pool.get(parent_model)._table added_clause = [clause.replace('"%s"' % parent_table, '"%s"' % parent_alias) for clause in added_clause] - # not sure of myself here (in the ORM, this statment is quite cool) + # change references to parent_table to parent_alias, because we now use the alias to refer to the table new_tables = [] for table in added_tables: + # table is just a table name -> switch to the full alias if table == '"%s"' % (parent_table): - new_table = '"%s" as "%s"' % (parent_table, parent_alias) + new_tables.append('"%s" as "%s"' % (parent_table, parent_alias)) + # table is already a full statement -> replace reference to the table to its alias, is correct with the way aliases are generated else: - new_table = table.replace('"%s"' % parent_table, '"%s"' % parent_alias) - new_tables.append(new_table) + new_tables.append(table.replace('"%s"' % parent_table, '"%s"' % parent_alias)) added_tables = new_tables query.where_clause += added_clause query.where_clause_params += added_params diff --git a/openerp/osv/query.py b/openerp/osv/query.py index 7f1bd930c91..98a90dcd0bd 100644 --- a/openerp/osv/query.py +++ b/openerp/osv/query.py @@ -28,14 +28,6 @@ def _quote(to_quote): return to_quote -def _get_alias_from_statement(string): - if len(string.split(' as ')) > 1: - alias = string.split(' as ')[1].replace('"', '') - else: - alias = string.replace('"', '') - return alias - - class Query(object): """ Dumb implementation of a Query object, using 3 string lists so far From a4083348d5f571dfb7ffcdd5b389c757b4998cf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Mon, 10 Dec 2012 09:37:18 +0100 Subject: [PATCH 58/80] =?UTF-8?q?[FIX]=20Portal:=20portal=20inbox=20domain?= =?UTF-8?q?=20fixed,=20favorite=5Fuser=5Fids=20does=20not=20exist=20anymor?= =?UTF-8?q?e.=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bzr revid: tde@openerp.com-20121210083718-idod6rjgaasz001y --- addons/portal/portal_view.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/addons/portal/portal_view.xml b/addons/portal/portal_view.xml index b7765e63ffd..4aad43716b6 100644 --- a/addons/portal/portal_view.xml +++ b/addons/portal/portal_view.xml @@ -57,9 +57,8 @@ } Date: Mon, 10 Dec 2012 11:01:37 +0100 Subject: [PATCH 60/80] [FIX] don't lose debug mode when clicking on logo bzr revid: xmo@openerp.com-20121210100137-h2ydpz6b63c48b1z --- addons/web/static/src/xml/base.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 2161bc84f3d..e742f336dc0 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -436,7 +436,8 @@ - + +
\n" " " msgstr "" +"
\n" +"

\n" +" Die Personalwesen Anzeigetafel ist noch ohne " +"Eintrag.\n" +"

\n" +" Zur Erstellung der ersten Statistik oder Liste für diese " +"Anzeigetafel,\n" +" gehen Sie zu einem Menü Ihrer Wahl, wechseln dort zur " +"Listen oder Grafik Ansicht\n" +" und klicken dort auf 'Hinzufügen zur " +"Anzeigetafel' in den erweiterten Suchoptionen.\n" +"

\n" +" Vorab können Sie die Filter und Gruppierung anwenden, " +"bevor Sie diese\n" +" Ansicht zur Anzeigetafel hinzufügen.\n" +"

\n" +"
\n" +" " #. module: hr #: view:hr.employee:0 @@ -946,7 +1033,7 @@ msgstr "Der Name der Jobbezeichnung muss je Unternehmen eindeutig sein" #. module: hr #: help:hr.config.settings,module_hr_expense:0 msgid "This installs the module hr_expense." -msgstr "" +msgstr "Durch aktivieren installieren Sie das Modul hr_expense." #. module: hr #: model:ir.model,name:hr.model_hr_config_settings @@ -973,7 +1060,7 @@ msgstr "Unterstellte Mitarbeiter" #. module: hr #: view:hr.config.settings:0 msgid "Apply" -msgstr "" +msgstr "Anwenden" #~ msgid "Working Time Categories" #~ msgstr "Arbeitszeiten Kategorien" diff --git a/addons/hr_attendance/i18n/de.po b/addons/hr_attendance/i18n/de.po index ec36120b42d..620c5fd7d61 100644 --- a/addons/hr_attendance/i18n/de.po +++ b/addons/hr_attendance/i18n/de.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: OpenERP Server 6.0dev\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-12-03 16:03+0000\n" -"PO-Revision-Date: 2012-05-10 18:09+0000\n" -"Last-Translator: Ferdinand-camptocamp \n" +"PO-Revision-Date: 2012-12-10 14:58+0000\n" +"Last-Translator: Thorsten Vocks (OpenBig.org) \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:41+0000\n" -"X-Generator: Launchpad (build 16335)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:48+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: hr_attendance #: model:ir.model,name:hr_attendance.model_hr_attendance_month @@ -29,7 +30,7 @@ msgstr "Suche Anwesenheitszeiten" #. module: hr_attendance #: field:hr.employee,last_sign:0 msgid "Last Sign" -msgstr "" +msgstr "Letzte Anmeldung" #. module: hr_attendance #: view:hr.attendance:0 @@ -43,12 +44,13 @@ msgstr "Anwesenheitszeit" #: code:addons/hr_attendance/static/src/js/attendance.js:34 #, python-format msgid "Last sign in: %s,
%s.
Click to sign out." -msgstr "" +msgstr "Letzte Anmeldung: %s,
%s.
Klicken Sie zur Abmeldung." #. module: hr_attendance #: constraint:hr.attendance:0 msgid "Error ! Sign in (resp. Sign out) must follow Sign out (resp. Sign in)" msgstr "" +"Fehler ! Anmelden (bzw. Abmelden) muss immer Abmelden folgen (bzw. Anmelden)" #. module: hr_attendance #: help:hr.action.reason,name:0 @@ -72,7 +74,7 @@ msgstr "Druck Anwesenheitszeit des Monats" #: code:addons/hr_attendance/report/timesheet.py:122 #, python-format msgid "Attendances by Week" -msgstr "" +msgstr "Anwesenheitszeiten der Woche" #. module: hr_attendance #: selection:hr.action.reason,action_type:0 @@ -106,7 +108,7 @@ msgstr "Abmeldung" #: code:addons/hr_attendance/wizard/hr_attendance_error.py:49 #, python-format msgid "No records are found for your selection!" -msgstr "" +msgstr "Es wurden keine Werte für diese Auswahl gefunden." #. module: hr_attendance #: view:hr.attendance.error:0 @@ -172,12 +174,12 @@ msgstr "Warnung" #. module: hr_attendance #: help:hr.config.settings,group_hr_attendance:0 msgid "Allocates attendance group to all users." -msgstr "" +msgstr "Zuweisen der Berechtigung zur Arbeitszeiteingabe für alle Benutzer" #. module: hr_attendance #: field:hr.employee,attendance_access:0 msgid "unknown" -msgstr "" +msgstr "unbekannt" #. module: hr_attendance #: view:hr.attendance:0 @@ -193,7 +195,7 @@ msgstr "Juni" #: code:addons/hr_attendance/report/attendance_by_month.py:193 #, python-format msgid "Attendances by Month" -msgstr "" +msgstr "Anwesenheitszeiten des Monats" #. module: hr_attendance #: model:ir.actions.act_window,name:hr_attendance.action_hr_attendance_week @@ -250,7 +252,7 @@ msgstr "Datum" #. module: hr_attendance #: field:hr.config.settings,group_hr_attendance:0 msgid "Track attendances for all employees" -msgstr "" +msgstr "Erfassen der Anwesenheit für alle Mitarbeiter" #. module: hr_attendance #: selection:hr.attendance.month,month:0 @@ -334,7 +336,7 @@ msgstr "Januar" #: code:addons/hr_attendance/wizard/hr_attendance_error.py:49 #, python-format msgid "No Data Available !" -msgstr "" +msgstr "Keine Daten vorhanden !" #. module: hr_attendance #: selection:hr.attendance.month,month:0 @@ -414,7 +416,7 @@ msgstr "" #: code:addons/hr_attendance/static/src/js/attendance.js:36 #, python-format msgid "Click to Sign In at %s." -msgstr "" +msgstr "Klicken zur Anmeldung als %s." #. module: hr_attendance #: field:hr.action.reason,action_type:0 @@ -433,6 +435,10 @@ msgid "" "You tried to %s with a date anterior to another event !\n" "Try to contact the HR Manager to correct attendances." msgstr "" +"Sie haben %s versucht mit einem Datum, was vor dem unmittelbar vorherigen " +"Ereignis\n" +"liegt. Kontaktieren Sie den verantwortlichen Mitarbeiter, um eine Korrektur " +"vorzunehmen." #. module: hr_attendance #: selection:hr.attendance.month,month:0 @@ -464,7 +470,7 @@ msgstr "" #: view:hr.attendance.month:0 #: view:hr.attendance.week:0 msgid "or" -msgstr "" +msgstr "oder" #. module: hr_attendance #: help:hr.attendance,action_desc:0 diff --git a/addons/hr_contract/i18n/de.po b/addons/hr_contract/i18n/de.po index 40008794882..6a642cdbc2d 100644 --- a/addons/hr_contract/i18n/de.po +++ b/addons/hr_contract/i18n/de.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: OpenERP Server 6.0dev\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-12-03 16:03+0000\n" -"PO-Revision-Date: 2012-01-13 18:59+0000\n" -"Last-Translator: Ferdinand @ Camptocamp \n" +"PO-Revision-Date: 2012-12-10 15:13+0000\n" +"Last-Translator: Thorsten Vocks (OpenBig.org) \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:37+0000\n" -"X-Generator: Launchpad (build 16335)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:48+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: hr_contract #: field:hr.contract,wage:0 @@ -102,7 +103,7 @@ msgstr "Vertragstypen" #. module: hr_contract #: view:hr.employee:0 msgid "Medical Exam" -msgstr "" +msgstr "Medizin Examen" #. module: hr_contract #: field:hr.contract,date_end:0 @@ -162,7 +163,7 @@ msgstr "Arbeitszeiten" #. module: hr_contract #: view:hr.contract:0 msgid "Salary and Advantages" -msgstr "" +msgstr "Gehalt und sonstige Bezüge" #. module: hr_contract #: field:hr.contract,job_id:0 @@ -173,6 +174,8 @@ msgstr "Arbeitsstelle" #: constraint:hr.contract:0 msgid "Error! Contract start-date must be less than contract end-date." msgstr "" +"Fehler ! Das Start Datum des Vertrags muss vor dem Ende Datum des Vertrags " +"liegen." #. module: hr_contract #: field:hr.employee,manager:0 @@ -192,7 +195,7 @@ msgstr "Visa Nr." #. module: hr_contract #: field:hr.employee,vehicle_distance:0 msgid "Home-Work Dist." -msgstr "" +msgstr "Entfernung Wohnung-Arbeit" #. module: hr_contract #: field:hr.employee,place_of_birth:0 @@ -202,7 +205,7 @@ msgstr "Geburtsort" #. module: hr_contract #: view:hr.contract:0 msgid "Trial Period Duration" -msgstr "" +msgstr "Probezeit" #. module: hr_contract #: view:hr.contract:0 diff --git a/addons/hr_contract/i18n/es.po b/addons/hr_contract/i18n/es.po index 3afe40a215b..720b69787bb 100644 --- a/addons/hr_contract/i18n/es.po +++ b/addons/hr_contract/i18n/es.po @@ -7,14 +7,14 @@ msgstr "" "Project-Id-Version: OpenERP Server 6.0dev\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-12-03 16:03+0000\n" -"PO-Revision-Date: 2011-01-13 14:41+0000\n" -"Last-Translator: Borja López Soilán (NeoPolus) \n" +"PO-Revision-Date: 2012-12-10 21:44+0000\n" +"Last-Translator: lambdasoftware \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:37+0000\n" -"X-Generator: Launchpad (build 16335)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:48+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: hr_contract #: field:hr.contract,wage:0 @@ -102,7 +102,7 @@ msgstr "Tipos de contrato" #. module: hr_contract #: view:hr.employee:0 msgid "Medical Exam" -msgstr "" +msgstr "Examen médico" #. module: hr_contract #: field:hr.contract,date_end:0 @@ -173,6 +173,7 @@ msgstr "Título del trabajo" #: constraint:hr.contract:0 msgid "Error! Contract start-date must be less than contract end-date." msgstr "" +"¡Error! la fecha de inicio de contrato debe ser anterior a la de fin." #. module: hr_contract #: field:hr.employee,manager:0 @@ -202,7 +203,7 @@ msgstr "Lugar de nacimiento" #. module: hr_contract #: view:hr.contract:0 msgid "Trial Period Duration" -msgstr "" +msgstr "Duración del periodo de prueba" #. module: hr_contract #: view:hr.contract:0 diff --git a/addons/hr_contract/i18n/it.po b/addons/hr_contract/i18n/it.po index cd461fd2219..47d45602f55 100644 --- a/addons/hr_contract/i18n/it.po +++ b/addons/hr_contract/i18n/it.po @@ -7,14 +7,14 @@ msgstr "" "Project-Id-Version: OpenERP Server 5.0.4\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-12-03 16:03+0000\n" -"PO-Revision-Date: 2011-01-13 15:08+0000\n" -"Last-Translator: Nicola Riolini - Micronaet \n" +"PO-Revision-Date: 2012-12-10 20:55+0000\n" +"Last-Translator: Sergio Corato \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:37+0000\n" -"X-Generator: Launchpad (build 16335)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:48+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: hr_contract #: field:hr.contract,wage:0 @@ -102,7 +102,7 @@ msgstr "Tipi contratto" #. module: hr_contract #: view:hr.employee:0 msgid "Medical Exam" -msgstr "" +msgstr "Esame Medico" #. module: hr_contract #: field:hr.contract,date_end:0 @@ -112,7 +112,7 @@ msgstr "Data di fine" #. module: hr_contract #: help:hr.contract,wage:0 msgid "Basic Salary of the employee" -msgstr "" +msgstr "Salario Base del dipendente" #. module: hr_contract #: field:hr.contract,name:0 @@ -133,7 +133,7 @@ msgstr "Note" #. module: hr_contract #: field:hr.contract,permit_no:0 msgid "Work Permit No" -msgstr "" +msgstr "Permesso di Lavoro N." #. module: hr_contract #: view:hr.contract:0 @@ -162,7 +162,7 @@ msgstr "Programma di lavoro" #. module: hr_contract #: view:hr.contract:0 msgid "Salary and Advantages" -msgstr "" +msgstr "Stipendio e Benefit" #. module: hr_contract #: field:hr.contract,job_id:0 @@ -173,6 +173,8 @@ msgstr "Qualifica" #: constraint:hr.contract:0 msgid "Error! Contract start-date must be less than contract end-date." msgstr "" +"Errore! La data di inizio del Contratto deve essere inferiore di quella " +"finale." #. module: hr_contract #: field:hr.employee,manager:0 @@ -187,12 +189,12 @@ msgstr "Data di inizio" #. module: hr_contract #: field:hr.contract,visa_no:0 msgid "Visa No" -msgstr "" +msgstr "N. VISA" #. module: hr_contract #: field:hr.employee,vehicle_distance:0 msgid "Home-Work Dist." -msgstr "" +msgstr "Distanza Casa-Lavoro" #. module: hr_contract #: field:hr.employee,place_of_birth:0 @@ -202,7 +204,7 @@ msgstr "Luogo di Nascita" #. module: hr_contract #: view:hr.contract:0 msgid "Trial Period Duration" -msgstr "" +msgstr "Durata Periodo di Prova" #. module: hr_contract #: view:hr.contract:0 @@ -212,7 +214,7 @@ msgstr "Durata" #. module: hr_contract #: field:hr.contract,visa_expire:0 msgid "Visa Expire Date" -msgstr "" +msgstr "Data scadenza VISA" #. module: hr_contract #: field:hr.employee,medic_exam:0 diff --git a/addons/hr_evaluation/i18n/de.po b/addons/hr_evaluation/i18n/de.po index 484d5cdfa4d..ad8e5938566 100644 --- a/addons/hr_evaluation/i18n/de.po +++ b/addons/hr_evaluation/i18n/de.po @@ -8,14 +8,15 @@ msgstr "" "Project-Id-Version: openobject-addons\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2012-12-03 16:02+0000\n" -"PO-Revision-Date: 2012-05-10 17:16+0000\n" -"Last-Translator: Ferdinand-camptocamp \n" +"PO-Revision-Date: 2012-12-10 15:40+0000\n" +"Last-Translator: Thorsten Vocks (OpenBig.org) \n" "Language-Team: German \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:44+0000\n" -"X-Generator: Launchpad (build 16335)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:49+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: hr_evaluation #: help:hr_evaluation.plan.phase,send_anonymous_manager:0 @@ -83,7 +84,7 @@ msgstr "Beurteilungs Phasen" #. module: hr_evaluation #: view:hr.evaluation.interview:0 msgid "Send Request" -msgstr "" +msgstr "Sende Fragebogen" #. module: hr_evaluation #: help:hr_evaluation.plan,month_first:0 @@ -98,7 +99,7 @@ msgstr "" #: view:hr.employee:0 #: model:ir.ui.menu,name:hr_evaluation.menu_open_view_hr_evaluation_tree msgid "Appraisals" -msgstr "" +msgstr "Bewertung" #. module: hr_evaluation #: view:hr_evaluation.plan.phase:0 @@ -109,7 +110,7 @@ msgstr "(eval_name)s: Beurteilung Bezeichnung" #: field:hr.evaluation.interview,message_ids:0 #: field:hr_evaluation.evaluation,message_ids:0 msgid "Messages" -msgstr "" +msgstr "Mitteilungen" #. module: hr_evaluation #: view:hr_evaluation.plan.phase:0 @@ -149,7 +150,7 @@ msgstr "Bewertung" #: help:hr.evaluation.interview,message_unread:0 #: help:hr_evaluation.evaluation,message_unread:0 msgid "If checked new messages require your attention." -msgstr "" +msgstr "Wenn aktiviert, erfordern neue Nachrichten Ihr Handeln" #. module: hr_evaluation #: view:hr_evaluation.plan.phase:0 @@ -223,6 +224,9 @@ msgid "" "Holds the Chatter summary (number of messages, ...). This summary is " "directly in html format in order to be inserted in kanban views." msgstr "" +"Hier finden Sie die Nachrichtenübersicht (Anzahl Nachrichten etc., ...) im " +"html Format, um über dieses Format dann später in einer Kanban Ansicht " +"weiterzuarbeiten." #. module: hr_evaluation #: view:hr_evaluation.evaluation:0 @@ -239,7 +243,7 @@ msgstr "Frist" #: code:addons/hr_evaluation/hr_evaluation.py:326 #, python-format msgid "Warning!" -msgstr "" +msgstr "Warnung !" #. module: hr_evaluation #: view:hr.evaluation.report:0 @@ -259,7 +263,7 @@ msgstr "%(date)s: aktuelles Datum" #. module: hr_evaluation #: model:ir.actions.act_window,name:hr_evaluation.act_hr_employee_2_hr__evaluation_interview msgid "Interviews" -msgstr "" +msgstr "Interviews" #. module: hr_evaluation #: code:addons/hr_evaluation/hr_evaluation.py:82 @@ -271,13 +275,13 @@ msgstr "Bezüglich " #: field:hr.evaluation.interview,message_follower_ids:0 #: field:hr_evaluation.evaluation,message_follower_ids:0 msgid "Followers" -msgstr "" +msgstr "Followers" #. module: hr_evaluation #: field:hr.evaluation.interview,message_unread:0 #: field:hr_evaluation.evaluation,message_unread:0 msgid "Unread Messages" -msgstr "" +msgstr "Ungelesene Mitteilungen" #. module: hr_evaluation #: view:hr.evaluation.report:0 @@ -400,6 +404,20 @@ msgid "" "

\n" " " msgstr "" +"

\n" +" Klicken Sie zur Erstellung eines Vorgehens zur Beurteilung " +"von Mitarbeitern .\n" +"

\n" +" Sie können verschiedene Vorgehensweisen zur Mitarbeiter " +"Beurteilung hinterlegen \n" +" (z.B. erstes Interview nach 6 Monaten, dann jährlich). " +"Danach können Sie jedem Mitarbeiter \n" +" das jeweilige Vorgehen zuweisen, damit OpenERP automatisch " +"die Interview Anfragen zur \n" +" Beurteilung an den Vorgesetzten / untergebenen Mitarbeiter " +"versenden kann.\n" +"

\n" +" " #. module: hr_evaluation #: view:hr_evaluation.plan.phase:0 @@ -447,7 +465,7 @@ msgstr "Alle Antworten" #: view:hr.evaluation.interview:0 #: view:hr_evaluation.evaluation:0 msgid "Answer Survey" -msgstr "" +msgstr "Beantworte Befragung" #. module: hr_evaluation #: selection:hr.evaluation.report,month:0 @@ -560,7 +578,7 @@ msgstr " (employee_name)s: Partner Bezeichnung" #: field:hr.evaluation.interview,message_is_follower:0 #: field:hr_evaluation.evaluation,message_is_follower:0 msgid "Is a Follower" -msgstr "" +msgstr "Ist bereits Follower" #. module: hr_evaluation #: view:hr.evaluation.report:0 @@ -591,7 +609,7 @@ msgstr "Erweiterter Filter..." #: field:hr_evaluation.evaluation,message_comment_ids:0 #: help:hr_evaluation.evaluation,message_comment_ids:0 msgid "Comments and emails" -msgstr "" +msgstr "Kommentare und EMails" #. module: hr_evaluation #: help:hr_evaluation.plan.phase,send_anonymous_employee:0 @@ -644,6 +662,19 @@ msgid "" "

\n" " " msgstr "" +"

\n" +" Klicken Sie zur Erstellung einer Befragung im Kontext einer " +"Beurteilung eines Mitarbeiters. \n" +"

\n" +" Diese Interviews werden normalerweise durch das dem " +"Mitarbeiter zugewiesene Vorgehen \n" +" zur Beurteilung direkt in OpenERP generiert. Jeder " +"Mitarbeiter erhält dadurch automatisch\n" +" und in regelmässigen Zeitabständen Anfragen um die " +"Mitarbeiter in regelmässigen Abständen\n" +" zu beurteilen. \n" +"

\n" +" " #. module: hr_evaluation #: field:hr_evaluation.plan.phase,action:0 @@ -697,7 +728,7 @@ msgstr "Frist" #. module: hr_evaluation #: help:hr_evaluation.evaluation,rating:0 msgid "This is the appreciation on which the evaluation is summarized." -msgstr "" +msgstr "Die geschätzte Gesamtbeurteilung aller Einzelbewertungen." #. module: hr_evaluation #: selection:hr_evaluation.plan.phase,action:0 @@ -825,6 +856,24 @@ msgid "" "

\n" " " msgstr "" +"

\n" +" Klicken Sie zur Erstellung einer neuen Beurteilung.\n" +"

\n" +" Jedem Mitarbeiter kann ein individuelles " +"Beurteilungsverfahren zugewiesen werden. Dieses\n" +" Vorgehen definiert die Häufigkeit und die Art und Weise der " +"regelmässigen Mitarbeiterbeurteilung.\n" +" Sie können die konkrete Abfolge mit den einzelnen Interviews " +"festlegen und dabei die entsprechenden\n" +" Fragebögen diesen Einzelschritten zuweisen. OpenERP kann " +"sämtliche übliche Vorgehensweisen\n" +" unterstützen: Vorgesetztenbeurteilung durch Mitarbeiter, " +"Mitarbeiterbeurteilung durch Vorgesetzte,\n" +" Selbsteinschätzungvon Mitarbeitern, sowie Abschlußbeurteilung " +"von Mitarbeitern und Vorgesetzten \n" +" durch die Unternehmensleitung. \n" +"

\n" +" " #. module: hr_evaluation #: view:hr_evaluation.evaluation:0 @@ -880,6 +929,8 @@ msgid "" "You cannot change state, because some appraisal(s) are in waiting answer or " "draft state." msgstr "" +"Sie können den Status nicht anpassen, da einige Bewertungen im Wiedervorlage " +"oder Entwurf Status sind." #. module: hr_evaluation #: selection:hr.evaluation.report,month:0 @@ -895,7 +946,7 @@ msgstr "Phasen des Beurteilungsplanes" #: help:hr.evaluation.interview,message_ids:0 #: help:hr_evaluation.evaluation,message_ids:0 msgid "Messages and communication history" -msgstr "" +msgstr "Nachrichten und Kommunikations-Historie" #. module: hr_evaluation #: view:hr.evaluation.interview:0 diff --git a/addons/hr_expense/i18n/de.po b/addons/hr_expense/i18n/de.po index 349aaa25109..454a22efab6 100644 --- a/addons/hr_expense/i18n/de.po +++ b/addons/hr_expense/i18n/de.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: OpenERP Server 6.0dev\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-12-03 16:02+0000\n" -"PO-Revision-Date: 2012-05-10 18:05+0000\n" -"Last-Translator: Ferdinand-camptocamp \n" +"PO-Revision-Date: 2012-12-10 16:24+0000\n" +"Last-Translator: Thorsten Vocks (OpenBig.org) \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:37+0000\n" -"X-Generator: Launchpad (build 16335)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:48+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: hr_expense #: view:hr.expense.expense:0 @@ -71,7 +72,7 @@ msgstr "Neue Ausgabe" #: field:hr.expense.line,uom_id:0 #: view:product.product:0 msgid "Unit of Measure" -msgstr "" +msgstr "Mengeneinheit" #. module: hr_expense #: selection:hr.expense.report,month:0 @@ -81,7 +82,7 @@ msgstr "März" #. module: hr_expense #: field:hr.expense.expense,message_unread:0 msgid "Unread Messages" -msgstr "" +msgstr "Ungelesene Mitteilungen" #. module: hr_expense #: field:hr.expense.expense,company_id:0 @@ -108,7 +109,7 @@ msgstr "Statistik Personalspesen" #. module: hr_expense #: view:hr.expense.expense:0 msgid "Open Receipt" -msgstr "" +msgstr "Öffne Spesenbeleg" #. module: hr_expense #: view:hr.expense.report:0 @@ -133,14 +134,14 @@ msgstr "Bemerkungen" #. module: hr_expense #: field:hr.expense.expense,message_ids:0 msgid "Messages" -msgstr "" +msgstr "Mitteilungen" #. module: hr_expense #: code:addons/hr_expense/hr_expense.py:179 #: code:addons/hr_expense/hr_expense.py:195 #, python-format msgid "Error!" -msgstr "" +msgstr "Fehler !" #. module: hr_expense #: model:ir.actions.act_window,name:hr_expense.hr_expense_product @@ -168,7 +169,7 @@ msgstr "" #. module: hr_expense #: help:hr.expense.expense,message_unread:0 msgid "If checked new messages require your attention." -msgstr "" +msgstr "Wenn aktiviert, erfordern neue Nachrichten Ihr Handeln" #. module: hr_expense #: selection:hr.expense.report,state:0 @@ -199,6 +200,17 @@ msgid "" "If the admin accepts it, the status is 'Accepted'.\n" " If a receipt is made for the expense request, the status is 'Done'." msgstr "" +"Wenn eine Spesenerstattung erstellt wird, ist der Status zunächst " +"'Entwurf'.\n" +"\n" +" Die erstellte Anfrage wird dann zuerst durch den Benutzer selbst bestätigt, " +"bevor der Abteilungsleiter \n" +"die Anfrage im Status 'Erwarte Prüfung' erhält. Durch Prüfung und " +"Genehmigung des Abteilungsleiters\n" +"wechselt der Status auf 'Genehmigt'. \n" +"\n" +" Wenn auch ein Buchungsbeleg mit erstellt werden soll, ändert sich dadurch " +"der Status auf 'erledigt'." #. module: hr_expense #: help:hr.expense.expense,date_confirm:0 @@ -221,12 +233,15 @@ msgid "" "Holds the Chatter summary (number of messages, ...). This summary is " "directly in html format in order to be inserted in kanban views." msgstr "" +"Hier finden Sie die Nachrichtenübersicht (Anzahl Nachrichten etc., ...) im " +"html Format, um über dieses Format dann später in einer Kanban Ansicht " +"weiterzuarbeiten." #. module: hr_expense #: code:addons/hr_expense/hr_expense.py:300 #, python-format msgid "Warning" -msgstr "" +msgstr "Warnung !" #. module: hr_expense #: report:hr.expense:0 @@ -274,7 +289,7 @@ msgstr "Reihenfolge für die Ansichtsliste der Personalspesen." #: view:hr.expense.report:0 #: field:hr.expense.report,state:0 msgid "Status" -msgstr "" +msgstr "Status" #. module: hr_expense #: field:hr.expense.line,analytic_account:0 @@ -296,7 +311,7 @@ msgstr "Warteliste" #. module: hr_expense #: field:hr.expense.expense,message_follower_ids:0 msgid "Followers" -msgstr "" +msgstr "Followers" #. module: hr_expense #: report:hr.expense:0 @@ -333,7 +348,7 @@ msgstr "Einige Aufwendungen können an Kunde weiterberechnet werden" #: code:addons/hr_expense/hr_expense.py:195 #, python-format msgid "The employee must have a home address." -msgstr "" +msgstr "Der Mitarbeiter muss eine Privatanschrift haben." #. module: hr_expense #: view:board.board:0 @@ -388,7 +403,7 @@ msgstr "Nach der Rechnungserstellung erfolgt die Erstattung." #: code:addons/hr_expense/hr_expense.py:106 #, python-format msgid "Warning!" -msgstr "" +msgstr "Warnung !" #. module: hr_expense #: model:process.node,name:hr_expense.process_node_reimbursement0 @@ -405,7 +420,7 @@ msgstr "Genehmigungsdatum" #: code:addons/hr_expense/hr_expense.py:225 #, python-format msgid "Expense Receipt" -msgstr "" +msgstr "Personalspesen Ausgabebeleg" #. module: hr_expense #: view:hr.expense.report:0 @@ -465,7 +480,7 @@ msgstr "Benutzer" #. module: hr_expense #: field:hr.expense.expense,voucher_id:0 msgid "Employee's Receipt" -msgstr "" +msgstr "Quittung für den Mitarbeiter" #. module: hr_expense #: selection:hr.expense.expense,state:0 @@ -484,11 +499,13 @@ msgid "" "Selected Unit of Measure does not belong to the same category as the product " "Unit of Measure" msgstr "" +"Die ausgewählte Mengeneinheit gehört nicht zur selben Kategorie wie die dem " +"Produkt zugewiesene Mengeneinheit." #. module: hr_expense #: help:hr.expense.expense,journal_id:0 msgid "The journal used when the expense is done." -msgstr "" +msgstr "Das Buchungsjournal der Spesenausgabe." #. module: hr_expense #: field:hr.expense.expense,note:0 @@ -512,6 +529,8 @@ msgid "" "Please configure Default Expense account for Product purchase: " "`property_account_expense_categ`." msgstr "" +"Bitte konfigurieren Sie das Standard Aufwand Konto für den Einkauf von " +"Waren: 'property_account_expense_categ`." #. module: hr_expense #: model:process.transition,note:hr_expense.process_transition_approveexpense0 @@ -546,7 +565,7 @@ msgstr "Entwurf Spesenerfassung" #. module: hr_expense #: field:hr.expense.expense,message_is_follower:0 msgid "Is a Follower" -msgstr "" +msgstr "Ist bereits Follower" #. module: hr_expense #: model:ir.actions.act_window,name:hr_expense.product_normal_form_view_installer @@ -574,7 +593,7 @@ msgstr "Erweiterter Filter..." #: field:hr.expense.expense,message_comment_ids:0 #: help:hr.expense.expense,message_comment_ids:0 msgid "Comments and emails" -msgstr "" +msgstr "Kommentare und EMails" #. module: hr_expense #: selection:hr.expense.report,month:0 @@ -596,11 +615,23 @@ msgid "" "

\n" " " msgstr "" +"

\n" +" Klicken Sie zur Erfassung Ihrer Personalspesen.\n" +"

\n" +" OpenERP wird dabei den gesamten Prozess unterstützen. Zuerst " +"wird der Spesenbeleg\n" +" durch den vorgesetzten Abteilungsleiter genehmigt, dann " +"erfolgt die Erstattung der\n" +" durch den Mitarbeiter bezahlten Ausgaben, möglicherweise " +"werden schlußendlich \n" +" bestimmte Ausgaben an Kunden weiter berechnet.\n" +"

\n" +" " #. module: hr_expense #: view:hr.expense.expense:0 msgid "Generate Accounting Entries" -msgstr "" +msgstr "Erzeuge Buchungen" #. module: hr_expense #: selection:hr.expense.report,month:0 @@ -615,7 +646,7 @@ msgstr "Personalkosten" #. module: hr_expense #: field:hr.expense.expense,message_summary:0 msgid "Summary" -msgstr "" +msgstr "Zusammenfassung" #. module: hr_expense #: model:product.template,name:hr_expense.car_travel_product_template @@ -630,7 +661,7 @@ msgstr "Dem Manager vorlegen" #. module: hr_expense #: view:hr.expense.report:0 msgid "Done Expenses" -msgstr "" +msgstr "Erledigte Spesenabrechnungen" #. module: hr_expense #: model:process.node,note:hr_expense.process_node_confirmedexpenses0 @@ -656,7 +687,7 @@ msgstr "Speseneingabeformular" #. module: hr_expense #: field:hr.expense.report,voucher_id:0 msgid "Receipt" -msgstr "" +msgstr "Ausgabenbeleg" #. module: hr_expense #: view:hr.expense.report:0 @@ -674,7 +705,7 @@ msgstr "Preis/ME" #: view:hr.expense.report:0 #: selection:hr.expense.report,state:0 msgid "Done" -msgstr "" +msgstr "Erledigt" #. module: hr_expense #: model:process.transition.action,name:hr_expense.process_transition_action_supplierinvoice0 @@ -695,12 +726,12 @@ msgstr "Weiterberechnung" #. module: hr_expense #: view:hr.expense.expense:0 msgid "Expense Date" -msgstr "" +msgstr "Ausgabendatum" #. module: hr_expense #: field:hr.expense.expense,user_valid:0 msgid "Validation By" -msgstr "" +msgstr "Geprüft durch" #. module: hr_expense #: view:hr.expense.expense:0 @@ -838,7 +869,7 @@ msgstr "Bezeichnung" #: code:addons/hr_expense/hr_expense.py:106 #, python-format msgid "You can only delete draft expenses!" -msgstr "" +msgstr "Sie können nur Spesenabrechnungen im Entwurf löschen" #. module: hr_expense #: field:hr.expense.expense,account_move_id:0 @@ -873,7 +904,7 @@ msgstr "Genehmigen" #. module: hr_expense #: help:hr.expense.expense,message_ids:0 msgid "Messages and communication history" -msgstr "" +msgstr "Nachrichten und Kommunikations-Historie" #. module: hr_expense #: field:hr.expense.line,sequence:0 @@ -883,7 +914,7 @@ msgstr "Sequenz" #. module: hr_expense #: model:ir.ui.menu,name:hr_expense.menu_hr_product msgid "Types of Expenses" -msgstr "" +msgstr "Ausgabentypen" #. module: hr_expense #: model:process.transition,note:hr_expense.process_transition_confirmexpense0 @@ -902,12 +933,12 @@ msgstr "Personalspesen" #. module: hr_expense #: help:product.product,hr_expense_ok:0 msgid "Specify if the product can be selected in an HR expense line." -msgstr "" +msgstr "Legen Sie fest, ob Produkt in Spesenabrechnung erfasst werden darf." #. module: hr_expense #: view:hr.expense.expense:0 msgid "Accounting" -msgstr "" +msgstr "Finanzbuchhaltung" #. module: hr_expense #: view:hr.expense.expense:0 diff --git a/addons/hr_expense/i18n/hr.po b/addons/hr_expense/i18n/hr.po index fdbffae5ec3..cad3f378129 100644 --- a/addons/hr_expense/i18n/hr.po +++ b/addons/hr_expense/i18n/hr.po @@ -7,14 +7,14 @@ msgstr "" "Project-Id-Version: OpenERP Server 5.0.4\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-12-03 16:02+0000\n" -"PO-Revision-Date: 2012-05-10 18:15+0000\n" -"Last-Translator: Raphael Collet (OpenERP) \n" +"PO-Revision-Date: 2012-12-10 08:20+0000\n" +"Last-Translator: Goran Kliska \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:38+0000\n" -"X-Generator: Launchpad (build 16335)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:48+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: hr_expense #: view:hr.expense.expense:0 @@ -71,7 +71,7 @@ msgstr "Novi trošak" #: field:hr.expense.line,uom_id:0 #: view:product.product:0 msgid "Unit of Measure" -msgstr "" +msgstr "Jedinica mjere" #. module: hr_expense #: selection:hr.expense.report,month:0 @@ -81,14 +81,14 @@ msgstr "Ožujak" #. module: hr_expense #: field:hr.expense.expense,message_unread:0 msgid "Unread Messages" -msgstr "" +msgstr "Nepročitane poruke" #. module: hr_expense #: field:hr.expense.expense,company_id:0 #: view:hr.expense.report:0 #: field:hr.expense.report,company_id:0 msgid "Company" -msgstr "" +msgstr "Organizacija" #. module: hr_expense #: view:hr.expense.expense:0 @@ -98,12 +98,12 @@ msgstr "Postavi na neodobreno" #. module: hr_expense #: view:hr.expense.expense:0 msgid "To Pay" -msgstr "" +msgstr "Za isplatu" #. module: hr_expense #: model:ir.model,name:hr_expense.model_hr_expense_report msgid "Expenses Statistics" -msgstr "" +msgstr "Statistika troškova" #. module: hr_expense #: view:hr.expense.expense:0 @@ -114,7 +114,7 @@ msgstr "" #: view:hr.expense.report:0 #: field:hr.expense.report,day:0 msgid "Day" -msgstr "" +msgstr "Dan" #. module: hr_expense #: help:hr.expense.expense,date_valid:0 @@ -131,25 +131,25 @@ msgstr "Bilješke" #. module: hr_expense #: field:hr.expense.expense,message_ids:0 msgid "Messages" -msgstr "" +msgstr "Poruke" #. module: hr_expense #: code:addons/hr_expense/hr_expense.py:179 #: code:addons/hr_expense/hr_expense.py:195 #, python-format msgid "Error!" -msgstr "" +msgstr "Greška!" #. module: hr_expense #: model:ir.actions.act_window,name:hr_expense.hr_expense_product #: view:product.product:0 msgid "Products" -msgstr "" +msgstr "Proizvodi" #. module: hr_expense #: view:hr.expense.report:0 msgid "Confirm Expenses" -msgstr "" +msgstr "Potvrdi troškove" #. module: hr_expense #: selection:hr.expense.report,state:0 @@ -207,7 +207,7 @@ msgstr "" #: view:hr.expense.report:0 #: field:hr.expense.report,nbr:0 msgid "# of Lines" -msgstr "" +msgstr "# linija" #. module: hr_expense #: help:hr.expense.expense,message_summary:0 @@ -220,7 +220,7 @@ msgstr "" #: code:addons/hr_expense/hr_expense.py:300 #, python-format msgid "Warning" -msgstr "" +msgstr "Upozoranje" #. module: hr_expense #: report:hr.expense:0 @@ -230,7 +230,7 @@ msgstr "" #. module: hr_expense #: report:hr.expense:0 msgid "Total:" -msgstr "" +msgstr "Ukupno:" #. module: hr_expense #: model:process.transition,name:hr_expense.process_transition_refuseexpense0 @@ -240,7 +240,7 @@ msgstr "Odbiti trošak" #. module: hr_expense #: field:hr.expense.report,price_average:0 msgid "Average Price" -msgstr "" +msgstr "Prosječna cijena" #. module: hr_expense #: view:hr.expense.expense:0 @@ -268,7 +268,7 @@ msgstr "" #: view:hr.expense.report:0 #: field:hr.expense.report,state:0 msgid "Status" -msgstr "" +msgstr "Status" #. module: hr_expense #: field:hr.expense.line,analytic_account:0 @@ -280,12 +280,12 @@ msgstr "Analitički konto" #. module: hr_expense #: field:hr.expense.report,date:0 msgid "Date " -msgstr "" +msgstr "Datum " #. module: hr_expense #: view:hr.expense.report:0 msgid "Waiting" -msgstr "" +msgstr "Na čekanju" #. module: hr_expense #: field:hr.expense.expense,message_follower_ids:0 @@ -304,7 +304,7 @@ msgstr "Djelatnik" #: view:hr.expense.expense:0 #: selection:hr.expense.expense,state:0 msgid "New" -msgstr "" +msgstr "Nova(i)" #. module: hr_expense #: report:hr.expense:0 @@ -339,7 +339,7 @@ msgstr "Moji troškovi" #. module: hr_expense #: view:hr.expense.report:0 msgid "Creation Date" -msgstr "" +msgstr "Datum kreiranja" #. module: hr_expense #: model:ir.actions.report.xml,name:hr_expense.hr_expenses @@ -366,12 +366,12 @@ msgstr "" #: view:hr.expense.report:0 #: field:hr.expense.report,no_of_products:0 msgid "# of Products" -msgstr "" +msgstr "# Proizvoda" #. module: hr_expense #: selection:hr.expense.report,month:0 msgid "July" -msgstr "" +msgstr "Srpanj" #. module: hr_expense #: model:process.transition,note:hr_expense.process_transition_reimburseexpense0 @@ -382,7 +382,7 @@ msgstr "Nakon generiranja računa, isplati troškove" #: code:addons/hr_expense/hr_expense.py:106 #, python-format msgid "Warning!" -msgstr "" +msgstr "Upozorenje!" #. module: hr_expense #: model:process.node,name:hr_expense.process_node_reimbursement0 @@ -431,19 +431,19 @@ msgstr "" #. module: hr_expense #: selection:hr.expense.report,month:0 msgid "September" -msgstr "" +msgstr "Rujan" #. module: hr_expense #: selection:hr.expense.report,month:0 msgid "December" -msgstr "" +msgstr "Prosinac" #. module: hr_expense #: view:hr.expense.expense:0 #: view:hr.expense.report:0 #: field:hr.expense.report,month:0 msgid "Month" -msgstr "" +msgstr "Mjesec" #. module: hr_expense #: field:hr.expense.expense,currency_id:0 @@ -454,7 +454,7 @@ msgstr "Valuta" #. module: hr_expense #: field:hr.expense.expense,user_id:0 msgid "User" -msgstr "" +msgstr "Korisnik" #. module: hr_expense #: field:hr.expense.expense,voucher_id:0 @@ -464,7 +464,7 @@ msgstr "" #. module: hr_expense #: selection:hr.expense.expense,state:0 msgid "Waiting Approval" -msgstr "" +msgstr "Čeka odobrenje" #. module: hr_expense #: model:process.node,note:hr_expense.process_node_draftexpenses0 @@ -497,7 +497,7 @@ msgstr "Generiraj novi izlazni račun" #. module: hr_expense #: selection:hr.expense.report,state:0 msgid "Draft" -msgstr "" +msgstr "Nacrt" #. module: hr_expense #: code:addons/hr_expense/hr_expense.py:179 @@ -515,7 +515,7 @@ msgstr "Trošak odobren" #. module: hr_expense #: selection:hr.expense.report,month:0 msgid "August" -msgstr "" +msgstr "Kolovoz" #. module: hr_expense #: model:process.node,note:hr_expense.process_node_approved0 @@ -530,7 +530,7 @@ msgstr "Ukupni iznos" #. module: hr_expense #: selection:hr.expense.report,month:0 msgid "June" -msgstr "" +msgstr "Lipanj" #. module: hr_expense #: model:process.node,name:hr_expense.process_node_draftexpenses0 @@ -552,28 +552,28 @@ msgstr "" #: field:hr.expense.expense,date:0 #: field:hr.expense.line,date_value:0 msgid "Date" -msgstr "" +msgstr "Datum" #. module: hr_expense #: selection:hr.expense.report,month:0 msgid "November" -msgstr "" +msgstr "Studeni" #. module: hr_expense #: view:hr.expense.report:0 msgid "Extended Filters..." -msgstr "" +msgstr "Dodatni filteri..." #. module: hr_expense #: field:hr.expense.expense,message_comment_ids:0 #: help:hr.expense.expense,message_comment_ids:0 msgid "Comments and emails" -msgstr "" +msgstr "Komentari i e-pošta." #. module: hr_expense #: selection:hr.expense.report,month:0 msgid "October" -msgstr "" +msgstr "Listopad" #. module: hr_expense #: model:ir.actions.act_window,help:hr_expense.expense_all @@ -599,7 +599,7 @@ msgstr "" #. module: hr_expense #: selection:hr.expense.report,month:0 msgid "January" -msgstr "" +msgstr "Siječanj" #. module: hr_expense #: report:hr.expense:0 @@ -609,7 +609,7 @@ msgstr "" #. module: hr_expense #: field:hr.expense.expense,message_summary:0 msgid "Summary" -msgstr "" +msgstr "Sažetak" #. module: hr_expense #: model:product.template,name:hr_expense.car_travel_product_template @@ -650,7 +650,7 @@ msgstr "" #. module: hr_expense #: field:hr.expense.report,voucher_id:0 msgid "Receipt" -msgstr "" +msgstr "Račun" #. module: hr_expense #: view:hr.expense.report:0 @@ -661,30 +661,30 @@ msgstr "" #: report:hr.expense:0 #: field:hr.expense.line,unit_amount:0 msgid "Unit Price" -msgstr "" +msgstr "Jed. cijena" #. module: hr_expense #: selection:hr.expense.expense,state:0 #: view:hr.expense.report:0 #: selection:hr.expense.report,state:0 msgid "Done" -msgstr "" +msgstr "Završeno" #. module: hr_expense #: model:process.transition.action,name:hr_expense.process_transition_action_supplierinvoice0 msgid "Invoice" -msgstr "" +msgstr "Račun" #. module: hr_expense #: view:hr.expense.report:0 #: field:hr.expense.report,year:0 msgid "Year" -msgstr "" +msgstr "Godina" #. module: hr_expense #: model:process.transition,name:hr_expense.process_transition_reimbursereinvoice0 msgid "Reinvoice" -msgstr "" +msgstr "Prefakturiraj" #. module: hr_expense #: view:hr.expense.expense:0 @@ -715,7 +715,7 @@ msgstr "" #. module: hr_expense #: model:process.transition.action,name:hr_expense.process_transition_action_accept0 msgid "Accept" -msgstr "" +msgstr "Prihvati" #. module: hr_expense #: report:hr.expense:0 @@ -750,7 +750,7 @@ msgstr "Odobreno" #: field:hr.expense.report,product_id:0 #: model:ir.model,name:hr_expense.model_product_product msgid "Product" -msgstr "" +msgstr "Proizvod" #. module: hr_expense #: report:hr.expense:0 @@ -758,22 +758,22 @@ msgstr "" #: field:hr.expense.expense,name:0 #: field:hr.expense.line,description:0 msgid "Description" -msgstr "" +msgstr "Opis" #. module: hr_expense #: selection:hr.expense.report,month:0 msgid "May" -msgstr "" +msgstr "Svibanj" #. module: hr_expense #: field:hr.expense.line,unit_quantity:0 msgid "Quantities" -msgstr "" +msgstr "Količine" #. module: hr_expense #: report:hr.expense:0 msgid "Price" -msgstr "" +msgstr "Cijena" #. module: hr_expense #: field:hr.expense.report,no_of_account:0 @@ -784,7 +784,7 @@ msgstr "" #: selection:hr.expense.expense,state:0 #: model:process.node,name:hr_expense.process_node_refused0 msgid "Refused" -msgstr "" +msgstr "Odbijen" #. module: hr_expense #: field:product.product,hr_expense_ok:0 @@ -794,7 +794,7 @@ msgstr "" #. module: hr_expense #: report:hr.expense:0 msgid "Ref." -msgstr "" +msgstr "Vezna oznaka" #. module: hr_expense #: field:hr.expense.report,employee_id:0 @@ -815,12 +815,12 @@ msgstr "Računovodstveni podatak" #. module: hr_expense #: selection:hr.expense.report,month:0 msgid "February" -msgstr "" +msgstr "Veljača" #. module: hr_expense #: report:hr.expense:0 msgid "Name" -msgstr "" +msgstr "Naziv" #. module: hr_expense #: code:addons/hr_expense/hr_expense.py:106 @@ -846,7 +846,7 @@ msgstr "" #. module: hr_expense #: selection:hr.expense.report,month:0 msgid "April" -msgstr "" +msgstr "Travanj" #. module: hr_expense #: field:hr.expense.line,name:0 @@ -856,7 +856,7 @@ msgstr "" #. module: hr_expense #: view:hr.expense.expense:0 msgid "Approve" -msgstr "" +msgstr "Odobri" #. module: hr_expense #: help:hr.expense.expense,message_ids:0 @@ -885,7 +885,7 @@ msgstr "" #: model:ir.ui.menu,name:hr_expense.next_id_49 #: model:product.category,name:hr_expense.cat_expense msgid "Expenses" -msgstr "" +msgstr "Troškovi" #. module: hr_expense #: help:product.product,hr_expense_ok:0 @@ -895,24 +895,24 @@ msgstr "" #. module: hr_expense #: view:hr.expense.expense:0 msgid "Accounting" -msgstr "" +msgstr "Računovodstvo" #. module: hr_expense #: view:hr.expense.expense:0 msgid "To Approve" -msgstr "" +msgstr "Za odobriti" #. module: hr_expense #: view:hr.expense.expense:0 #: view:hr.expense.line:0 #: field:hr.expense.line,total_amount:0 msgid "Total" -msgstr "" +msgstr "Ukupno" #. module: hr_expense #: model:process.node,name:hr_expense.process_node_reinvoicing0 msgid "Reinvoicing" -msgstr "" +msgstr "Prefakturiranje" #~ msgid "All expenses" #~ msgstr "Svi troškovi" diff --git a/addons/hr_holidays/i18n/de.po b/addons/hr_holidays/i18n/de.po index c74995a818c..0bafa9dcb50 100644 --- a/addons/hr_holidays/i18n/de.po +++ b/addons/hr_holidays/i18n/de.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: OpenERP Server 6.0dev\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-12-03 16:02+0000\n" -"PO-Revision-Date: 2012-02-22 23:52+0000\n" -"Last-Translator: Ferdinand @ Camptocamp \n" +"PO-Revision-Date: 2012-12-10 17:23+0000\n" +"Last-Translator: Thorsten Vocks (OpenBig.org) \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:38+0000\n" -"X-Generator: Launchpad (build 16335)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:48+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: hr_holidays #: selection:hr.holidays.status,color_name:0 @@ -38,6 +39,8 @@ msgid "" "You cannot modify a leave request that has been approved. Contact a human " "resource manager." msgstr "" +"Sie können keine bereits genehmigte Urlaubsanfrage stornieren. Kontaktieren " +"Sie einen Mitarbeiter aus der Personalabteilung." #. module: hr_holidays #: help:hr.holidays.status,remaining_leaves:0 @@ -62,7 +65,7 @@ msgstr "Zuteilungsmethode" #. module: hr_holidays #: field:hr.employee,leave_date_from:0 msgid "From Date" -msgstr "" +msgstr "Von Datum" #. module: hr_holidays #: view:hr.holidays:0 @@ -74,7 +77,7 @@ msgstr "Abteilung" #: model:ir.actions.act_window,name:hr_holidays.request_approve_allocation #: model:ir.ui.menu,name:hr_holidays.menu_request_approve_allocation msgid "Allocation Requests to Approve" -msgstr "" +msgstr "Zu prüfende Urlaubsanfrage" #. module: hr_holidays #: help:hr.holidays,category_id:0 @@ -107,11 +110,15 @@ msgid "" "The default duration interval between the start date and the end date is 8 " "hours. Feel free to adapt it to your needs." msgstr "" +"Die Standardeinstellung für die Zeitspanne zwischen Beginn und Ende einer " +"Urlaubsanfrage ist 8 Stunden.\r\n" +"Sie können selbstverständlich diese Grundeinstellung auf Ihren Bedarf hin " +"anpassen." #. module: hr_holidays #: field:hr.holidays,number_of_days_temp:0 msgid "Allocation" -msgstr "" +msgstr "Urlaubsanspruch" #. module: hr_holidays #: xsl:holidays.summary:0 @@ -127,6 +134,8 @@ msgstr "Cyan Blau" #: constraint:hr.holidays:0 msgid "You can not have 2 leaves that overlaps on same day!" msgstr "" +"Sie können nicht zwei sich überschneidende Abwesenheitsanfragen am gleichen " +"Tag anfragen !" #. module: hr_holidays #: selection:hr.holidays.status,color_name:0 @@ -141,7 +150,7 @@ msgstr "aktueller Abwesenheitstyp" #. module: hr_holidays #: view:hr.holidays:0 msgid "Validate" -msgstr "" +msgstr "Genehmigt" #. module: hr_holidays #: selection:hr.employee,current_leave_state:0 @@ -166,12 +175,15 @@ msgstr "Abgelehnt" #, python-format msgid "Request submitted, waiting for validation by the manager." msgstr "" +"Urlaub wurde beantragt, erwartet wird Prüfung und Genehmigung durch " +"den Vorgesetzten." #. module: hr_holidays #: code:addons/hr_holidays/hr_holidays.py:439 #, python-format msgid "Request approved, waiting second validation." msgstr "" +"Der Antrag wurde genehmigt, erwartet wird eine zweite Genehmigung." #. module: hr_holidays #: view:hr.employee:0 @@ -183,7 +195,7 @@ msgstr "Urlaubstage" #. module: hr_holidays #: field:hr.holidays,message_ids:0 msgid "Messages" -msgstr "" +msgstr "Mitteilungen" #. module: hr_holidays #: xsl:holidays.summary:0 @@ -194,7 +206,7 @@ msgstr "Analysieren durch" #: code:addons/hr_holidays/wizard/hr_holidays_summary_department.py:44 #, python-format msgid "Error!" -msgstr "" +msgstr "Fehler !" #. module: hr_holidays #: model:ir.ui.menu,name:hr_holidays.menu_request_approve_holidays @@ -218,7 +230,7 @@ msgstr "Zweite Genehmigung" #: code:addons/hr_holidays/hr_holidays.py:448 #, python-format msgid "The request has been approved." -msgstr "" +msgstr "Die Anfrage wurde genehmigt." #. module: hr_holidays #: selection:hr.employee,current_leave_state:0 @@ -245,7 +257,7 @@ msgstr "Genehmigung" #. module: hr_holidays #: help:hr.holidays,message_unread:0 msgid "If checked new messages require your attention." -msgstr "" +msgstr "Wenn aktiviert, erfordern neue Nachrichten Ihr Handeln" #. module: hr_holidays #: field:hr.holidays.status,color_name:0 @@ -276,6 +288,9 @@ msgid "" "Holds the Chatter summary (number of messages, ...). This summary is " "directly in html format in order to be inserted in kanban views." msgstr "" +"Hier finden Sie die Nachrichtenübersicht (Anzahl Nachrichten etc., ...) im " +"html Format, um über dieses Format dann später in einer Kanban Ansicht " +"weiterzuarbeiten." #. module: hr_holidays #: code:addons/hr_holidays/hr_holidays.py:230 @@ -296,12 +311,12 @@ msgstr "Magenta" #. module: hr_holidays #: model:ir.actions.act_window,name:hr_holidays.act_hr_leave_request_to_meeting msgid "Leave Meetings" -msgstr "" +msgstr "Abwesenheit Kalendereinträge" #. module: hr_holidays #: model:hr.holidays.status,name:hr_holidays.holiday_status_cl msgid "Legal Leaves 2012" -msgstr "" +msgstr "Gesetzliche Feiertage 2012" #. module: hr_holidays #: selection:hr.holidays.summary.dept,holiday_type:0 @@ -335,7 +350,7 @@ msgstr "Summe" #: view:hr.holidays.status:0 #: model:ir.actions.act_window,name:hr_holidays.open_view_holiday_status msgid "Leave Types" -msgstr "" +msgstr "Abwesenheitstypen" #. module: hr_holidays #: field:hr.holidays.status,remaining_leaves:0 @@ -345,7 +360,7 @@ msgstr "Resturlaub" #. module: hr_holidays #: field:hr.holidays,message_follower_ids:0 msgid "Followers" -msgstr "" +msgstr "Followers" #. module: hr_holidays #: model:ir.model,name:hr_holidays.model_hr_holidays_remaining_leaves_user @@ -384,7 +399,7 @@ msgstr "Abwesenheit nach Typ" #. module: hr_holidays #: model:ir.actions.server,name:hr_holidays.actions_server_holidays_unread msgid "Holidays: Mark unread" -msgstr "" +msgstr "Urlaub: Markiere als ungelesen" #. module: hr_holidays #: selection:hr.holidays.status,color_name:0 @@ -408,6 +423,14 @@ msgid "" " \n" "The status is 'Approved', when holiday request is approved by manager." msgstr "" +"Der Status wird auf 'Zu Beantragen' gesetzt, wenn eine Anfrage für Urlaub " +"neu erstellt wurde.\n" +"Der Status ist \"Zu Genehmigen\", wenn die erstellte Anfrage vom Benutzer " +"bestätigt wurde.\n" +"Der Status wird zu \"Abgelehnt\", wenn die Urlaubsanfrage vom Vorgesetzten " +"abgelehnt wird.\n" +"Der Status wird zu \"Genehmigt\", wenn die Urlaubsanfrage vom Vorgesetzten " +"geprüft und genehmigt wurde ." #. module: hr_holidays #: view:hr.holidays:0 @@ -445,7 +468,7 @@ msgstr "Suche Abwesenheitstyp" #. module: hr_holidays #: model:ir.actions.server,name:hr_holidays.actions_server_holidays_read msgid "Holidays: Mark read" -msgstr "" +msgstr "Urlaub: Markiere als gelesen" #. module: hr_holidays #: selection:hr.employee,current_leave_state:0 @@ -455,7 +478,7 @@ msgstr "Erwarte Genehmigung" #. module: hr_holidays #: field:hr.holidays,category_id:0 msgid "Employee Tag" -msgstr "" +msgstr "Mitarbeiter 'Tag'" #. module: hr_holidays #: field:hr.holidays.status,max_leaves:0 @@ -473,6 +496,8 @@ msgid "" "Filters only on allocations and requests that belong to an holiday type that " "is 'active' (active field is True)" msgstr "" +"Filter auf Urlaubsansprüche und Anfragen, die 'aktiv' sind (Aktiv Feld ist " +"angehakt)" #. module: hr_holidays #: model:ir.actions.act_window,help:hr_holidays.hr_holidays_leaves_assign_legal @@ -484,6 +509,13 @@ msgid "" "

\n" " " msgstr "" +"

\n" +" Sie können durch die Zuweisung von gesetzlichen Feiertage bei " +"allen Mitarbeitern, \n" +" automatisch die Abwesenheiten der Mitarbeiter erstellen und " +"genehmigen.\n" +"

\n" +" " #. module: hr_holidays #: help:hr.holidays.status,categ_id:0 @@ -491,6 +523,8 @@ msgid "" "Once a leave is validated, OpenERP will create a corresponding meeting of " "this type in the calendar." msgstr "" +"Sobald eine Urlaubsanfrage genehmigt wird, trägt OpenERP dieses unmittelbar " +"in den Kalender als Abwesenheit ein." #. module: hr_holidays #: field:hr.holidays,parent_id:0 @@ -510,7 +544,7 @@ msgstr "Monat" #. module: hr_holidays #: field:hr.holidays,message_unread:0 msgid "Unread Messages" -msgstr "" +msgstr "Ungelesene Mitteilungen" #. module: hr_holidays #: view:hr.holidays:0 @@ -537,12 +571,14 @@ msgid "" "There are not enough %s allocated for employee %s; please create an " "allocation request for this leave type." msgstr "" +"Es gibt nicht ausreichend %s für den Mitarbeiter %s, bitte erstellen Sie " +"zunächst eine Anfrage für weiteren Urlaubsanspruch." #. module: hr_holidays #: view:hr.holidays.summary.dept:0 #: view:hr.holidays.summary.employee:0 msgid "or" -msgstr "" +msgstr "oder" #. module: hr_holidays #: model:ir.actions.act_window,help:hr_holidays.open_ask_holidays @@ -557,11 +593,27 @@ msgid "" "

\n" " " msgstr "" +"

\n" +" Klicken Sie zur Erstellung einer neuen Anfrage für Urlaub " +"oder Abwesenheit.\n" +"

\n" +" Sobald Sie Ihre Anfrage bestätigt haben, wird diese an Ihren " +"Vorgesetzten \n" +" zur Genehmigung versendet. Stellen Sie sicher, dass Sie den " +"korrekten Abwesenheitstyp\n" +" eingetragen haben (unbezahlter Urlaub, Krankheit, " +"gesetzlicher Urlaub) und die korrekte\n" +" Anzahl der zugewiesenen Abwesenheitstage für den " +"entsprechenden Typ anfragen.\n" +"

\n" +" " #. module: hr_holidays #: sql_constraint:hr.holidays:0 msgid "The employee or employee category of this request is missing." msgstr "" +"Der Mitarbeiter oder eine Mitarbeiter Kategorie fehlt für die Erstellung " +"einer Urlaubsanfrage." #. module: hr_holidays #: view:hr.holidays:0 @@ -584,16 +636,19 @@ msgid "" "leaves than the available ones for this type and take them into account for " "the \"Remaining Legal Leaves\" defined on the employee form." msgstr "" +"Durch Aktivierung wird die Erlaubnis erteilt, mehr Urlaubstage als " +"genehmigt und zugewiesen anzufragen und dieses für die Prüfung der " +"verbleibenden Resttage zu berücksichtigen." #. module: hr_holidays #: view:hr.holidays:0 msgid "Reset to New" -msgstr "" +msgstr "Zurück auf Neu" #. module: hr_holidays #: sql_constraint:hr.holidays:0 msgid "The number of days must be greater than 0." -msgstr "" +msgstr "Die Anzahl der Tage muss größer '0' sein." #. module: hr_holidays #: selection:hr.holidays.status,color_name:0 @@ -603,7 +658,7 @@ msgstr "hellrot gelblich" #. module: hr_holidays #: field:hr.employee,leave_date_to:0 msgid "To Date" -msgstr "" +msgstr "Bis" #. module: hr_holidays #: selection:hr.holidays.status,color_name:0 @@ -618,7 +673,7 @@ msgstr "Zuteilung der Urlaube je Mitarbeiter" #. module: hr_holidays #: model:ir.ui.menu,name:hr_holidays.menu_open_view_holiday_status msgid "Leaves Types" -msgstr "" +msgstr "Abwesenheitstypen" #. module: hr_holidays #: field:hr.holidays,meeting_id:0 @@ -631,12 +686,14 @@ msgid "" "This color will be used in the leaves summary located in Reporting\\Leaves " "by Department." msgstr "" +"Diese Farbe wird genutzt in der Statistik der Abwesenheitstage unter " +"Statistiken / Urlaub nach Abteilungen" #. module: hr_holidays #: view:hr.holidays:0 #: field:hr.holidays,state:0 msgid "Status" -msgstr "" +msgstr "Status" #. module: hr_holidays #: selection:hr.holidays.status,color_name:0 @@ -646,12 +703,12 @@ msgstr "Elfenbein" #. module: hr_holidays #: model:ir.model,name:hr_holidays.model_hr_holidays_summary_employee msgid "HR Leaves Summary Report By Employee" -msgstr "" +msgstr "Personal Urlaubstage Statistik nach Mitarbeiter" #. module: hr_holidays #: model:ir.actions.act_window,name:hr_holidays.request_approve_holidays msgid "Requests to Approve" -msgstr "" +msgstr "Zu genehmigende Anfragen" #. module: hr_holidays #: field:hr.holidays.status,leaves_taken:0 @@ -661,7 +718,7 @@ msgstr "Beanspruchter Uralub" #. module: hr_holidays #: field:hr.holidays,message_is_follower:0 msgid "Is a Follower" -msgstr "" +msgstr "Ist bereits Follower" #. module: hr_holidays #: field:hr.holidays,user_id:0 @@ -678,7 +735,7 @@ msgstr "Aktiv" #: field:hr.holidays,message_comment_ids:0 #: help:hr.holidays,message_comment_ids:0 msgid "Comments and emails" -msgstr "" +msgstr "Kommentare und EMails" #. module: hr_holidays #: view:hr.employee:0 @@ -694,7 +751,7 @@ msgstr "Erste Genehmigung" #. module: hr_holidays #: field:hr.holidays,message_summary:0 msgid "Summary" -msgstr "" +msgstr "Zusammenfassung" #. module: hr_holidays #: model:hr.holidays.status,name:hr_holidays.holiday_status_unpaid @@ -715,7 +772,7 @@ msgstr "Auswertung Urlaubskonto" #. module: hr_holidays #: view:hr.holidays:0 msgid "Submit to Manager" -msgstr "" +msgstr "Dem Manager vorlegen" #. module: hr_holidays #: view:hr.employee:0 @@ -736,7 +793,7 @@ msgstr "Urlaube meiner Abteilung" #: code:addons/hr_holidays/hr_holidays.py:428 #, python-format msgid "Request created, waiting confirmation." -msgstr "" +msgstr "Antrag wurde erstellt, Genehmigung wird erwartet" #. module: hr_holidays #: field:hr.employee,current_leave_state:0 @@ -793,7 +850,7 @@ msgstr "Bestätigt" #: code:addons/hr_holidays/hr_holidays.py:230 #, python-format msgid "You cannot delete a leave which is in %s state." -msgstr "" +msgstr "Sie können keine Anfrage im %s Status stornieren" #. module: hr_holidays #: view:hr.holidays:0 @@ -807,6 +864,9 @@ msgid "" "By Employee: Allocation/Request for individual Employee, By Employee Tag: " "Allocation/Request for group of employees in category" msgstr "" +"Nach Mitarbeiter: Anspruch / Anfrage für einzelne Mitarbeiter, nach " +"Mitarbeiter 'Tag': Anspruch / Anfragen für Gruppe von Mitarbeitern nach " +"Kategorien" #. module: hr_holidays #: model:ir.model,name:hr_holidays.model_resource_calendar_leaves @@ -823,7 +883,7 @@ msgstr "Doppelte Genehmigung" #: view:hr.employee:0 #: view:hr.holidays:0 msgid "days" -msgstr "" +msgstr "Tage" #. module: hr_holidays #: view:hr.holidays.summary.dept:0 @@ -851,7 +911,7 @@ msgstr "Abteilungen" #. module: hr_holidays #: selection:hr.holidays,state:0 msgid "To Submit" -msgstr "" +msgstr "Zu bestätigen" #. module: hr_holidays #: code:addons/hr_holidays/hr_holidays.py:328 @@ -870,7 +930,7 @@ msgstr "Beschreibung" #. module: hr_holidays #: selection:hr.holidays,holiday_type:0 msgid "By Employee Tag" -msgstr "" +msgstr "Nach Mitarbeiter 'Tag'" #. module: hr_holidays #: selection:hr.employee,current_leave_state:0 @@ -881,7 +941,7 @@ msgstr "Abgelehnt" #. module: hr_holidays #: field:hr.holidays.status,categ_id:0 msgid "Meeting Type" -msgstr "" +msgstr "Terminart" #. module: hr_holidays #: field:hr.holidays.remaining.leaves.user,no_of_leaves:0 @@ -891,7 +951,7 @@ msgstr "Resturlaub" #. module: hr_holidays #: view:hr.holidays:0 msgid "Allocated Days" -msgstr "" +msgstr "Zugewiesener Urlaubsanspruch" #. module: hr_holidays #: view:hr.holidays:0 @@ -940,7 +1000,7 @@ msgstr "Art" #: selection:hr.holidays.summary.dept,holiday_type:0 #: selection:hr.holidays.summary.employee,holiday_type:0 msgid "Both Approved and Confirmed" -msgstr "" +msgstr "Genehmigt und bestätigt" #. module: hr_holidays #: view:hr.holidays:0 @@ -950,7 +1010,7 @@ msgstr "Genehmige" #. module: hr_holidays #: help:hr.holidays,message_ids:0 msgid "Messages and communication history" -msgstr "" +msgstr "Nachrichten und Kommunikations-Historie" #. module: hr_holidays #: code:addons/hr_holidays/hr_holidays.py:241 @@ -958,7 +1018,7 @@ msgstr "" #: sql_constraint:hr.holidays:0 #, python-format msgid "The start date must be anterior to the end date." -msgstr "" +msgstr "Das Start Datum muss vor dem Ende Datum sein" #. module: hr_holidays #: model:ir.model,name:hr_holidays.model_hr_holidays @@ -969,7 +1029,7 @@ msgstr "Urlaub" #: code:addons/hr_holidays/hr_holidays.py:453 #, python-format msgid "Request refused" -msgstr "" +msgstr "Anfrage wurdeabgelehnt" #. module: hr_holidays #: help:hr.holidays.status,double_validation:0 @@ -977,6 +1037,8 @@ msgid "" "When selected, the Allocation/Leave Requests for this type require a second " "validation to be approved." msgstr "" +"Durch eine Aktivierung, erfordert eine Anfrage für Urlaubsanspruch und " +"Abwesenheit eine zweite weitere Genehmigung." #. module: hr_holidays #: view:hr.holidays:0 @@ -997,6 +1059,10 @@ msgid "" "to create allocation/leave request. Total based on all the leave types " "without overriding limit." msgstr "" +"Gesamtzahl gesetzlicher Urlaubstage für diesen Mitarbeiter, ändern Sie " +"diesen Wert um eine Anfrage für einen Anspruch / Abwesenheit zu generieren. " +"Gesamtzahl basiert auf allen Abwesenheitstypen, die keine Überschreitung " +"zulassen." #. module: hr_holidays #: selection:hr.holidays.status,color_name:0 @@ -1006,7 +1072,7 @@ msgstr "Hellrosa" #. module: hr_holidays #: xsl:holidays.summary:0 msgid "leaves." -msgstr "" +msgstr "abwesend." #. module: hr_holidays #: view:hr.holidays:0 @@ -1016,7 +1082,7 @@ msgstr "Abteilungsleiter" #. module: hr_holidays #: model:ir.model,name:hr_holidays.model_hr_holidays_summary_dept msgid "HR Leaves Summary Report By Department" -msgstr "" +msgstr "Personal Urlaubsstatistik nach Abteilungen" #. module: hr_holidays #: view:hr.holidays:0 @@ -1026,7 +1092,7 @@ msgstr "Jahr" #. module: hr_holidays #: view:hr.holidays:0 msgid "Duration" -msgstr "" +msgstr "Dauer" #. module: hr_holidays #: view:hr.holidays:0 @@ -1039,6 +1105,7 @@ msgstr "Zu Genehmigen" #, python-format msgid "You have to select at least one Department. And try again." msgstr "" +"Sie müssen mindestens eine Abteilung zuweisen. Versuchen Sie es erneut." #. module: hr_holidays #: field:hr.holidays,notes:0 @@ -1048,13 +1115,13 @@ msgstr "Abwesenheitsgrund" #. module: hr_holidays #: field:hr.holidays.summary.employee,holiday_type:0 msgid "Select Leave Type" -msgstr "" +msgstr "Auswahl des Abwesenheitstypen" #. module: hr_holidays #: code:addons/hr_holidays/hr_holidays.py:445 #, python-format msgid "Request validated." -msgstr "" +msgstr "Anfrage wurde genehmigt" #~ msgid "Set to Draft" #~ msgstr "Setze auf Entwurf" diff --git a/addons/hr_payroll/i18n/de.po b/addons/hr_payroll/i18n/de.po index 7aa18391158..bb2085cff91 100644 --- a/addons/hr_payroll/i18n/de.po +++ b/addons/hr_payroll/i18n/de.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: OpenERP Server 6.0dev\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-12-03 16:02+0000\n" -"PO-Revision-Date: 2012-02-08 09:21+0000\n" -"Last-Translator: Ferdinand @ Camptocamp \n" +"PO-Revision-Date: 2012-12-10 17:42+0000\n" +"Last-Translator: Thorsten Vocks (OpenBig.org) \n" "Language-Team: German \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:45+0000\n" -"X-Generator: Launchpad (build 16335)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:49+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: hr_payroll #: field:hr.payslip.line,condition_select:0 @@ -124,7 +125,7 @@ msgstr "hr.salary.rule" #: view:hr.payslip:0 #: view:hr.payslip.run:0 msgid "to" -msgstr "" +msgstr "an" #. module: hr_payroll #: field:hr.payslip,payslip_run_id:0 @@ -208,7 +209,7 @@ msgstr "Bemerkungen" #: code:addons/hr_payroll/hr_payroll.py:900 #, python-format msgid "Error!" -msgstr "" +msgstr "Fehler !" #. module: hr_payroll #: report:contribution.register.lines:0 @@ -234,7 +235,7 @@ msgstr "Weitere Informationen" #. module: hr_payroll #: field:hr.config.settings,module_hr_payroll_account:0 msgid "Link your payroll to accounting system" -msgstr "" +msgstr "Verbinden Sie Ihre Personalabrechnung mit der Fibu" #. module: hr_payroll #: help:hr.payslip.line,amount_select:0 @@ -303,7 +304,7 @@ msgstr "Struktur" #. module: hr_payroll #: field:hr.contribution.register,partner_id:0 msgid "Partner" -msgstr "" +msgstr "Partner" #. module: hr_payroll #: view:hr.payslip:0 @@ -349,6 +350,18 @@ msgid "" "

\n" " " msgstr "" +"

\n" +" Klicken Sie zur Hinterlegung einer neuen Sozialabgabe.\n" +"

\n" +" Durch Sozialabgaben sind Dritte mit in die Abrechnung von " +"Gehalt und sonstigen Bezügen\n" +" einzubeziehen. Es kann sich um eine Rentenversicherung, " +"Krankenversicherung, öffentliche Einrichtung\n" +" oder Kirche sein, die entsprechende finanzielle Abgaben in " +"die Gehaltsabrechnungen mit einbeziehen \n" +" möchte. \n" +"

\n" +" " #. module: hr_payroll #: help:hr.payslip.line,condition_range_max:0 @@ -436,12 +449,12 @@ msgstr "Prozent basierend auf" #: code:addons/hr_payroll/hr_payroll.py:85 #, python-format msgid "%s (copy)" -msgstr "" +msgstr "%s (Kopie)" #. module: hr_payroll #: help:hr.config.settings,module_hr_payroll_account:0 msgid "Create journal entries from payslips" -msgstr "" +msgstr "Erstelle Buchungen durch Gehaltsabrechnung" #. module: hr_payroll #: field:hr.payslip,paid:0 @@ -464,7 +477,7 @@ msgstr "Lohnkonto Zeilen" #. module: hr_payroll #: view:hr.payslip:0 msgid "Miscellaneous" -msgstr "" +msgstr "Verschiedenes" #. module: hr_payroll #: selection:hr.payslip,state:0 @@ -516,7 +529,7 @@ msgstr "Fester Betrag" #: code:addons/hr_payroll/hr_payroll.py:365 #, python-format msgid "Warning!" -msgstr "" +msgstr "Warnung !" #. module: hr_payroll #: help:hr.payslip.line,active:0 @@ -532,7 +545,7 @@ msgstr "" #: field:hr.payslip,state:0 #: field:hr.payslip.run,state:0 msgid "Status" -msgstr "" +msgstr "Status" #. module: hr_payroll #: view:hr.payslip:0 @@ -625,7 +638,7 @@ msgstr "Lohnkonto Berechnung Details" #: help:hr.payslip.line,appears_on_payslip:0 #: help:hr.salary.rule,appears_on_payslip:0 msgid "Used to display the salary rule on payslip." -msgstr "" +msgstr "Abrechnungsregel soll bei Gehaltsabrechnung mit angezeigt werden." #. module: hr_payroll #: model:ir.model,name:hr_payroll.model_hr_payslip_input @@ -654,7 +667,7 @@ msgstr "Berechnung" #: code:addons/hr_payroll/hr_payroll.py:894 #, python-format msgid "Wrong range condition defined for salary rule %s (%s)." -msgstr "" +msgstr "Falsche Bedingungen für die Bandbreiten der Abrechnungsregel %s (%s)" #. module: hr_payroll #: help:hr.payslip.input,amount:0 @@ -698,6 +711,8 @@ msgstr "Wenn markiert, dann sind alle hier erzeugten Lohnkonten Vergütungen." #, python-format msgid "Wrong percentage base or quantity defined for salary rule %s (%s)." msgstr "" +"Falsche prozentuale Kalkulationsgrundlage oder Menge die für " +"Gehaltsabrechnungsregel %s (%s) angewendet wird." #. module: hr_payroll #: model:ir.actions.act_window,name:hr_payroll.action_view_hr_payroll_structure_list_form @@ -755,7 +770,7 @@ msgstr "Prozent (%)" #: code:addons/hr_payroll/hr_payroll.py:866 #, python-format msgid "Wrong quantity defined for salary rule %s (%s)." -msgstr "" +msgstr "Falsche Menge wurde definiert für diese Abrechnungsregel %s (%s)." #. module: hr_payroll #: view:hr.payslip:0 @@ -841,6 +856,7 @@ msgstr "Arbeitsvertrag" #, python-format msgid "You must select employee(s) to generate payslip(s)." msgstr "" +"Sie müssen Mitarbeiter für die zu erstellende Gehaltsabrechnung wählen" #. module: hr_payroll #: report:paylip.details:0 @@ -919,12 +935,12 @@ msgstr "Reihenfolge" #. module: hr_payroll #: view:hr.payslip:0 msgid "Period" -msgstr "" +msgstr "Periode" #. module: hr_payroll #: view:hr.payslip.run:0 msgid "Period from" -msgstr "" +msgstr "Abrechnungsperiode" #. module: hr_payroll #: view:hr.salary.rule:0 @@ -1093,6 +1109,8 @@ msgstr "Lohnkontozeilen je Beitragskonto" #, python-format msgid "You cannot delete a payslip which is not draft or cancelled!" msgstr "" +"Sie können keine Personalabrechnung stornieren, die nicht mehr im Entwurf " +"ist oder bereits abgebrochen wurde." #. module: hr_payroll #: report:paylip.details:0 @@ -1191,7 +1209,7 @@ msgstr "Lohnkonto Eingaben" #. module: hr_payroll #: view:hr.payslip:0 msgid "Other Inputs" -msgstr "" +msgstr "Andere Eingaben" #. module: hr_payroll #: model:ir.actions.act_window,name:hr_payroll.action_hr_salary_rule_category_tree_view @@ -1266,7 +1284,7 @@ msgstr "Lohnkonto Bezeichnung" #. module: hr_payroll #: view:hr.payslip:0 msgid "Accounting" -msgstr "" +msgstr "Finanzbuchhaltung" #. module: hr_payroll #: field:hr.payslip.line,condition_range:0 diff --git a/addons/hr_recruitment/i18n/de.po b/addons/hr_recruitment/i18n/de.po index f7ab1e73445..0e07e03704c 100644 --- a/addons/hr_recruitment/i18n/de.po +++ b/addons/hr_recruitment/i18n/de.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: OpenERP Server 6.0dev\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-12-03 16:02+0000\n" -"PO-Revision-Date: 2012-02-08 09:22+0000\n" -"Last-Translator: Ferdinand @ Camptocamp \n" +"PO-Revision-Date: 2012-12-10 20:45+0000\n" +"Last-Translator: Thorsten Vocks (OpenBig.org) \n" "Language-Team: German \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:48+0000\n" -"X-Generator: Launchpad (build 16335)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:49+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: hr_recruitment #: help:hr.applicant,active:0 @@ -34,17 +35,17 @@ msgstr "Anforderungen" #. module: hr_recruitment #: view:hr.applicant:0 msgid "Application Summary" -msgstr "" +msgstr "Zusammenfassung Bewerbung" #. module: hr_recruitment #: view:hr.applicant:0 msgid "Start Interview" -msgstr "" +msgstr "Erstes Bewerberinterview" #. module: hr_recruitment #: view:hr.applicant:0 msgid "Mobile:" -msgstr "" +msgstr "Mobile:" #. module: hr_recruitment #: help:hr.recruitment.stage,fold:0 @@ -52,6 +53,8 @@ msgid "" "This stage is not visible, for example in status bar or kanban view, when " "there are no records in that stage to display." msgstr "" +"Die Stufe ist unsichtbar, z.B. in der Status Anzeige oder Kanban Ansicht, " +"wenn es keine Datensätze in dieser Stufe gibt." #. module: hr_recruitment #: model:hr.recruitment.degree,name:hr_recruitment.degree_graduate @@ -100,7 +103,7 @@ msgstr "Ausgeschriebene Jobs" #: view:hr.applicant:0 #: field:hr.applicant,message_unread:0 msgid "Unread Messages" -msgstr "" +msgstr "Ungelesene Mitteilungen" #. module: hr_recruitment #: field:hr.applicant,company_id:0 @@ -153,12 +156,30 @@ msgid "" "

\n" " " msgstr "" +"

\n" +" Klicken Sie zur Eingabe einer neuen Bewerbung\n" +"

\n" +" OpenERP hilft Ihnen bei der Erfassung und Verfolgung von " +"Bewerbungen:\n" +" Termine mit Bewerbern, Vorstellungsgespräche, Assessments " +"u.s.w.\n" +"

\n" +" Durch die Installation des EMail Gateway, können EMail " +"Bewerbungen mit angehängten\n" +" Lebensläufen und Zeugnisse an z.B. jobs@ihrefirma.com " +"automatisch übernommen werden.\n" +" Wenn Sie das Dokumenten Management aktivieren, werden die " +"Zusammenfassungen der\n" +" Bewerbungen indiziert und ermöglichen auf diesem Weg eine " +"einfache Volltextsuche.\n" +"

\n" +" " #. module: hr_recruitment #: model:ir.actions.act_window,name:hr_recruitment.crm_case_categ0_act_job #: model:ir.ui.menu,name:hr_recruitment.menu_crm_case_categ0_act_job msgid "Applications" -msgstr "" +msgstr "Bewerbungen" #. module: hr_recruitment #: field:hr.applicant,day_open:0 @@ -168,12 +189,12 @@ msgstr "Tage b. Eröffnung" #. module: hr_recruitment #: field:hr.applicant,emp_id:0 msgid "employee" -msgstr "" +msgstr "Mitarbeiter" #. module: hr_recruitment #: field:hr.config.settings,fetchmail_applicants:0 msgid "Create applicants from an incoming email account" -msgstr "" +msgstr "Erstelle Bewerbungen aus Posteingang" #. module: hr_recruitment #: view:hr.recruitment.report:0 @@ -185,7 +206,7 @@ msgstr "Tag" #: view:hr.recruitment.partner.create:0 #: model:ir.actions.act_window,name:hr_recruitment.action_hr_recruitment_partner_create msgid "Create Contact" -msgstr "" +msgstr "Erstelle Kontakt" #. module: hr_recruitment #: view:hr.applicant:0 @@ -217,7 +238,7 @@ msgstr "Nächste Schritte" #: code:addons/hr_recruitment/wizard/hr_recruitment_create_partner_job.py:56 #, python-format msgid "Error!" -msgstr "" +msgstr "Fehler !" #. module: hr_recruitment #: model:hr.recruitment.degree,name:hr_recruitment.degree_bac5 @@ -236,11 +257,14 @@ msgid "" "Stages of the recruitment process may be different per department. If this " "stage is common to all departments, keep this field empty." msgstr "" +"Die Stufen bei einem Einstellungsverfahren können sich zwischen Abteilungen " +"unterscheiden. Wenn diese Stufe bei allen Abteilungen benötigt wird, tragen " +"Sie hier einfach keinen Wert ein." #. module: hr_recruitment #: help:hr.applicant,message_unread:0 msgid "If checked new messages require your attention." -msgstr "" +msgstr "Wenn aktiviert, erfordern neue Nachrichten Ihr Handeln" #. module: hr_recruitment #: field:hr.applicant,color:0 @@ -250,7 +274,7 @@ msgstr "Farb Index" #. module: hr_recruitment #: model:ir.actions.act_window,name:hr_recruitment.act_hr_applicant_to_meeting msgid "Meetings" -msgstr "" +msgstr "Meetings" #. module: hr_recruitment #: view:hr.applicant:0 @@ -288,6 +312,9 @@ msgid "" "Holds the Chatter summary (number of messages, ...). This summary is " "directly in html format in order to be inserted in kanban views." msgstr "" +"Hier finden Sie die Nachrichtenübersicht (Anzahl Nachrichten etc., ...) im " +"html Format, um über dieses Format dann später in einer Kanban Ansicht " +"weiterzuarbeiten." #. module: hr_recruitment #: code:addons/hr_recruitment/hr_recruitment.py:424 @@ -303,7 +330,7 @@ msgstr "Gehaltsvorschlag" #. module: hr_recruitment #: view:hr.applicant:0 msgid "Edit..." -msgstr "" +msgstr "Bearbeiten ..." #. module: hr_recruitment #: view:hr.recruitment.report:0 @@ -327,7 +354,7 @@ msgstr "Gehaltsvorschlag" #: code:addons/hr_recruitment/hr_recruitment.py:482 #, python-format msgid "Applicant has been set in progress." -msgstr "" +msgstr "Diese Bewerbung wurde geändert auf in Bearbeitung." #. module: hr_recruitment #: view:hr.recruitment.partner.create:0 @@ -342,7 +369,7 @@ msgstr "Personaleinstellungen Statistik" #. module: hr_recruitment #: view:hr.applicant:0 msgid "Print interview report" -msgstr "" +msgstr "Drucke Interview" #. module: hr_recruitment #: view:hr.recruitment.report:0 @@ -357,13 +384,13 @@ msgstr "Stellenbeschreibung" #. module: hr_recruitment #: model:ir.actions.server,name:hr_recruitment.actions_server_applicant_read msgid "HR Applicant: Mark read" -msgstr "" +msgstr "Personal Bewerbung: Markiere als gelesen" #. module: hr_recruitment #: code:addons/hr_recruitment/hr_recruitment.py:504 #, python-format msgid "Applicant has been set as new." -msgstr "" +msgstr "Bewerbung ist Neu." #. module: hr_recruitment #: view:hr.applicant:0 @@ -392,6 +419,17 @@ msgid "" "

\n" " " msgstr "" +"

\n" +" Klicken Sie, um eine neue Stufe im Einstellungsprozess " +"anzulegen.\n" +"

\n" +" Definieren Sie an dieser Stelle die Stufen des " +"Einstellungsprozess, z.B.:\n" +" Erste Bewerberqualifizierung, erstes Bewerberinterview, zweites " +"Vorstellungsgespräch,\n" +" Absage an Bewerber, eingestellt.\n" +"

\n" +" " #. module: hr_recruitment #: view:hr.recruitment.report:0 @@ -409,6 +447,8 @@ msgid "" "Email alias for this job position. New emails will automatically create new " "applicants for this job position." msgstr "" +"Email Alias für diesen Stelle.Neue eingehende EMails erstellen dann " +"automatisch neue Bewerbungen für die ausgeschriebene Stelle." #. module: hr_recruitment #: selection:hr.applicant,priority:0 @@ -560,7 +600,7 @@ msgstr "In Bearbeitung" #. module: hr_recruitment #: view:hr.applicant:0 msgid "Hire & Create Employee" -msgstr "" +msgstr "Einstellen & Erstellen Mitarbeiter" #. module: hr_recruitment #: view:hr.applicant:0 @@ -575,7 +615,7 @@ msgstr "Wahrscheinlichkeit" #. module: hr_recruitment #: field:hr.job,alias_id:0 msgid "Alias" -msgstr "" +msgstr "Alias" #. module: hr_recruitment #: selection:hr.recruitment.report,month:0 @@ -591,17 +631,17 @@ msgstr "Dezember" #: code:addons/hr_recruitment/wizard/hr_recruitment_create_partner_job.py:39 #, python-format msgid "A contact is already defined on this job request." -msgstr "" +msgstr "Ein Kontakt wurde bereits für diese Stellenausschreibung definiert." #. module: hr_recruitment #: field:hr.applicant,categ_ids:0 msgid "Tags" -msgstr "" +msgstr "Tags" #. module: hr_recruitment #: model:ir.model,name:hr_recruitment.model_hr_applicant_category msgid "Category of applicant" -msgstr "" +msgstr "Kategorien Bewerber" #. module: hr_recruitment #: view:hr.recruitment.report:0 @@ -612,7 +652,7 @@ msgstr "Monat" #. module: hr_recruitment #: view:hr.applicant:0 msgid "Answer related job question" -msgstr "" +msgstr "Beantworte das Bewerberinterview" #. module: hr_recruitment #: model:ir.model,name:hr_recruitment.model_hr_recruitment_degree @@ -639,7 +679,7 @@ msgstr "Betreff" #: view:hired.employee:0 #: view:hr.recruitment.partner.create:0 msgid "or" -msgstr "" +msgstr "oder" #. module: hr_recruitment #: view:hr.applicant:0 @@ -749,7 +789,7 @@ msgstr "Status" #. module: hr_recruitment #: view:hr.applicant:0 msgid "Schedule interview with this applicant" -msgstr "" +msgstr "Terminiere ein Interview mit Bewerber" #. module: hr_recruitment #: selection:hr.recruitment.report,month:0 @@ -769,7 +809,7 @@ msgstr "Abschluss" #. module: hr_recruitment #: model:ir.actions.server,name:hr_recruitment.actions_server_applicant_unread msgid "HR Applicant: Mark unread" -msgstr "" +msgstr "Personal Bewerber: Markiere als ungelesen" #. module: hr_recruitment #: selection:hr.recruitment.report,month:0 @@ -785,6 +825,11 @@ msgid "" "the case needs to be reviewed then the status is set " "to 'Pending'." msgstr "" +"Der Status wird auf 'Entwurf' geändert, wenn der Vorfall gespeichert wurde. " +"Durch die Vorgangsbearbeitung ändert sich der Status auf 'In Bearbeitung'. " +"Durch Abschluss wird der Status auf 'Erledigt' geändert. Wenn der Vorfall " +"eine weitere Bearbeitung zu einem späteren Zeitpunkt erfordert, ändert man " +"den Status am Besten auf 'Wiedervorlage'." #. module: hr_recruitment #: field:hr.applicant,day_close:0 @@ -794,7 +839,7 @@ msgstr "Tage f. Beend." #. module: hr_recruitment #: field:hr.applicant,message_is_follower:0 msgid "Is a Follower" -msgstr "" +msgstr "Ist bereits Follower" #. module: hr_recruitment #: field:hr.recruitment.report,user_id:0 @@ -816,7 +861,7 @@ msgstr "Aktiv" #: view:hr.recruitment.report:0 #: field:hr.recruitment.report,nbr:0 msgid "# of Applications" -msgstr "" +msgstr "Anzahl Bewerbungen" #. module: hr_recruitment #: model:ir.actions.act_window,help:hr_recruitment.hr_recruitment_stage_act @@ -830,6 +875,14 @@ msgid "" "

\n" " " msgstr "" +"

\n" +" Klicken Sie für eine neue Stufe im Einstellungsprozess.\n" +"

\n" +" Vergessen Sie nicht die Spezifikation der Abteilung, wenn Ihr " +"Einstellungsverfahren abhängt\n" +" von der Stellenausschreibung.\n" +"

\n" +" " #. module: hr_recruitment #: field:hr.applicant,response:0 @@ -840,7 +893,7 @@ msgstr "Rückmeldung" #: field:hr.applicant,message_comment_ids:0 #: help:hr.applicant,message_comment_ids:0 msgid "Comments and emails" -msgstr "" +msgstr "Kommentare und EMails" #. module: hr_recruitment #: selection:hr.recruitment.report,month:0 @@ -850,7 +903,7 @@ msgstr "Oktober" #. module: hr_recruitment #: field:hr.config.settings,module_document_ftp:0 msgid "Allow the automatic indexation of resumes" -msgstr "" +msgstr "Ermöglicht die automatische Indizierung der Zusammenfassung" #. module: hr_recruitment #: field:hr.applicant,salary_proposed_extra:0 @@ -860,7 +913,7 @@ msgstr "Vorgeschlagene ausserordentliche Bezüge" #. module: hr_recruitment #: model:mail.message.subtype,name:hr_recruitment.mt_hired msgid "Employee Hired" -msgstr "" +msgstr "Angestellter wurde eingestellt" #. module: hr_recruitment #: selection:hr.recruitment.report,month:0 @@ -871,23 +924,23 @@ msgstr "Januar" #: code:addons/hr_recruitment/wizard/hr_recruitment_create_partner_job.py:56 #, python-format msgid "A contact is already existing with the same name." -msgstr "" +msgstr "Ein Kontakt existiert bereits unter einem anderen Namen" #. module: hr_recruitment #: code:addons/hr_recruitment/hr_recruitment.py:495 #, python-format msgid "Applicant has been hired." -msgstr "" +msgstr "Der Bewerber wurde letzlich eingestellt" #. module: hr_recruitment #: view:hr.applicant:0 msgid "Contact:" -msgstr "" +msgstr "Kontakt:" #. module: hr_recruitment #: field:hr.applicant,message_summary:0 msgid "Summary" -msgstr "" +msgstr "Zusammenfassung" #. module: hr_recruitment #: field:hr.applicant,date:0 @@ -908,7 +961,7 @@ msgstr "Wollen Sie einem Mitarbeiter anlegen" #. module: hr_recruitment #: view:hr.applicant:0 msgid "Degree:" -msgstr "" +msgstr "Grad:" #. module: hr_recruitment #: view:hr.recruitment.report:0 @@ -929,13 +982,14 @@ msgstr "" #. module: hr_recruitment #: view:hr.config.settings:0 msgid "Configure" -msgstr "" +msgstr "Einstellungen" #. module: hr_recruitment #: code:addons/hr_recruitment/hr_recruitment.py:492 #, python-format msgid "Applicant has been hired and created as an employee." msgstr "" +"Bewerber wurde eingestellt und als neuer Mitarbeiter angelegt." #. module: hr_recruitment #: view:hr.applicant:0 @@ -973,12 +1027,14 @@ msgstr "Abbrechen" #. module: hr_recruitment #: field:hr.applicant,message_follower_ids:0 msgid "Followers" -msgstr "" +msgstr "Followers" #. module: hr_recruitment #: view:hr.recruitment.partner.create:0 msgid "Are you sure you want to create a contact based on this job request ?" msgstr "" +"Sind Sie sicher, dass Sie einen neuen Kontakt auf der Historie Ihrer " +"Jobanfrage haben." #. module: hr_recruitment #: help:hr.config.settings,fetchmail_applicants:0 @@ -987,6 +1043,10 @@ msgid "" "(jobs@mycompany.com),\n" " and create automatically application documents in the system." msgstr "" +"Ermöglicht den automatischen Posteingang durch Empfang der Bewerbungen unter " +"einer Adresse (jobs@mycompany.com),\n" +" und erstellt dabei automatisch Anwendungsdokumente im " +"System." #. module: hr_recruitment #: view:hr.applicant:0 @@ -1034,7 +1094,7 @@ msgstr "LinkedIn" #. module: hr_recruitment #: model:mail.message.subtype,name:hr_recruitment.mt_applicant_new msgid "New Applicant" -msgstr "" +msgstr "Neue Bewerbung" #. module: hr_recruitment #: model:ir.model,name:hr_recruitment.model_hr_recruitment_stage @@ -1071,12 +1131,12 @@ msgstr "Bezeichnung der Herkunft" #: code:addons/hr_recruitment/hr_recruitment.py:476 #, python-format msgid "Stage changed to %s." -msgstr "" +msgstr "Stufe wurde geändert auf %s" #. module: hr_recruitment #: view:hr.applicant:0 msgid "Day(s)" -msgstr "" +msgstr "Tag(e)" #. module: hr_recruitment #: field:hr.applicant,description:0 @@ -1106,7 +1166,7 @@ msgstr "Mundpropaganda" #. module: hr_recruitment #: field:hr.recruitment.stage,fold:0 msgid "Hide in views if empty" -msgstr "" +msgstr "In Ansichten ausblenden" #. module: hr_recruitment #: help:hr.config.settings,module_document_ftp:0 @@ -1116,6 +1176,10 @@ msgid "" "knowledge management module in order to allow you to search using specific " "keywords through the content of all documents (PDF, .DOCx...)" msgstr "" +"Verwalten Sie Lebensläufe und Anschreiben der Bewerber. \n" +" Es wird das Modul document_ftp installiert. Dadurch wird das " +"Wissensmanagement Modul installiert, um z.B. per Volltextsuche (PDF, " +".DOCx...) die indizierten Dokumente zu durchsuchen." #. module: hr_recruitment #: selection:hr.applicant,state:0 @@ -1136,12 +1200,12 @@ msgstr "Eingestellt" #. module: hr_recruitment #: field:hr.applicant,reference:0 msgid "Referred By" -msgstr "" +msgstr "Vermittelt durch" #. module: hr_recruitment #: view:hr.applicant:0 msgid "Departement:" -msgstr "" +msgstr "Abteilung:" #. module: hr_recruitment #: selection:hr.applicant,priority:0 @@ -1157,7 +1221,7 @@ msgstr "Erstes Bewerberinterview" #. module: hr_recruitment #: field:hr.recruitment.report,salary_prop_avg:0 msgid "Avg. Proposed Salary" -msgstr "" +msgstr "Durchschnittl. Gehalt" #. module: hr_recruitment #: view:hr.applicant:0 @@ -1190,12 +1254,12 @@ msgstr "November" #. module: hr_recruitment #: field:hr.recruitment.report,salary_exp_avg:0 msgid "Avg. Expected Salary" -msgstr "" +msgstr "Durchschnittl. erwartetes Gehalt" #. module: hr_recruitment #: view:hr.recruitment.report:0 msgid "Avg Expected Salary" -msgstr "" +msgstr "Erwartetes Gehalt im Durchschnitt" #. module: hr_recruitment #: model:ir.model,name:hr_recruitment.model_hr_recruitment_partner_create @@ -1220,12 +1284,12 @@ msgstr "Wartende Einstellungen" #. module: hr_recruitment #: view:hr.applicant:0 msgid "Contract" -msgstr "" +msgstr "Vertrag" #. module: hr_recruitment #: help:hr.applicant,message_ids:0 msgid "Messages and communication history" -msgstr "" +msgstr "Nachrichten und Kommunikations-Historie" #. module: hr_recruitment #: model:hr.recruitment.source,name:hr_recruitment.source_monster @@ -1252,7 +1316,7 @@ msgstr "Reihenfolge" #: code:addons/hr_recruitment/hr_recruitment.py:490 #, python-format msgid "New employee joined the company %s." -msgstr "" +msgstr "Neuer Mitarbeiter in Unternehmen %s eingestellt." #. module: hr_recruitment #: model:hr.recruitment.degree,name:hr_recruitment.degree_bachelor @@ -1268,7 +1332,7 @@ msgstr "" #: code:addons/hr_recruitment/hr_recruitment.py:508 #, python-format msgid "Applicant has been created." -msgstr "" +msgstr "Bewerbung wurde erstellt" #. module: hr_recruitment #: help:hr.recruitment.stage,state:0 @@ -1278,6 +1342,10 @@ msgid "" "related to the status 'Close', when your document reach this stage, it will " "be automatically closed." msgstr "" +"Der Statusbezug für die Stufe. Dieser ändert sich automatisch in Bezug auf " +"die ausgewählte Stufe. Zum Beispiel eine Stufe, die auf den Status " +"'Abgeschlossen' referenziert, wird automatisch beendet wenn diese Stufe " +"tatsächlich hinterlegt wird." #. module: hr_recruitment #: help:hr.applicant,salary_proposed_extra:0 @@ -1297,12 +1365,12 @@ msgstr "Offen" #. module: hr_recruitment #: view:board.board:0 msgid "Applications to be Processed" -msgstr "" +msgstr "Zu bearbeitende Bewerbungen" #. module: hr_recruitment #: view:hr.applicant:0 msgid "Schedule Interview" -msgstr "" +msgstr "Terminiere Interview" #~ msgid "# of Cases" #~ msgstr "# Fälle" diff --git a/addons/hr_timesheet/i18n/hr.po b/addons/hr_timesheet/i18n/hr.po index 3de67c9d0ed..1083bdc1e61 100644 --- a/addons/hr_timesheet/i18n/hr.po +++ b/addons/hr_timesheet/i18n/hr.po @@ -7,14 +7,14 @@ msgstr "" "Project-Id-Version: OpenERP Server 6.0dev\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-12-03 16:02+0000\n" -"PO-Revision-Date: 2010-10-30 10:48+0000\n" -"Last-Translator: OpenERP Administrators \n" +"PO-Revision-Date: 2012-12-10 08:24+0000\n" +"Last-Translator: Goran Kliska \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:33+0000\n" -"X-Generator: Launchpad (build 16335)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:48+0000\n" +"X-Generator: Launchpad (build 16356)\n" "Language: hr\n" #. module: hr_timesheet @@ -48,7 +48,7 @@ msgstr "" #: code:addons/hr_timesheet/report/users_timesheet.py:77 #, python-format msgid "Wed" -msgstr "" +msgstr "Sri" #. module: hr_timesheet #: view:hr.sign.out.project:0 @@ -58,7 +58,7 @@ msgstr "(Treba ostati prazno za current_time)" #. module: hr_timesheet #: view:hr.analytic.timesheet:0 msgid "Group By..." -msgstr "" +msgstr "Grupiraj po..." #. module: hr_timesheet #: model:ir.actions.act_window,help:hr_timesheet.action_hr_timesheet_sign_in @@ -72,7 +72,7 @@ msgstr "" #. module: hr_timesheet #: field:hr.employee,uom_id:0 msgid "Unit of Measure" -msgstr "" +msgstr "Jedinica mjere" #. module: hr_timesheet #: field:hr.employee,journal_id:0 @@ -107,7 +107,7 @@ msgstr "" #: code:addons/hr_timesheet/report/users_timesheet.py:77 #, python-format msgid "Mon" -msgstr "" +msgstr "Pon" #. module: hr_timesheet #: view:hr.sign.in.project:0 @@ -119,7 +119,7 @@ msgstr "Prijava" #: code:addons/hr_timesheet/report/users_timesheet.py:77 #, python-format msgid "Fri" -msgstr "" +msgstr "Pet" #. module: hr_timesheet #: view:hr.analytic.timesheet:0 @@ -147,7 +147,7 @@ msgstr "Posao obavljen u zadnjem razdoblju" #: field:hr.sign.in.project,name:0 #: field:hr.sign.out.project,name:0 msgid "Employees name" -msgstr "" +msgstr "Imena zaposlenih" #. module: hr_timesheet #: field:hr.sign.out.project,account_id:0 @@ -168,14 +168,14 @@ msgstr "" #. module: hr_timesheet #: model:ir.actions.act_window,name:hr_timesheet.act_analytic_cost_revenue msgid "Costs & Revenues" -msgstr "" +msgstr "Troškovi i prihodi" #. module: hr_timesheet #: code:addons/hr_timesheet/report/user_timesheet.py:44 #: code:addons/hr_timesheet/report/users_timesheet.py:77 #, python-format msgid "Tue" -msgstr "" +msgstr "Uto" #. module: hr_timesheet #: model:ir.model,name:hr_timesheet.model_account_analytic_account @@ -185,7 +185,7 @@ msgstr "Analitički Konto" #. module: hr_timesheet #: view:account.analytic.account:0 msgid "Costs and Revenues" -msgstr "" +msgstr "Troškovi i prihodi" #. module: hr_timesheet #: code:addons/hr_timesheet/hr_timesheet.py:141 @@ -195,26 +195,26 @@ msgstr "" #: code:addons/hr_timesheet/wizard/hr_timesheet_print_employee.py:43 #, python-format msgid "Warning!" -msgstr "" +msgstr "Upozorenje!" #. module: hr_timesheet #: field:hr.analytic.timesheet,partner_id:0 msgid "Partner" -msgstr "" +msgstr "Partner" #. module: hr_timesheet #: code:addons/hr_timesheet/report/user_timesheet.py:44 #: code:addons/hr_timesheet/report/users_timesheet.py:77 #, python-format msgid "Sat" -msgstr "" +msgstr "Sub" #. module: hr_timesheet #: code:addons/hr_timesheet/report/user_timesheet.py:44 #: code:addons/hr_timesheet/report/users_timesheet.py:77 #, python-format msgid "Sun" -msgstr "" +msgstr "Ned" #. module: hr_timesheet #: xsl:hr.analytical.timesheet:0 @@ -224,7 +224,7 @@ msgstr "Zbroj" #. module: hr_timesheet #: view:hr.analytic.timesheet:0 msgid "Analytic account" -msgstr "" +msgstr "Analitički konto" #. module: hr_timesheet #: model:ir.actions.act_window,help:hr_timesheet.act_hr_timesheet_line_evry1_all_form @@ -265,7 +265,7 @@ msgstr "" #: selection:hr.analytical.timesheet.users,month:0 #, python-format msgid "July" -msgstr "" +msgstr "Srpanj" #. module: hr_timesheet #: field:hr.sign.in.project,date:0 @@ -312,7 +312,7 @@ msgstr "" #: selection:hr.analytical.timesheet.users,month:0 #, python-format msgid "September" -msgstr "" +msgstr "Rujan" #. module: hr_timesheet #: code:addons/hr_timesheet/report/user_timesheet.py:41 @@ -321,7 +321,7 @@ msgstr "" #: selection:hr.analytical.timesheet.users,month:0 #, python-format msgid "December" -msgstr "" +msgstr "Prosinac" #. module: hr_timesheet #: field:hr.analytical.timesheet.users,employee_ids:0 @@ -345,7 +345,7 @@ msgstr "Opis Posla" #: view:hr.sign.in.project:0 #: view:hr.sign.out.project:0 msgid "or" -msgstr "" +msgstr "ili" #. module: hr_timesheet #: xsl:hr.analytical.timesheet:0 @@ -395,7 +395,7 @@ msgstr "" #. module: hr_timesheet #: field:hr.analytic.timesheet,line_id:0 msgid "Analytic Line" -msgstr "" +msgstr "Stavka analitike" #. module: hr_timesheet #: code:addons/hr_timesheet/report/user_timesheet.py:41 @@ -404,7 +404,7 @@ msgstr "" #: selection:hr.analytical.timesheet.users,month:0 #, python-format msgid "August" -msgstr "" +msgstr "Kolovoz" #. module: hr_timesheet #: code:addons/hr_timesheet/hr_timesheet.py:146 @@ -421,7 +421,7 @@ msgstr "" #: selection:hr.analytical.timesheet.users,month:0 #, python-format msgid "June" -msgstr "" +msgstr "Lipanj" #. module: hr_timesheet #: field:hr.sign.in.project,state:0 @@ -441,7 +441,7 @@ msgstr "Datum" #: selection:hr.analytical.timesheet.users,month:0 #, python-format msgid "November" -msgstr "" +msgstr "Studeni" #. module: hr_timesheet #: field:hr.sign.out.project,date:0 @@ -455,7 +455,7 @@ msgstr "Datum zaključenja" #: selection:hr.analytical.timesheet.users,month:0 #, python-format msgid "October" -msgstr "" +msgstr "Listopad" #. module: hr_timesheet #: code:addons/hr_timesheet/report/user_timesheet.py:41 @@ -464,14 +464,14 @@ msgstr "" #: selection:hr.analytical.timesheet.users,month:0 #, python-format msgid "January" -msgstr "" +msgstr "Siječanj" #. module: hr_timesheet #: code:addons/hr_timesheet/report/user_timesheet.py:44 #: code:addons/hr_timesheet/report/users_timesheet.py:77 #, python-format msgid "Thu" -msgstr "" +msgstr "Čet" #. module: hr_timesheet #: view:hr.sign.in.project:0 @@ -493,7 +493,7 @@ msgstr "" #. module: hr_timesheet #: view:hr.analytical.timesheet.users:0 msgid "Period" -msgstr "" +msgstr "Period" #. module: hr_timesheet #: view:hr.sign.out.project:0 @@ -519,13 +519,13 @@ msgstr "Kontrolna kartica Djelatnika" #. module: hr_timesheet #: view:hr.analytic.timesheet:0 msgid "Information" -msgstr "" +msgstr "Informacija" #. module: hr_timesheet #: field:hr.analytical.timesheet.employee,employee_id:0 #: model:ir.model,name:hr_timesheet.model_hr_employee msgid "Employee" -msgstr "" +msgstr "Radnik" #. module: hr_timesheet #: view:hr.sign.in.project:0 @@ -565,7 +565,7 @@ msgstr "Korisnici" #: selection:hr.analytical.timesheet.users,month:0 #, python-format msgid "May" -msgstr "" +msgstr "Svibanj" #. module: hr_timesheet #: view:hr.analytic.timesheet:0 @@ -589,7 +589,7 @@ msgstr "" #: selection:hr.analytical.timesheet.users,month:0 #, python-format msgid "February" -msgstr "" +msgstr "Veljača" #. module: hr_timesheet #: model:ir.model,name:hr_timesheet.model_hr_sign_out_project @@ -607,7 +607,7 @@ msgstr "" #. module: hr_timesheet #: view:hr.analytical.timesheet.users:0 msgid "Employees" -msgstr "" +msgstr "Radnici" #. module: hr_timesheet #: code:addons/hr_timesheet/report/user_timesheet.py:41 @@ -616,7 +616,7 @@ msgstr "" #: selection:hr.analytical.timesheet.users,month:0 #, python-format msgid "March" -msgstr "" +msgstr "Ožujak" #. module: hr_timesheet #: code:addons/hr_timesheet/report/user_timesheet.py:41 diff --git a/addons/hr_timesheet/i18n/pt_BR.po b/addons/hr_timesheet/i18n/pt_BR.po index 3879e32e121..5d92fcc5208 100644 --- a/addons/hr_timesheet/i18n/pt_BR.po +++ b/addons/hr_timesheet/i18n/pt_BR.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: pt_BR\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-12-03 16:02+0000\n" -"PO-Revision-Date: 2012-02-19 15:10+0000\n" -"Last-Translator: Rafael Sales - http://www.tompast.com.br \n" +"PO-Revision-Date: 2012-12-10 17:44+0000\n" +"Last-Translator: Joe Pimentel \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:34+0000\n" -"X-Generator: Launchpad (build 16335)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:48+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: hr_timesheet #: model:ir.actions.act_window,help:hr_timesheet.act_analytic_cost_revenue @@ -223,7 +223,7 @@ msgstr "Dom" #. module: hr_timesheet #: xsl:hr.analytical.timesheet:0 msgid "Sum" -msgstr "" +msgstr "Soma" #. module: hr_timesheet #: view:hr.analytic.timesheet:0 @@ -671,7 +671,7 @@ msgstr "Contabilidade" #: xsl:hr.analytical.timesheet:0 #: xsl:hr.analytical.timesheet_users:0 msgid "Total" -msgstr "" +msgstr "Total" #. module: hr_timesheet #: view:hr.sign.out.project:0 diff --git a/addons/hr_timesheet_invoice/i18n/de.po b/addons/hr_timesheet_invoice/i18n/de.po index 2440a38bddf..9a162fec297 100644 --- a/addons/hr_timesheet_invoice/i18n/de.po +++ b/addons/hr_timesheet_invoice/i18n/de.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: OpenERP Server 6.0dev\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-12-03 16:03+0000\n" -"PO-Revision-Date: 2012-12-09 21:46+0000\n" -"Last-Translator: Felix Schubert \n" +"PO-Revision-Date: 2012-12-10 20:49+0000\n" +"Last-Translator: Thorsten Vocks (OpenBig.org) \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-10 04:38+0000\n" -"X-Generator: Launchpad (build 16341)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:48+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: hr_timesheet_invoice #: view:report.timesheet.line:0 @@ -858,12 +859,12 @@ msgstr "" #: code:addons/hr_timesheet_invoice/hr_timesheet_invoice.py:110 #, python-format msgid "Contract has been opened." -msgstr "" +msgstr "Vertrag wurde geöffnet." #. module: hr_timesheet_invoice #: field:hr.timesheet.invoice.create.final,time:0 msgid "Time Spent" -msgstr "" +msgstr "Geleistete Stunden" #. module: hr_timesheet_invoice #: help:account.analytic.account,amount_max:0 @@ -874,6 +875,7 @@ msgstr "Leer lassen, wenn der Vertrag keinen fixierten Preis beinhaltet." #: view:hr.timesheet.invoice.create.final:0 msgid "Do you want to show details of each activity to your customer?" msgstr "" +"Möchten Sie die Details zu den einzelnen Aktivitäten Ihrer Kunden einsehen ?" #. module: hr_timesheet_invoice #: view:report_timesheet.invoice:0 @@ -902,7 +904,7 @@ msgstr "Kunde" #. module: hr_timesheet_invoice #: view:report.account.analytic.line.to.invoice:0 msgid "Analytic Lines" -msgstr "" +msgstr "Kostenstellen Buchungen" #. module: hr_timesheet_invoice #: view:report_timesheet.account.date:0 @@ -940,6 +942,8 @@ msgid "" "There is no product defined. Please select one or force the product through " "the wizard." msgstr "" +"Es ist kein Produkt definiert, bitte wählen Sie eins aus oder definieren Sie " +"ein Neues durch den Assistenten." #. module: hr_timesheet_invoice #: help:hr_timesheet_invoice.factor,factor:0 @@ -972,14 +976,14 @@ msgstr "Analytische Buchungen zur Abrechnung" #. module: hr_timesheet_invoice #: model:hr_timesheet_invoice.factor,name:hr_timesheet_invoice.timesheet_invoice_factor4 msgid "80%" -msgstr "" +msgstr "80 %" #. module: hr_timesheet_invoice #: view:hr.timesheet.analytic.profit:0 #: view:hr.timesheet.invoice.create:0 #: view:hr.timesheet.invoice.create.final:0 msgid "or" -msgstr "" +msgstr "oder" #. module: hr_timesheet_invoice #: field:report_timesheet.invoice,manager_id:0 @@ -1009,7 +1013,7 @@ msgstr "Jahr" #. module: hr_timesheet_invoice #: view:hr.timesheet.analytic.profit:0 msgid "Duration" -msgstr "" +msgstr "Dauer" #~ msgid "%" #~ msgstr "%" diff --git a/addons/hr_timesheet_invoice/i18n/pt_BR.po b/addons/hr_timesheet_invoice/i18n/pt_BR.po index a62a7f65761..6bf67d95d35 100644 --- a/addons/hr_timesheet_invoice/i18n/pt_BR.po +++ b/addons/hr_timesheet_invoice/i18n/pt_BR.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: pt_BR\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-12-03 16:03+0000\n" -"PO-Revision-Date: 2012-02-19 15:16+0000\n" -"Last-Translator: Joe Pimentel \n" +"PO-Revision-Date: 2012-12-10 17:43+0000\n" +"Last-Translator: Projetaty Soluções OpenSource \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:36+0000\n" -"X-Generator: Launchpad (build 16335)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:48+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: hr_timesheet_invoice #: view:report.timesheet.line:0 @@ -512,7 +512,7 @@ msgstr "Total faturado" #. module: hr_timesheet_invoice #: field:report.analytic.account.close,state:0 msgid "Status" -msgstr "" +msgstr "Situação" #. module: hr_timesheet_invoice #: model:ir.model,name:hr_timesheet_invoice.model_account_analytic_line @@ -803,7 +803,7 @@ msgstr "Tipos de faturamento" #. module: hr_timesheet_invoice #: view:hr.analytic.timesheet:0 msgid "Invoicing" -msgstr "" +msgstr "Emissão de Fatura" #. module: hr_timesheet_invoice #: selection:report.account.analytic.line.to.invoice,month:0 diff --git a/addons/hr_timesheet_sheet/i18n/de.po b/addons/hr_timesheet_sheet/i18n/de.po index 5c20d11e3c2..b1a0fb5c0f4 100644 --- a/addons/hr_timesheet_sheet/i18n/de.po +++ b/addons/hr_timesheet_sheet/i18n/de.po @@ -7,14 +7,15 @@ msgstr "" "Project-Id-Version: OpenERP Server 6.0dev\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-12-03 16:02+0000\n" -"PO-Revision-Date: 2012-02-08 09:25+0000\n" -"Last-Translator: Ferdinand @ Camptocamp \n" +"PO-Revision-Date: 2012-12-10 21:42+0000\n" +"Last-Translator: Thorsten Vocks (OpenBig.org) \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:36+0000\n" -"X-Generator: Launchpad (build 16335)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:48+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: hr_timesheet_sheet #: field:hr.analytic.timesheet,sheet_id:0 @@ -33,7 +34,7 @@ msgstr "Dienstleistung" #: field:hr.timesheet.report,quantity:0 #: field:timesheet.report,quantity:0 msgid "Time" -msgstr "" +msgstr "Zeit" #. module: hr_timesheet_sheet #: help:hr.config.settings,timesheet_max_difference:0 @@ -42,6 +43,10 @@ msgid "" " computation for one sheet. Set this to 0 if you do not want " "any control." msgstr "" +"Erlaubt Differenz in Stunden zwischen dem Anmelden/ Abmelden und der " +"Stundenzettel\n" +"                 Berechnung eines Tabellenblatts. Setze dies auf 0, wenn Sie " +"keinerlei Kontrollen haben möchten." #. module: hr_timesheet_sheet #: view:hr.timesheet.report:0 @@ -76,6 +81,9 @@ msgid "" "In order to create a timesheet for this employee, you must assign an " "analytic journal to the employee, like 'Timesheet Journal'." msgstr "" +"Um einen Stundenzettel für diesen Mitarbeiter zu erstellen, müssen Sie " +"diesem eine Kostenstelle zuweisen, vergleichbar mit dem Beispiel für das " +"\"Timesheet Journal\"." #. module: hr_timesheet_sheet #: selection:hr.timesheet.report,month:0 @@ -92,7 +100,7 @@ msgstr "#Kosten" #. module: hr_timesheet_sheet #: field:hr_timesheet_sheet.sheet,message_unread:0 msgid "Unread Messages" -msgstr "" +msgstr "Ungelesene Nachrichten" #. module: hr_timesheet_sheet #: view:hr.timesheet.report:0 @@ -122,7 +130,7 @@ msgstr "Setze auf Entwurf" #. module: hr_timesheet_sheet #: view:hr_timesheet_sheet.sheet:0 msgid "Timesheet Period" -msgstr "" +msgstr "Zeiterfassungsperiode" #. module: hr_timesheet_sheet #: field:hr_timesheet_sheet.sheet,date_to:0 @@ -133,7 +141,7 @@ msgstr "Bis" #. module: hr_timesheet_sheet #: view:hr_timesheet_sheet.sheet:0 msgid "to" -msgstr "" +msgstr "An" #. module: hr_timesheet_sheet #: model:process.node,note:hr_timesheet_sheet.process_node_invoiceonwork0 @@ -146,6 +154,7 @@ msgstr "Basierend auf Zeiterfassung" #, python-format msgid "You cannot modify an entry in a confirmed timesheet." msgstr "" +"Sie können keinen Eintrag einer validierten Zeiterfassung bearbeiten." #. module: hr_timesheet_sheet #: view:hr.timesheet.report:0 @@ -188,13 +197,14 @@ msgstr "Zurücksetzen" #: view:hr_timesheet_sheet.sheet:0 #: model:ir.actions.act_window,name:hr_timesheet_sheet.act_hr_timesheet_sheet_sheet_2_hr_analytic_timesheet msgid "Timesheet Activities" -msgstr "" +msgstr "Zeiterfassungsaktivitäten" #. module: hr_timesheet_sheet #: code:addons/hr_timesheet_sheet/wizard/hr_timesheet_current.py:38 #, python-format msgid "Please create an employee and associate it with this user." msgstr "" +"Bitte legen Sie einen Mitarbeiter an und weisen Sie ihm einen Benutzer zu." #. module: hr_timesheet_sheet #: code:addons/hr_timesheet_sheet/hr_timesheet_sheet.py:388 @@ -203,12 +213,14 @@ msgstr "" msgid "" "You cannot enter an attendance date outside the current timesheet dates." msgstr "" +"Sie können keine Anwesenheitszeiten ausserhalb des festgelegten Zeitraums " +"der aktuellen Zeiterfassung erfassen." #. module: hr_timesheet_sheet #: code:addons/hr_timesheet_sheet/hr_timesheet_sheet.py:205 #, python-format msgid "Week " -msgstr "" +msgstr "Woche " #. module: hr_timesheet_sheet #: model:ir.actions.act_window,help:hr_timesheet_sheet.action_hr_timesheet_current_open @@ -232,7 +244,7 @@ msgstr "" #. module: hr_timesheet_sheet #: field:hr_timesheet_sheet.sheet,message_ids:0 msgid "Messages" -msgstr "" +msgstr "Nachrichten" #. module: hr_timesheet_sheet #: help:hr_timesheet_sheet.sheet,state:0 @@ -244,6 +256,12 @@ msgid "" "* The 'Done' status is used when users timesheet is accepted by his/her " "senior." msgstr "" +" * Der \"Entwurf\"-Status wird verwendet, wenn ein Benutzer einen neuen und " +"noch nicht bestätigten Stundenzettel anlegen möchte.\n" +"* Die \"Bestätigt\"-Status wird eigetragen, wenn Benutzer Ihre Stundenzettel " +"bestätigen.\n" +"* Die \"Fertig\" Status wird verwendet, wenn Stundenzettel durch dessen " +"Vorgesetzten genehmigt wurden." #. module: hr_timesheet_sheet #: code:addons/hr_timesheet_sheet/hr_timesheet_sheet.py:64 @@ -258,13 +276,15 @@ msgstr "" #: code:addons/hr_timesheet_sheet/wizard/hr_timesheet_current.py:38 #, python-format msgid "Error!" -msgstr "" +msgstr "Fehler!" #. module: hr_timesheet_sheet #: field:hr.config.settings,timesheet_max_difference:0 msgid "" "Allow a difference of time between timesheets and attendances of (in hours)" msgstr "" +"Abweichungen zwischen Zeiterfassung und Anwesenheitszeiten zulassen (in " +"Stunden)" #. module: hr_timesheet_sheet #: code:addons/hr_timesheet_sheet/hr_timesheet_sheet.py:99 @@ -272,6 +292,8 @@ msgstr "" msgid "" "Please verify that the total difference of the sheet is lower than %.2f." msgstr "" +"Bitte stellen Sie sicher, das die vollständige Differenz zwischen den " +"Zeiterfassungen weniger als %.2f." #. module: hr_timesheet_sheet #: model:ir.actions.act_window,name:hr_timesheet_sheet.action_timesheet_report_stat_all @@ -292,7 +314,7 @@ msgstr "Genehmigung" #. module: hr_timesheet_sheet #: help:hr_timesheet_sheet.sheet,message_unread:0 msgid "If checked new messages require your attention." -msgstr "" +msgstr "Wenn aktiviert, erfordern neue Nachrichten Ihr Handeln" #. module: hr_timesheet_sheet #: code:addons/hr_timesheet_sheet/hr_timesheet_sheet.py:69 @@ -302,6 +324,8 @@ msgid "" "In order to create a timesheet for this employee, you must assign it to a " "user." msgstr "" +"Um eine Zeiterfassung für diesen Mitarbeiter anzulegen, müssen Sie ihn " +"zuerst einem Benutzer zuordnen." #. module: hr_timesheet_sheet #: model:process.node,note:hr_timesheet_sheet.process_node_attendance0 @@ -313,7 +337,7 @@ msgstr "Erfasste Zeit durch Benutzer" #: code:addons/hr_timesheet_sheet/hr_timesheet_sheet.py:215 #, python-format msgid "Invalid Action!" -msgstr "" +msgstr "Ungültige Aktion!" #. module: hr_timesheet_sheet #: view:hr.timesheet.report:0 @@ -329,6 +353,9 @@ msgid "" "Holds the Chatter summary (number of messages, ...). This summary is " "directly in html format in order to be inserted in kanban views." msgstr "" +"Hier finden Sie die Nachrichtenübersicht (Anzahl Nachrichten etc., ...) im " +"html Format, um über dieses Format dann später in einer Kanban Ansicht " +"weiterzuarbeiten." #. module: hr_timesheet_sheet #: field:timesheet.report,nbr:0 @@ -376,7 +403,7 @@ msgstr "Zeiterfassung Positionen" #. module: hr_timesheet_sheet #: field:hr_timesheet_sheet.sheet,message_follower_ids:0 msgid "Followers" -msgstr "" +msgstr "Followers" #. module: hr_timesheet_sheet #: model:process.node,note:hr_timesheet_sheet.process_node_confirmedtimesheet0 @@ -408,7 +435,7 @@ msgstr "Gesamtzeit" #: model:ir.actions.act_window,name:hr_timesheet_sheet.act_hr_timesheet_sheet_form #: model:ir.ui.menu,name:hr_timesheet_sheet.menu_act_hr_timesheet_sheet_form msgid "Timesheets to Validate" -msgstr "" +msgstr "Zu bestätigende Zeiterfassungen" #. module: hr_timesheet_sheet #: view:hr.timesheet.report:0 @@ -436,14 +463,14 @@ msgstr "Juli" #. module: hr_timesheet_sheet #: field:hr.config.settings,timesheet_range:0 msgid "Validate timesheets every" -msgstr "" +msgstr "Zeiterfassung bestätigen alle" #. module: hr_timesheet_sheet #: code:addons/hr_timesheet_sheet/hr_timesheet_sheet.py:73 #: code:addons/hr_timesheet_sheet/hr_timesheet_sheet.py:86 #, python-format msgid "Configuration Error!" -msgstr "" +msgstr "Konfigurationsfehler!" #. module: hr_timesheet_sheet #: field:hr_timesheet_sheet.sheet,state:0 @@ -507,6 +534,8 @@ msgid "" "In order to create a timesheet for this employee, you must link the employee " "to a product, like 'Consultant'." msgstr "" +"Um eine Zeiterfassung für diesen Mitarbeiter anzulegen, muss im Datensatz " +"des Mitarbeiters ein Produkt, wie z.B. 'Consultant', festgelegt werden." #. module: hr_timesheet_sheet #: selection:hr.timesheet.report,month:0 @@ -558,6 +587,8 @@ msgid "" "In order to create a timesheet for this employee, you must link the employee " "to a product." msgstr "" +"Um eine Zeiterfassung für diesen Mitarbeiter anzulegen, muss dem Mitarbeiter " +"ein Produkt zugewiesen werden." #. module: hr_timesheet_sheet #. openerp-web @@ -566,12 +597,12 @@ msgstr "" msgid "" "You will be able to register your working hours and\n" " activities." -msgstr "" +msgstr "Sie können sämtliche Arbeitszeiten und Aktivitäten eingeben." #. module: hr_timesheet_sheet #: view:hr.timesheet.current.open:0 msgid "or" -msgstr "" +msgstr "oder" #. module: hr_timesheet_sheet #: model:process.transition,name:hr_timesheet_sheet.process_transition_invoiceontimesheet0 @@ -597,7 +628,7 @@ msgstr "Notiz" #: code:addons/hr_timesheet_sheet/static/src/xml/timesheet.xml:33 #, python-format msgid "Add" -msgstr "" +msgstr "Hinzufügen" #. module: hr_timesheet_sheet #: view:timesheet.report:0 @@ -636,11 +667,26 @@ msgid "" "

\n" " " msgstr "" +"p class=\"oe_view_nocontent_create\">\n" +" Neuer zu genehmigender Stundenzettel\n" +"

\n" +" Zeichnen Sie jeden Tag Stundenzettel auf. Sobald diese " +"bestätigt werden, \n" +" sollten sie wöchentlich zusammengefasst werden und dann " +"abschließend\n" +" durch einen Vorgesetzten bestätigt werden.\n" +"

\n" +" Stundenzettel können auch gegenüber dem Kunden abgerechnet " +"werden, z.B. \n" +" in Abhängigkeit vom korrespondierenden Vertrag für dieses " +"Projekt.\n" +"

\n" +" " #. module: hr_timesheet_sheet #: model:ir.model,name:hr_timesheet_sheet.model_account_analytic_line msgid "Analytic Line" -msgstr "" +msgstr "Analytische Buchung" #. module: hr_timesheet_sheet #: selection:hr.timesheet.report,month:0 @@ -651,7 +697,7 @@ msgstr "August" #. module: hr_timesheet_sheet #: view:hr_timesheet_sheet.sheet:0 msgid "Differences" -msgstr "" +msgstr "Differenzen" #. module: hr_timesheet_sheet #: selection:hr.timesheet.report,month:0 @@ -679,7 +725,7 @@ msgstr "Zeiterfassung nach Perioden" #. module: hr_timesheet_sheet #: field:hr_timesheet_sheet.sheet,message_is_follower:0 msgid "Is a Follower" -msgstr "" +msgstr "Ist bereits Follower" #. module: hr_timesheet_sheet #: view:hr.timesheet.report:0 @@ -723,7 +769,7 @@ msgstr "Zeiterfassung Zeitraum" #: field:hr_timesheet_sheet.sheet,message_comment_ids:0 #: help:hr_timesheet_sheet.sheet,message_comment_ids:0 msgid "Comments and emails" -msgstr "" +msgstr "Kommentare und E-Mails" #. module: hr_timesheet_sheet #: selection:hr.timesheet.report,month:0 @@ -738,6 +784,8 @@ msgid "" "The timesheet cannot be validated as it does not contain an equal number of " "sign ins and sign outs." msgstr "" +"Die Zeiterfassung kann ich bestätigt werden, da die Anzahl der An- und " +"Abmeldungen nicht übereinstimmt." #. module: hr_timesheet_sheet #: selection:hr.timesheet.report,month:0 @@ -766,6 +814,7 @@ msgstr "Zusammenfassung" #, python-format msgid "You cannot delete a timesheet which have attendance entries." msgstr "" +"Sie können keine Zeiterfassung löschen, die Anwesenheitseinträge enthält." #. module: hr_timesheet_sheet #: view:hr_timesheet_sheet.sheet:0 @@ -779,11 +828,15 @@ msgid "" "You cannot have 2 timesheets that overlap!\n" "You should use the menu 'My Timesheet' to avoid this problem." msgstr "" +"Sie können keine 2 Zeiterfassungen anlegen, deren Zeiträume sich " +"überschneiden!\n" +"Sie sollten das Menu 'Meine Zeiterfassung' benutzen, um dieses Problem zu " +"vermeiden." #. module: hr_timesheet_sheet #: view:hr_timesheet_sheet.sheet:0 msgid "Submit to Manager" -msgstr "" +msgstr "Dem Manager vorlegen" #. module: hr_timesheet_sheet #: view:hr.timesheet.report:0 @@ -808,7 +861,7 @@ msgstr "Suche Konto" #: code:addons/hr_timesheet_sheet/hr_timesheet_sheet.py:415 #, python-format msgid "You cannot modify an entry in a confirmed timesheet" -msgstr "" +msgstr "Sie können keinen Eintrag in einer bestätigten Zeiterfassung ändern" #. module: hr_timesheet_sheet #: help:res.company,timesheet_max_difference:0 @@ -841,6 +894,9 @@ msgid "" "You cannot have 2 timesheets that overlap!\n" "Please use the menu 'My Current Timesheet' to avoid this problem." msgstr "" +"Bei meinen aktuellen Stundenzetteln können sich 2 Teilnehmer überschneiden. " +"Bitte benutzen Sie das Menü\n" +"\"Meine Zeiterfassung\", um dieses Problem zu lösen." #. module: hr_timesheet_sheet #: view:hr.timesheet.current.open:0 @@ -898,6 +954,7 @@ msgstr "Gruppiere je Jahr" #, python-format msgid "Click to add projects, contracts or analytic accounts." msgstr "" +"Klicken Sie zur Erstellung von Projekten, Verträgen oder Kostenstellen" #. module: hr_timesheet_sheet #: model:process.node,note:hr_timesheet_sheet.process_node_validatedtimesheet0 @@ -929,7 +986,7 @@ msgstr "Bestätigte Zeiterfassungen" #. module: hr_timesheet_sheet #: view:hr_timesheet_sheet.sheet:0 msgid "Details" -msgstr "" +msgstr "Details" #. module: hr_timesheet_sheet #: model:ir.model,name:hr_timesheet_sheet.model_hr_analytic_timesheet @@ -940,7 +997,7 @@ msgstr "Zeiterfassung Positionen" #: code:addons/hr_timesheet_sheet/hr_timesheet_sheet.py:213 #, python-format msgid "You cannot delete a timesheet which is already confirmed." -msgstr "" +msgstr "Sie können keine Zeiterfassung löschen, die bereits bestätigt wurde." #. module: hr_timesheet_sheet #: view:hr.timesheet.report:0 @@ -1001,7 +1058,7 @@ msgstr "Gesamt Anwesenheitszeit" #: code:addons/hr_timesheet_sheet/static/src/xml/timesheet.xml:39 #, python-format msgid "Add a Line" -msgstr "" +msgstr "EIntrag hinzufügen" #. module: hr_timesheet_sheet #: field:hr_timesheet_sheet.sheet,total_difference:0 @@ -1013,7 +1070,7 @@ msgstr "Differenz" #: code:addons/hr_timesheet_sheet/hr_timesheet_sheet.py:64 #, python-format msgid "You cannot duplicate a timesheet." -msgstr "" +msgstr "Sie können keine Zeiterfassung duplizieren." #. module: hr_timesheet_sheet #: selection:hr_timesheet_sheet.sheet,state_attendance:0 @@ -1038,6 +1095,16 @@ msgid "" "

\n" " " msgstr "" +"p>\n" +" Diese Statistik wertet die erfassten Studenzettel der " +"Mitarbeiter aus.\n" +" Ein kompletter Überblick über abrechenbare Arbeitszeiten von " +"Mitarbeitern\n" +" ist somit einfach möglich. Sie können mit Hilfe des Suche " +"Tools auch vorab \n" +" weitere Filter oder Gruppierungen erstellen. \n" +"

\n" +" " #. module: hr_timesheet_sheet #: view:hr_timesheet_sheet.sheet:0 @@ -1048,6 +1115,8 @@ msgstr "Mitarbeiter" #: constraint:hr.analytic.timesheet:0 msgid "You cannot modify an entry in a Confirmed/Done timesheet !" msgstr "" +"Sie können keine Änderungen für einen bereits erledigten / bestätigten " +"Stundenzettel übernehmen" #. module: hr_timesheet_sheet #: model:process.node,note:hr_timesheet_sheet.process_node_timesheet0 @@ -1069,7 +1138,7 @@ msgstr "Bestätigung" #: code:addons/hr_timesheet_sheet/hr_timesheet_sheet.py:99 #, python-format msgid "Warning!" -msgstr "" +msgstr "Warnung !" #. module: hr_timesheet_sheet #: field:hr_timesheet_sheet.sheet.account,invoice_rate:0 @@ -1081,7 +1150,7 @@ msgstr "Quote Abrechnung" #: code:addons/hr_timesheet_sheet/hr_timesheet_sheet.py:408 #, python-format msgid "User Error!" -msgstr "" +msgstr "Benutzerfehler!" #. module: hr_timesheet_sheet #: view:hr_timesheet_sheet.sheet.day:0 @@ -1096,7 +1165,7 @@ msgstr "Genehmige" #. module: hr_timesheet_sheet #: help:hr_timesheet_sheet.sheet,message_ids:0 msgid "Messages and communication history" -msgstr "" +msgstr "Nachrichten und Kommunikations-Historie" #. module: hr_timesheet_sheet #: field:hr_timesheet_sheet.sheet,account_ids:0 diff --git a/addons/hr_timesheet_sheet/i18n/pt_BR.po b/addons/hr_timesheet_sheet/i18n/pt_BR.po index 25930141e67..4fe0d59a19a 100644 --- a/addons/hr_timesheet_sheet/i18n/pt_BR.po +++ b/addons/hr_timesheet_sheet/i18n/pt_BR.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: pt_BR\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-12-03 16:02+0000\n" -"PO-Revision-Date: 2012-07-28 22:13+0000\n" -"Last-Translator: Joe Pimentel \n" +"PO-Revision-Date: 2012-12-10 14:56+0000\n" +"Last-Translator: Projetaty Soluções OpenSource \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:36+0000\n" -"X-Generator: Launchpad (build 16335)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:48+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: hr_timesheet_sheet #: field:hr.analytic.timesheet,sheet_id:0 @@ -450,7 +450,7 @@ msgstr "" #: view:timesheet.report:0 #: field:timesheet.report,state:0 msgid "Status" -msgstr "" +msgstr "Situação" #. module: hr_timesheet_sheet #: model:process.node,name:hr_timesheet_sheet.process_node_workontask0 diff --git a/addons/knowledge/i18n/de.po b/addons/knowledge/i18n/de.po index 5a425c2aec5..cd501c600e9 100644 --- a/addons/knowledge/i18n/de.po +++ b/addons/knowledge/i18n/de.po @@ -7,20 +7,20 @@ msgstr "" "Project-Id-Version: OpenERP Server 6.0dev\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-11-24 02:53+0000\n" -"PO-Revision-Date: 2011-01-13 14:43+0000\n" +"PO-Revision-Date: 2012-12-10 21:53+0000\n" "Last-Translator: Thorsten Vocks (OpenBig.org) \n" "Language-Team: German \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-11-25 06:24+0000\n" -"X-Generator: Launchpad (build 16293)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:49+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: knowledge #: view:knowledge.config.settings:0 msgid "Documents" -msgstr "" +msgstr "Dokumente" #. module: knowledge #: model:ir.model,name:knowledge.model_knowledge_config_settings @@ -33,11 +33,13 @@ msgid "" "Access your documents in OpenERP through WebDAV.\n" " This installs the module document_webdav." msgstr "" +"Verbinden Sie Ihre Dokumente mit WebDAV. Es wird das Modul \n" +"document_webdav installieren." #. module: knowledge #: help:knowledge.config.settings,module_document_page:0 msgid "This installs the module document_page." -msgstr "" +msgstr "Es wird das Modul document_page installiert." #. module: knowledge #: model:ir.ui.menu,name:knowledge.menu_document2 @@ -48,12 +50,12 @@ msgstr "Wikipedia" #: model:ir.actions.act_window,name:knowledge.action_knowledge_configuration #: view:knowledge.config.settings:0 msgid "Configure Knowledge" -msgstr "" +msgstr "Konfigurieren Sie den Know-How-Transfer" #. module: knowledge #: view:knowledge.config.settings:0 msgid "Knowledge and Documents Management" -msgstr "" +msgstr "Knowledge und Dokumenten Management" #. module: knowledge #: help:knowledge.config.settings,module_document:0 @@ -63,31 +65,37 @@ msgid "" "and a document dashboard.\n" " This installs the module document." msgstr "" +"Es handelt sich um ein vollständiges Dokumenten Management, mit folgenden " +"Funktionen:\n" +" komplette Dokumenten Suche mit Volltext Suche und eine " +"Dokumenten Anzeigetafel.\n" +" Durch Aktivierung wird das Modul 'document' dann " +"installiert." #. module: knowledge #: field:knowledge.config.settings,module_document_page:0 msgid "Create static web pages" -msgstr "" +msgstr "Erstelle statische Webseiten" #. module: knowledge #: field:knowledge.config.settings,module_document_ftp:0 msgid "Share repositories (FTP)" -msgstr "" +msgstr "Freigeben von Verzeichnissen (FTP)" #. module: knowledge #: field:knowledge.config.settings,module_document:0 msgid "Manage documents" -msgstr "" +msgstr "Management von Dokumenten" #. module: knowledge #: view:knowledge.config.settings:0 msgid "Cancel" -msgstr "" +msgstr "Abbrechen" #. module: knowledge #: view:knowledge.config.settings:0 msgid "Apply" -msgstr "" +msgstr "Anwenden" #. module: knowledge #: model:ir.ui.menu,name:knowledge.menu_document_configuration @@ -100,16 +108,18 @@ msgid "" "Access your documents in OpenERP through an FTP interface.\n" " This installs the module document_ftp." msgstr "" +"Verbinden Sie Ihre Dokumente in OpenERP durch ein FTP Interface.\n" +" Es wird das Modul document_ftp installiert." #. module: knowledge #: view:knowledge.config.settings:0 msgid "or" -msgstr "" +msgstr "oder" #. module: knowledge #: field:knowledge.config.settings,module_document_webdav:0 msgid "Share repositories (WebDAV)" -msgstr "" +msgstr "Freigabe von Verzeichnissen (WebDAV)" #. module: knowledge #: model:ir.ui.menu,name:knowledge.menu_document diff --git a/addons/knowledge/i18n/es.po b/addons/knowledge/i18n/es.po index 46994f5f09e..3388454c68a 100644 --- a/addons/knowledge/i18n/es.po +++ b/addons/knowledge/i18n/es.po @@ -8,20 +8,19 @@ msgstr "" "Project-Id-Version: openobject-addons\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2012-11-24 02:53+0000\n" -"PO-Revision-Date: 2010-12-27 08:12+0000\n" -"Last-Translator: Jordi Esteve (www.zikzakmedia.com) " -"\n" +"PO-Revision-Date: 2012-12-10 21:52+0000\n" +"Last-Translator: lambdasoftware \n" "Language-Team: Spanish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-11-25 06:25+0000\n" -"X-Generator: Launchpad (build 16293)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:49+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: knowledge #: view:knowledge.config.settings:0 msgid "Documents" -msgstr "" +msgstr "Documentos" #. module: knowledge #: model:ir.model,name:knowledge.model_knowledge_config_settings @@ -34,6 +33,8 @@ msgid "" "Access your documents in OpenERP through WebDAV.\n" " This installs the module document_webdav." msgstr "" +"Accede a tus documentos de OpenERP a través de WebDAV\n" +" Esto instalará el módulo document_webdav." #. module: knowledge #: help:knowledge.config.settings,module_document_page:0 @@ -78,17 +79,17 @@ msgstr "" #. module: knowledge #: field:knowledge.config.settings,module_document:0 msgid "Manage documents" -msgstr "" +msgstr "Gestión de documentos" #. module: knowledge #: view:knowledge.config.settings:0 msgid "Cancel" -msgstr "" +msgstr "Cancelar" #. module: knowledge #: view:knowledge.config.settings:0 msgid "Apply" -msgstr "" +msgstr "Aplicar" #. module: knowledge #: model:ir.ui.menu,name:knowledge.menu_document_configuration @@ -105,12 +106,12 @@ msgstr "" #. module: knowledge #: view:knowledge.config.settings:0 msgid "or" -msgstr "" +msgstr "o" #. module: knowledge #: field:knowledge.config.settings,module_document_webdav:0 msgid "Share repositories (WebDAV)" -msgstr "" +msgstr "Repositorios compartidos (WebDAV)" #. module: knowledge #: model:ir.ui.menu,name:knowledge.menu_document diff --git a/addons/l10n_be_hr_payroll/i18n/pt.po b/addons/l10n_be_hr_payroll/i18n/pt.po new file mode 100644 index 00000000000..92d708a5604 --- /dev/null +++ b/addons/l10n_be_hr_payroll/i18n/pt.po @@ -0,0 +1,148 @@ +# Portuguese translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-11-24 02:53+0000\n" +"PO-Revision-Date: 2012-12-10 17:52+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Portuguese \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-12-11 04:49+0000\n" +"X-Generator: Launchpad (build 16356)\n" + +#. module: l10n_be_hr_payroll +#: help:hr.employee,disabled_spouse_bool:0 +msgid "if recipient spouse is declared disabled by law" +msgstr "" + +#. module: l10n_be_hr_payroll +#: help:hr.employee,disabled_children_bool:0 +msgid "if recipient children is/are declared disabled by law" +msgstr "" + +#. module: l10n_be_hr_payroll +#: field:hr.contract,misc_onss_deduction:0 +msgid "Miscellaneous exempt ONSS " +msgstr "" + +#. module: l10n_be_hr_payroll +#: model:ir.model,name:l10n_be_hr_payroll.model_hr_employee +msgid "Employee" +msgstr "" + +#. module: l10n_be_hr_payroll +#: field:hr.employee,disabled_spouse_bool:0 +msgid "Disabled Spouse" +msgstr "" + +#. module: l10n_be_hr_payroll +#: field:hr.contract,retained_net_amount:0 +msgid "Net retained " +msgstr "" + +#. module: l10n_be_hr_payroll +#: field:hr.employee,resident_bool:0 +msgid "Nonresident" +msgstr "" + +#. module: l10n_be_hr_payroll +#: help:hr.employee,resident_bool:0 +msgid "if recipient lives in a foreign country" +msgstr "" + +#. module: l10n_be_hr_payroll +#: view:hr.employee:0 +msgid "if spouse has professionnel income or not" +msgstr "" + +#. module: l10n_be_hr_payroll +#: field:hr.contract,insurance_employee_deduction:0 +msgid "Insurance Group - by worker " +msgstr "" + +#. module: l10n_be_hr_payroll +#: selection:hr.employee,spouse_fiscal_status:0 +msgid "With Income" +msgstr "" + +#. module: l10n_be_hr_payroll +#: selection:hr.employee,spouse_fiscal_status:0 +msgid "Without Income" +msgstr "" + +#. module: l10n_be_hr_payroll +#: field:hr.employee,disabled_children_number:0 +msgid "Number of disabled children" +msgstr "" + +#. module: l10n_be_hr_payroll +#: field:hr.contract,additional_net_amount:0 +msgid "Net supplements" +msgstr "" + +#. module: l10n_be_hr_payroll +#: field:hr.contract,car_company_amount:0 +msgid "Company car employer" +msgstr "" + +#. module: l10n_be_hr_payroll +#: field:hr.contract,misc_advantage_amount:0 +msgid "Benefits of various nature " +msgstr "" + +#. module: l10n_be_hr_payroll +#: field:hr.contract,car_employee_deduction:0 +msgid "Company Car Deduction for Worker" +msgstr "" + +#. module: l10n_be_hr_payroll +#: field:hr.employee,disabled_children_bool:0 +msgid "Disabled Children" +msgstr "" + +#. module: l10n_be_hr_payroll +#: model:ir.model,name:l10n_be_hr_payroll.model_hr_contract +msgid "Contract" +msgstr "" + +#. module: l10n_be_hr_payroll +#: field:hr.contract,meal_voucher_amount:0 +msgid "Check Value Meal " +msgstr "" + +#. module: l10n_be_hr_payroll +#: field:hr.contract,travel_reimbursement_amount:0 +msgid "Reimbursement of travel expenses" +msgstr "" + +#. module: l10n_be_hr_payroll +#: constraint:hr.contract:0 +msgid "Error! Contract start-date must be less than contract end-date." +msgstr "" + +#. module: l10n_be_hr_payroll +#: field:hr.employee,spouse_fiscal_status:0 +msgid "Tax status for spouse" +msgstr "" + +#. module: l10n_be_hr_payroll +#: view:hr.employee:0 +msgid "number of dependent children declared as disabled" +msgstr "" + +#. module: l10n_be_hr_payroll +#: constraint:hr.employee:0 +msgid "Error! You cannot create recursive hierarchy of Employee(s)." +msgstr "" + +#. module: l10n_be_hr_payroll +#: field:hr.contract,meal_voucher_employee_deduction:0 +msgid "Check Value Meal - by worker " +msgstr "" diff --git a/addons/l10n_multilang/i18n/nl.po b/addons/l10n_multilang/i18n/nl.po new file mode 100644 index 00000000000..a9de914012c --- /dev/null +++ b/addons/l10n_multilang/i18n/nl.po @@ -0,0 +1,163 @@ +# Dutch translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-02-08 01:06+0000\n" +"PO-Revision-Date: 2012-12-10 21:43+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Dutch \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-12-11 04:49+0000\n" +"X-Generator: Launchpad (build 16356)\n" + +#. module: l10n_multilang +#: model:ir.model,name:l10n_multilang.model_account_fiscal_position_template +msgid "Template for Fiscal Position" +msgstr "" + +#. module: l10n_multilang +#: sql_constraint:account.account:0 +msgid "The code of the account must be unique per company !" +msgstr "" + +#. module: l10n_multilang +#: constraint:account.account.template:0 +msgid "" +"Configuration Error!\n" +"You can not define children to an account with internal type different of " +"\"View\"! " +msgstr "" + +#. module: l10n_multilang +#: model:ir.model,name:l10n_multilang.model_account_analytic_journal +msgid "Analytic Journal" +msgstr "" + +#. module: l10n_multilang +#: constraint:account.account.template:0 +msgid "Error ! You can not create recursive account templates." +msgstr "" + +#. module: l10n_multilang +#: model:ir.model,name:l10n_multilang.model_account_journal +msgid "Journal" +msgstr "" + +#. module: l10n_multilang +#: model:ir.model,name:l10n_multilang.model_account_chart_template +msgid "Templates for Account Chart" +msgstr "" + +#. module: l10n_multilang +#: sql_constraint:account.tax:0 +msgid "The description must be unique per company!" +msgstr "" + +#. module: l10n_multilang +#: constraint:account.tax.code.template:0 +msgid "Error ! You can not create recursive Tax Codes." +msgstr "" + +#. module: l10n_multilang +#: model:ir.model,name:l10n_multilang.model_account_tax_template +msgid "account.tax.template" +msgstr "" + +#. module: l10n_multilang +#: model:ir.model,name:l10n_multilang.model_account_tax +msgid "account.tax" +msgstr "" + +#. module: l10n_multilang +#: model:ir.model,name:l10n_multilang.model_account_account +msgid "Account" +msgstr "" + +#. module: l10n_multilang +#: model:ir.model,name:l10n_multilang.model_wizard_multi_charts_accounts +msgid "wizard.multi.charts.accounts" +msgstr "" + +#. module: l10n_multilang +#: constraint:account.journal:0 +msgid "" +"Configuration error! The currency chosen should be shared by the default " +"accounts too." +msgstr "" + +#. module: l10n_multilang +#: model:ir.model,name:l10n_multilang.model_account_account_template +msgid "Templates for Accounts" +msgstr "" + +#. module: l10n_multilang +#: help:account.chart.template,spoken_languages:0 +msgid "" +"State here the languages for which the translations of templates could be " +"loaded at the time of installation of this localization module and copied in " +"the final object when generating them from templates. You must provide the " +"language codes separated by ';'" +msgstr "" + +#. module: l10n_multilang +#: constraint:account.account:0 +msgid "Error ! You can not create recursive accounts." +msgstr "" + +#. module: l10n_multilang +#: constraint:account.account:0 +msgid "" +"Configuration Error! \n" +"You can not select an account type with a deferral method different of " +"\"Unreconciled\" for accounts with internal type \"Payable/Receivable\"! " +msgstr "" + +#. module: l10n_multilang +#: sql_constraint:account.journal:0 +msgid "The name of the journal must be unique per company !" +msgstr "" + +#. module: l10n_multilang +#: model:ir.model,name:l10n_multilang.model_account_analytic_account +msgid "Analytic Account" +msgstr "" + +#. module: l10n_multilang +#: sql_constraint:account.journal:0 +msgid "The code of the journal must be unique per company !" +msgstr "" + +#. module: l10n_multilang +#: model:ir.model,name:l10n_multilang.model_account_fiscal_position +msgid "Fiscal Position" +msgstr "" + +#. module: l10n_multilang +#: constraint:account.account:0 +msgid "" +"Configuration Error! \n" +"You can not define children to an account with internal type different of " +"\"View\"! " +msgstr "" + +#. module: l10n_multilang +#: constraint:account.analytic.account:0 +msgid "Error! You can not create recursive analytic accounts." +msgstr "" + +#. module: l10n_multilang +#: model:ir.model,name:l10n_multilang.model_account_tax_code_template +msgid "Tax Code Template" +msgstr "" + +#. module: l10n_multilang +#: field:account.chart.template,spoken_languages:0 +msgid "Spoken Languages" +msgstr "" diff --git a/addons/mail/i18n/pl.po b/addons/mail/i18n/pl.po index ff41ffe285d..3ac74c530d7 100644 --- a/addons/mail/i18n/pl.po +++ b/addons/mail/i18n/pl.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openobject-addons\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2012-12-03 16:01+0000\n" -"PO-Revision-Date: 2011-01-13 16:42+0000\n" +"PO-Revision-Date: 2012-12-10 20:01+0000\n" "Last-Translator: Grzegorz Grzelak (OpenGLOBE.pl) \n" "Language-Team: Polish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:52+0000\n" -"X-Generator: Launchpad (build 16335)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:49+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: mail #: field:res.partner,notification_email_send:0 @@ -36,7 +36,7 @@ msgstr "" #: field:mail.compose.message,author_id:0 #: field:mail.message,author_id:0 msgid "Author" -msgstr "" +msgstr "Autor" #. module: mail #: view:mail.mail:0 @@ -51,7 +51,7 @@ msgstr "Odbiorcy wiadomości" #. module: mail #: view:mail.message:0 msgid "Comments" -msgstr "" +msgstr "Komentarze" #. module: mail #: view:mail.alias:0 @@ -88,12 +88,12 @@ msgstr "" #. module: mail #: view:mail.group:0 msgid "Group Name" -msgstr "" +msgstr "Nazwa Grupy" #. module: mail #: selection:mail.group,public:0 msgid "Public" -msgstr "" +msgstr "Publiczne" #. module: mail #: view:mail.mail:0 @@ -103,7 +103,7 @@ msgstr "Treść" #. module: mail #: view:mail.message:0 msgid "Show messages to read" -msgstr "" +msgstr "Pokaż wiadomości do przeczytania" #. module: mail #: help:mail.compose.message,email_from:0 @@ -116,14 +116,14 @@ msgstr "" #. module: mail #: model:ir.model,name:mail.model_mail_compose_message msgid "Email composition wizard" -msgstr "" +msgstr "Kreator email" #. module: mail #: field:mail.group,message_unread:0 #: field:mail.thread,message_unread:0 #: field:res.partner,message_unread:0 msgid "Unread Messages" -msgstr "" +msgstr "Nieprzeczytane wiadomości" #. module: mail #. openerp-web @@ -149,18 +149,18 @@ msgstr "" #: code:addons/mail/static/src/js/mail.js:778 #, python-format msgid "Do you really want to delete this message?" -msgstr "" +msgstr "Chcesz usunąć tę wiadomość?" #. module: mail #: view:mail.message:0 #: field:mail.notification,read:0 msgid "Read" -msgstr "" +msgstr "Przeczytane" #. module: mail #: view:mail.group:0 msgid "Search Groups" -msgstr "" +msgstr "Szukaj grup" #. module: mail #. openerp-web @@ -173,7 +173,7 @@ msgstr "" #: code:addons/mail/mail_message.py:646 #, python-format msgid "Access Denied" -msgstr "" +msgstr "Brak dostępu" #. module: mail #: help:mail.group,image_medium:0 @@ -204,7 +204,7 @@ msgstr "Wątek" #: code:addons/mail/static/src/xml/mail.xml:37 #, python-format msgid "Open the full mail composer" -msgstr "" +msgstr "Otwórz pełny edytor email" #. module: mail #. openerp-web @@ -221,7 +221,7 @@ msgstr "" #. module: mail #: field:mail.group,group_ids:0 msgid "Auto Subscription" -msgstr "" +msgstr "Autosubskrypcja" #. module: mail #: field:mail.mail,references:0 @@ -233,12 +233,12 @@ msgstr "Odnośniki" #: code:addons/mail/static/src/xml/mail.xml:179 #, python-format msgid "No messages." -msgstr "" +msgstr "Brak wiadomości" #. module: mail #: model:ir.model,name:mail.model_mail_group msgid "Discussion group" -msgstr "" +msgstr "Grupa dyskusyjna" #. module: mail #. openerp-web @@ -253,7 +253,7 @@ msgstr "" #: code:addons/mail/static/src/xml/mail.xml:211 #, python-format msgid "Set back to Inbox" -msgstr "" +msgstr "Ustaw z powrotem do Odebranych" #. module: mail #: help:mail.compose.message,type:0 @@ -266,7 +266,7 @@ msgstr "" #. module: mail #: model:ir.model,name:mail.model_mail_wizard_invite msgid "Invite wizard" -msgstr "" +msgstr "Kreator zaproszenia" #. module: mail #: selection:mail.mail,state:0 @@ -282,14 +282,14 @@ msgstr "Odpowiedź do" #: code:addons/mail/wizard/invite.py:36 #, python-format msgid "
You have been invited to follow %s.
" -msgstr "" +msgstr "
Zostałeś zaproszony na %s.
" #. module: mail #: help:mail.group,message_unread:0 #: help:mail.thread,message_unread:0 #: help:res.partner,message_unread:0 msgid "If checked new messages require your attention." -msgstr "" +msgstr "Jeśli zaznaczone, to wiadomość wymaga twojej uwagi" #. module: mail #: field:mail.group,image_medium:0 @@ -305,7 +305,7 @@ msgstr "" #. module: mail #: field:mail.message.subtype,name:0 msgid "Message Type" -msgstr "" +msgstr "Typ Wiadomości" #. module: mail #: field:mail.mail,auto_delete:0 @@ -318,21 +318,21 @@ msgstr "Usuń automatycznie" #: view:mail.group:0 #, python-format msgid "Unfollow" -msgstr "" +msgstr "Przestań obserwować" #. module: mail #. openerp-web #: code:addons/mail/static/src/xml/mail.xml:246 #, python-format msgid "show one more message" -msgstr "" +msgstr "pokaż jeszcze jedną wiadomość" #. module: mail #: code:addons/mail/mail_mail.py:71 #: code:addons/mail/res_users.py:76 #, python-format msgid "Invalid Action!" -msgstr "" +msgstr "Niedozwolona akcja!" #. module: mail #. openerp-web @@ -352,7 +352,7 @@ msgstr "Emaile" #. module: mail #: field:mail.followers,partner_id:0 msgid "Related Partner" -msgstr "" +msgstr "Powiązany partner" #. module: mail #: help:mail.group,message_summary:0 @@ -386,7 +386,7 @@ msgstr "" #. module: mail #: model:ir.ui.menu,name:mail.mail_my_stuff msgid "Organizer" -msgstr "" +msgstr "Organizator" #. module: mail #: field:mail.compose.message,subject:0 @@ -432,7 +432,7 @@ msgstr "" #: field:mail.compose.message,to_read:0 #: field:mail.message,to_read:0 msgid "To read" -msgstr "" +msgstr "Do przeczytania" #. module: mail #: view:mail.compose.message:0 @@ -454,7 +454,7 @@ msgstr "Nie udało się" #. module: mail #: model:mail.message.subtype,name:mail.mt_crm_won msgid "Won" -msgstr "" +msgstr "Sukces" #. module: mail #. openerp-web @@ -473,14 +473,14 @@ msgstr "" #: model:ir.actions.client,name:mail.action_mail_archives_feeds #: model:ir.ui.menu,name:mail.mail_archivesfeeds msgid "Archives" -msgstr "" +msgstr "Archiwum" #. module: mail #. openerp-web #: code:addons/mail/static/src/xml/mail.xml:82 #, python-format msgid "Delete this attachment" -msgstr "" +msgstr "Usuń ten załącznik" #. module: mail #: field:mail.compose.message,message_id:0 @@ -499,7 +499,7 @@ msgstr "" #: field:mail.compose.message,type:0 #: field:mail.message,type:0 msgid "Type" -msgstr "" +msgstr "Typ" #. module: mail #: selection:mail.compose.message,type:0 @@ -511,23 +511,23 @@ msgstr "" #. module: mail #: field:ir.ui.menu,mail_group_id:0 msgid "Mail Group" -msgstr "" +msgstr "Grupa mailowa" #. module: mail #: selection:res.partner,notification_email_send:0 msgid "Comments and Emails" -msgstr "" +msgstr "Komentarze i emaile" #. module: mail #: model:ir.model,name:mail.model_mail_favorite msgid "Favorite messages" -msgstr "" +msgstr "Ulubione wiadomości" #. module: mail #: code:addons/mail/res_users.py:97 #, python-format msgid "%s has joined the %s network." -msgstr "" +msgstr "%s dołączył do sieci %s." #. module: mail #: help:mail.group,image_small:0 @@ -560,17 +560,17 @@ msgstr "" #. module: mail #: field:mail.group,group_public_id:0 msgid "Authorized Group" -msgstr "" +msgstr "Grupa upoważniona" #. module: mail #: view:mail.group:0 msgid "Join Group" -msgstr "" +msgstr "Dołacz do grupy" #. module: mail #: help:mail.mail,email_from:0 msgid "Message sender, taken from user preferences." -msgstr "" +msgstr "Nadawca wiadomości pobrany z preferencji" #. module: mail #: code:addons/mail/wizard/invite.py:39 @@ -582,7 +582,7 @@ msgstr "" #: field:mail.compose.message,parent_id:0 #: field:mail.message,parent_id:0 msgid "Parent Message" -msgstr "" +msgstr "Wiadomość nadrzędna" #. module: mail #: field:mail.compose.message,res_id:0 @@ -618,7 +618,7 @@ msgstr "" #. module: mail #: model:ir.model,name:mail.model_mail_thread msgid "Email Thread" -msgstr "" +msgstr "Wątek email" #. module: mail #: view:mail.mail:0 @@ -646,6 +646,8 @@ msgid "" "You may not create a user. To create new users, you should use the " "\"Settings > Users\" menu." msgstr "" +"Nie możesz utworzyć użytkownika. Do tworzenia użytkownika służy menu " +"\"Ustawienia - Użytkownicy\"." #. module: mail #: model:ir.actions.act_window,name:mail.action_view_mail_message @@ -661,14 +663,14 @@ msgstr "Wiadomości" #: help:mail.followers,res_model:0 #: help:mail.wizard.invite,res_model:0 msgid "Model of the followed resource" -msgstr "" +msgstr "Model następnego zasobu" #. module: mail #. openerp-web #: code:addons/mail/static/src/xml/mail.xml:271 #, python-format msgid "like" -msgstr "" +msgstr "podobny do" #. module: mail #: view:mail.compose.message:0 @@ -687,7 +689,7 @@ msgstr "" #. module: mail #: field:mail.notification,partner_id:0 msgid "Contact" -msgstr "" +msgstr "Kontakt" #. module: mail #: view:mail.group:0 @@ -704,7 +706,7 @@ msgstr "" #. module: mail #: view:mail.message:0 msgid "Has attachments" -msgstr "" +msgstr "Ma załączniki" #. module: mail #: view:mail.mail:0 @@ -717,7 +719,7 @@ msgstr "" msgid "" "The following partners chosen as recipients for the email have no email " "address linked :" -msgstr "" +msgstr "Następujący partnerzy wybrani jako adresaci nie mają adresów email:" #. module: mail #: help:mail.alias,alias_defaults:0 @@ -738,7 +740,7 @@ msgstr "" #: view:mail.mail:0 #: selection:mail.message,type:0 msgid "Comment" -msgstr "" +msgstr "Komentarz" #. module: mail #: model:ir.actions.client,help:mail.action_mail_inbox_feeds @@ -765,7 +767,7 @@ msgstr "" #: code:addons/mail/static/src/xml/mail.xml:161 #, python-format msgid "Compose a new message" -msgstr "" +msgstr "Utwórz nową wiadomość" #. module: mail #: view:mail.mail:0 @@ -777,7 +779,7 @@ msgstr "Wyślij teraz" #, python-format msgid "" "Unable to send email, please configure the sender's email address or alias." -msgstr "" +msgstr "Nie można wysłać maila. Skonfiguruj adres nadawcy lub alias." #. module: mail #: help:res.users,alias_id:0 @@ -785,11 +787,13 @@ msgid "" "Email address internally associated with this user. Incoming emails will " "appear in the user's notifications." msgstr "" +"Adres email powiązany z tym użytkownikiem. Przychodzące maile pojawią się w " +"powiadomieniach." #. module: mail #: field:mail.group,image:0 msgid "Photo" -msgstr "" +msgstr "Zdjęcie" #. module: mail #. openerp-web @@ -804,7 +808,7 @@ msgstr "" #: help:mail.compose.message,vote_user_ids:0 #: help:mail.message,vote_user_ids:0 msgid "Users that voted for this message" -msgstr "" +msgstr "Użytkownicy głosujący za tą wiadomością" #. module: mail #: help:mail.group,alias_id:0 @@ -812,6 +816,7 @@ msgid "" "The email address associated with this group. New emails received will " "automatically create new topics." msgstr "" +"Adres związany z grupą. Nowe przychodzące maile będą tworzyć nowe tematy." #. module: mail #: view:mail.mail:0 @@ -826,13 +831,13 @@ msgstr "Przeszukaj wiadomości" #. module: mail #: model:mail.message.subtype,name:mail.mt_issue_canceled msgid "Canceled" -msgstr "" +msgstr "Anulowano" #. module: mail #: field:mail.compose.message,child_ids:0 #: field:mail.message,child_ids:0 msgid "Child Messages" -msgstr "" +msgstr "Wiadomości podrzędne" #. module: mail #: help:mail.compose.message,to_read:0 @@ -843,12 +848,12 @@ msgstr "" #. module: mail #: field:mail.alias,alias_user_id:0 msgid "Owner" -msgstr "" +msgstr "Właściciel" #. module: mail #: model:ir.model,name:mail.model_res_users msgid "Users" -msgstr "" +msgstr "Użytkownicy" #. module: mail #: model:ir.model,name:mail.model_mail_message @@ -859,13 +864,13 @@ msgstr "" #: field:mail.vote,message_id:0 #: field:mail.wizard.invite,message:0 msgid "Message" -msgstr "" +msgstr "Wiadomość" #. module: mail #: model:ir.actions.client,name:mail.action_mail_star_feeds #: model:ir.ui.menu,name:mail.mail_starfeeds msgid "Todo" -msgstr "" +msgstr "Do zrobienia" #. module: mail #: help:mail.followers,res_id:0 @@ -877,39 +882,39 @@ msgstr "" #: field:mail.compose.message,body:0 #: field:mail.message,body:0 msgid "Contents" -msgstr "" +msgstr "Zawartość" #. module: mail #: model:ir.actions.act_window,name:mail.action_view_mail_alias #: model:ir.ui.menu,name:mail.mail_alias_menu msgid "Aliases" -msgstr "" +msgstr "Aliasy" #. module: mail #: field:mail.compose.message,vote_user_ids:0 #: field:mail.message,vote_user_ids:0 msgid "Votes" -msgstr "" +msgstr "Głosy" #. module: mail #: view:mail.group:0 msgid "Group" -msgstr "" +msgstr "Grupa" #. module: mail #: field:mail.group,public:0 msgid "Privacy" -msgstr "" +msgstr "Prywatność" #. module: mail #: view:mail.mail:0 msgid "Notification" -msgstr "" +msgstr "Powiadomienie" #. module: mail #: model:ir.ui.menu,name:mail.group_support_ir_ui_menu msgid "Support" -msgstr "" +msgstr "Wsparcie" #. module: mail #: view:mail.wizard.invite:0 @@ -919,7 +924,7 @@ msgstr "" #. module: mail #: model:mail.message.subtype,name:mail.mt_issue_closed msgid "Closed" -msgstr "" +msgstr "Zamknięte" #. module: mail #: field:mail.alias,alias_force_thread_id:0 @@ -929,7 +934,7 @@ msgstr "" #. module: mail #: model:ir.ui.menu,name:mail.mail_group_root msgid "My Groups" -msgstr "" +msgstr "Moje grupy" #. module: mail #: model:ir.actions.client,help:mail.action_mail_archives_feeds @@ -943,12 +948,20 @@ msgid "" "

\n" " " msgstr "" +"

\n" +" Nie ma wiadomości i żadnej wiadomości nie wysłano.\n" +"

\n" +" Kliknij ikonę w prawym górnym rogu, aby utworzyć " +"wiadomość.\n" +" Będzie ona wysłana jeśli jest wewnętrzny kontakt.\n" +"

\n" +" " #. module: mail #: view:mail.mail:0 #: field:mail.mail,state:0 msgid "Status" -msgstr "" +msgstr "Stan" #. module: mail #: view:mail.mail:0 @@ -976,7 +989,7 @@ msgstr "" #: field:mail.message,notification_ids:0 #: view:mail.notification:0 msgid "Notifications" -msgstr "" +msgstr "Powiadomienia" #. module: mail #: view:mail.alias:0 @@ -1004,17 +1017,17 @@ msgstr "" #. module: mail #: view:mail.mail:0 msgid "by" -msgstr "" +msgstr "przez" #. module: mail #: model:ir.ui.menu,name:mail.group_best_sales_practices_ir_ui_menu msgid "Best Sales Practices" -msgstr "" +msgstr "Najlepsze praktyki sprzedaży" #. module: mail #: selection:mail.group,public:0 msgid "Selected Group Only" -msgstr "" +msgstr "Tylko wybrane grupy" #. module: mail #: field:mail.group,message_is_follower:0 @@ -1034,12 +1047,12 @@ msgstr "Użytkownik" #. module: mail #: view:mail.group:0 msgid "Groups" -msgstr "" +msgstr "Grupy" #. module: mail #: view:mail.message:0 msgid "Messages Search" -msgstr "" +msgstr "Przeszukiwanie wiadomości" #. module: mail #: field:mail.compose.message,date:0 @@ -1052,7 +1065,7 @@ msgstr "Data" #: code:addons/mail/static/src/xml/mail.xml:34 #, python-format msgid "Post" -msgstr "" +msgstr "Zaksięguj" #. module: mail #: view:mail.mail:0 @@ -1067,14 +1080,14 @@ msgstr "Rozszerzone filtry..." #: field:res.partner,message_comment_ids:0 #: help:res.partner,message_comment_ids:0 msgid "Comments and emails" -msgstr "" +msgstr "Komentarze i emaile" #. module: mail #. openerp-web #: code:addons/mail/static/src/xml/mail.xml:107 #, python-format msgid "To:" -msgstr "" +msgstr "Do:" #. module: mail #. openerp-web @@ -1086,33 +1099,33 @@ msgstr "" #. module: mail #: model:ir.model,name:mail.model_res_groups msgid "Access Groups" -msgstr "" +msgstr "Grupy dostępu" #. module: mail #: field:mail.message.subtype,default:0 msgid "Default" -msgstr "" +msgstr "Domyślne" #. module: mail #. openerp-web #: code:addons/mail/static/src/xml/mail.xml:245 #, python-format msgid "show more message" -msgstr "" +msgstr "pokaż więcej wiadomości" #. module: mail #. openerp-web #: code:addons/mail/static/src/xml/mail.xml:213 #, python-format msgid "Mark as Todo" -msgstr "" +msgstr "Oznacz jako Do zrobienia" #. module: mail #: field:mail.group,message_summary:0 #: field:mail.thread,message_summary:0 #: field:res.partner,message_summary:0 msgid "Summary" -msgstr "" +msgstr "Podsumowanie" #. module: mail #: field:mail.compose.message,subtype_id:0 @@ -1120,25 +1133,25 @@ msgstr "" #: field:mail.message,subtype_id:0 #: view:mail.message.subtype:0 msgid "Subtype" -msgstr "" +msgstr "Podtyp" #. module: mail #: view:mail.group:0 msgid "Group Form" -msgstr "" +msgstr "Formularz grupy" #. module: mail #. openerp-web #: code:addons/mail/static/src/xml/mail.xml:247 #, python-format msgid "more messages" -msgstr "" +msgstr "więcej wiadomości" #. module: mail #: code:addons/mail/update.py:93 #, python-format msgid "Error" -msgstr "" +msgstr "Błąd" #. module: mail #. openerp-web @@ -1151,7 +1164,7 @@ msgstr "" #: sql_constraint:mail.alias:0 msgid "" "Unfortunately this email alias is already used, please choose a unique one" -msgstr "" +msgstr "Niestety ten alias jest już zajęty, Wybierz inny." #. module: mail #: help:mail.alias,alias_user_id:0 @@ -1203,19 +1216,19 @@ msgstr "" #. module: mail #: help:mail.mail,auto_delete:0 msgid "Permanently delete this email after sending it, to save space" -msgstr "" +msgstr "Ostatecznie usuń tę wiadomość po wysłaniu. Dla oszczędności miejsca." #. module: mail #: model:ir.actions.client,name:mail.action_mail_group_feeds msgid "Discussion Group" -msgstr "" +msgstr "Grupa dyskusyjna" #. module: mail #. openerp-web #: code:addons/mail/static/src/xml/mail.xml:209 #, python-format msgid "Done" -msgstr "" +msgstr "Wykonano" #. module: mail #: help:mail.message.subtype,res_model:0 @@ -1226,7 +1239,7 @@ msgstr "" #. module: mail #: model:mail.message.subtype,name:mail.mt_comment msgid "Discussions" -msgstr "" +msgstr "Dyskusje" #. module: mail #. openerp-web @@ -1238,7 +1251,7 @@ msgstr "" #. module: mail #: model:ir.ui.menu,name:mail.group_all_employees_ir_ui_menu msgid "Whole Company" -msgstr "" +msgstr "Cała firma" #. module: mail #. openerp-web @@ -1263,17 +1276,17 @@ msgstr "Miesiąc tworzenia" #: code:addons/mail/static/src/xml/mail.xml:257 #, python-format msgid "Compose new Message" -msgstr "" +msgstr "Utwórz nową wiadomość" #. module: mail #: field:mail.group,menu_id:0 msgid "Related Menu" -msgstr "" +msgstr "Powiązane menu" #. module: mail #: view:mail.message:0 msgid "Content" -msgstr "" +msgstr "Treść" #. module: mail #: field:mail.mail,email_to:0 @@ -1288,25 +1301,25 @@ msgstr "" #. module: mail #: selection:mail.group,public:0 msgid "Private" -msgstr "" +msgstr "Prywatne" #. module: mail #: model:mail.message.subtype,name:mail.mt_issue_new msgid "New" -msgstr "" +msgstr "Nowe" #. module: mail #: field:mail.compose.message,notified_partner_ids:0 #: field:mail.message,notified_partner_ids:0 msgid "Notified partners" -msgstr "" +msgstr "Powiadomieni partnerzy" #. module: mail #. openerp-web #: code:addons/mail/static/src/xml/mail_followers.xml:22 #, python-format msgid "Invite others" -msgstr "" +msgstr "Zaproś innych" #. module: mail #: help:mail.group,public:0 @@ -1314,11 +1327,13 @@ msgid "" "This group is visible by non members. Invisible groups can add " "members through the invite button." msgstr "" +"Ta grupa jest niewidoczna dla nieczłonków. Niewidoczne grupy mogą dodawać " +"członków przez przycisk zaproszenia." #. module: mail #: model:ir.ui.menu,name:mail.group_board_ir_ui_menu msgid "Board meetings" -msgstr "" +msgstr "Spotkania" #. module: mail #: field:mail.alias,alias_model_id:0 @@ -1329,18 +1344,18 @@ msgstr "" #: help:mail.compose.message,message_id:0 #: help:mail.message,message_id:0 msgid "Message unique identifier" -msgstr "" +msgstr "Unikalny identyfikator wiadomości" #. module: mail #: field:mail.group,description:0 msgid "Description" -msgstr "" +msgstr "Opis" #. module: mail #: model:mail.message.subtype,name:mail.mt_crm_stage #: model:mail.message.subtype,name:mail.mt_issue_change msgid "Stage Changed" -msgstr "" +msgstr "Zmieniono etap" #. module: mail #: model:ir.model,name:mail.model_mail_followers @@ -1360,7 +1375,7 @@ msgstr "" #. module: mail #: selection:res.partner,notification_email_send:0 msgid "Never" -msgstr "" +msgstr "Nigdy" #. module: mail #: field:mail.mail,mail_server_id:0 @@ -1371,7 +1386,7 @@ msgstr "Serwer poczty wychodzącej" #: code:addons/mail/mail_message.py:813 #, python-format msgid "Partners email addresses not found" -msgstr "" +msgstr "Nie znaleziono adresów partnerów" #. module: mail #: view:mail.mail:0 @@ -1384,19 +1399,19 @@ msgstr "Wysłane" #: model:ir.ui.menu,name:mail.menu_email_favorites #: view:mail.favorite:0 msgid "Favorites" -msgstr "" +msgstr "Ulubione" #. module: mail #: help:res.partner,notification_email_send:0 msgid "" "Choose in which case you want to receive an email when you receive new feeds." -msgstr "" +msgstr "Wybierz gdzie chcesz otrzymywać pocztę przy pobieraniu." #. module: mail #: model:ir.actions.act_window,name:mail.action_view_groups #: model:ir.ui.menu,name:mail.mail_allgroups msgid "Join a group" -msgstr "" +msgstr "Dołącz do grupy" #. module: mail #: model:ir.actions.client,help:mail.action_mail_group_feeds @@ -1406,6 +1421,10 @@ msgid "" "

\n" " " msgstr "" +"

\n" +" Brak wiadomości w tej grupie.\n" +"

\n" +" " #. module: mail #: view:mail.group:0 @@ -1415,20 +1434,24 @@ msgid "" "installed\n" " the portal module." msgstr "" +"Ta grupa jest widoczna dla każdego,\n" +" włączając klientów, jeśli jest " +"zainstalowany\n" +" moduł portalu." #. module: mail #. openerp-web #: code:addons/mail/static/src/xml/mail.xml:210 #, python-format msgid "Set back to Todo" -msgstr "" +msgstr "Ustaw z powrotem na Do zrobienia" #. module: mail #. openerp-web #: code:addons/mail/static/src/xml/mail.xml:113 #, python-format msgid "this document" -msgstr "" +msgstr "ten dokument" #. module: mail #: field:mail.compose.message,filter_id:0 @@ -1438,12 +1461,12 @@ msgstr "Filtry" #. module: mail #: model:mail.message.subtype,name:mail.mt_crm_lost msgid "Lost" -msgstr "" +msgstr "Porażka" #. module: mail #: field:mail.alias,alias_defaults:0 msgid "Default Values" -msgstr "" +msgstr "Wartości domyślne" #. module: mail #: help:base.config.settings,alias_domain:0 @@ -1456,14 +1479,14 @@ msgstr "" #: field:mail.compose.message,favorite_user_ids:0 #: field:mail.message,favorite_user_ids:0 msgid "Favorite" -msgstr "" +msgstr "Ulubione" #. module: mail #. openerp-web #: code:addons/mail/static/src/xml/mail.xml:125 #, python-format msgid "others..." -msgstr "" +msgstr "inni..." #. module: mail #: view:mail.alias:0 @@ -1476,7 +1499,7 @@ msgstr "" #. module: mail #: model:ir.model,name:mail.model_mail_mail msgid "Outgoing Mails" -msgstr "" +msgstr "Wychodzace" #. module: mail #: help:mail.compose.message,notification_ids:0 @@ -1490,7 +1513,7 @@ msgstr "" #: model:ir.ui.menu,name:mail.mail_feeds #: model:ir.ui.menu,name:mail.mail_feeds_main msgid "Messaging" -msgstr "" +msgstr "Wiadomości" #. module: mail #: view:mail.alias:0 @@ -1501,7 +1524,7 @@ msgstr "" #. module: mail #: view:mail.message:0 msgid "Unread" -msgstr "" +msgstr "Nieprzeczytane" #. module: mail #: help:mail.followers,subtype_ids:0 @@ -1515,7 +1538,7 @@ msgstr "" #: help:mail.thread,message_ids:0 #: help:res.partner,message_ids:0 msgid "Messages and communication history" -msgstr "" +msgstr "Wiadomości i historia komunikacji" #. module: mail #: help:mail.mail,references:0 @@ -1525,7 +1548,7 @@ msgstr "" #. module: mail #: field:mail.compose.message,composition_mode:0 msgid "Composition mode" -msgstr "" +msgstr "Tryb pisania" #. module: mail #: field:mail.compose.message,model:0 @@ -1540,7 +1563,7 @@ msgstr "" #: code:addons/mail/static/src/xml/mail.xml:272 #, python-format msgid "unlike" -msgstr "" +msgstr "nie podobny do" #. module: mail #: help:mail.compose.message,author_id:0 @@ -1569,7 +1592,7 @@ msgstr "" #. module: mail #: model:ir.model,name:mail.model_mail_vote msgid "Mail Vote" -msgstr "" +msgstr "Głosowanie nad mailem" #. module: mail #: model:ir.actions.client,help:mail.action_mail_star_feeds @@ -1588,18 +1611,18 @@ msgstr "" #. module: mail #: selection:mail.mail,state:0 msgid "Delivery Failed" -msgstr "" +msgstr "Dostarczenie nieudane" #. module: mail #: field:mail.compose.message,partner_ids:0 msgid "Additional contacts" -msgstr "" +msgstr "Dodatkowe kontakty" #. module: mail #: help:mail.compose.message,parent_id:0 #: help:mail.message,parent_id:0 msgid "Initial thread message." -msgstr "" +msgstr "Wiadomość inicjująca wątek" #. module: mail #: model:ir.ui.menu,name:mail.group_hr_policies_ir_ui_menu @@ -1609,38 +1632,38 @@ msgstr "" #. module: mail #: selection:res.partner,notification_email_send:0 msgid "Emails only" -msgstr "" +msgstr "Tylko emaile" #. module: mail #: model:ir.actions.client,name:mail.action_mail_inbox_feeds #: model:ir.ui.menu,name:mail.mail_inboxfeeds msgid "Inbox" -msgstr "" +msgstr "Przychodzące" #. module: mail #. openerp-web #: code:addons/mail/static/src/xml/mail.xml:58 #, python-format msgid "File" -msgstr "" +msgstr "Plik" #. module: mail #. openerp-web #: code:addons/mail/static/src/js/many2many_tags_email.js:63 #, python-format msgid "Please complete partner's informations and Email" -msgstr "" +msgstr "Uzupełnij informacje o partnerze i email" #. module: mail #: model:ir.actions.act_window,name:mail.action_view_message_subtype #: model:ir.ui.menu,name:mail.menu_message_subtype msgid "Subtypes" -msgstr "" +msgstr "Podtypy" #. module: mail #: model:ir.model,name:mail.model_mail_alias msgid "Email Aliases" -msgstr "" +msgstr "Aliasy email" #. module: mail #: field:mail.group,image_small:0 @@ -1650,7 +1673,7 @@ msgstr "" #. module: mail #: help:mail.mail,reply_to:0 msgid "Preferred response address for the message" -msgstr "" +msgstr "Preferowany adres zwrotny dla wiadomości" #~ msgid "Open Attachments" #~ msgstr "Otwórz załączniki" diff --git a/addons/mail/i18n/pt.po b/addons/mail/i18n/pt.po index a27c85eab49..1b93969542e 100644 --- a/addons/mail/i18n/pt.po +++ b/addons/mail/i18n/pt.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openobject-addons\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2012-12-03 16:01+0000\n" -"PO-Revision-Date: 2011-01-12 19:10+0000\n" -"Last-Translator: OpenERP Administrators \n" +"PO-Revision-Date: 2012-12-10 18:18+0000\n" +"Last-Translator: Andrei Talpa (multibase.pt) \n" "Language-Team: Portuguese \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:52+0000\n" -"X-Generator: Launchpad (build 16335)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:49+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: mail #: field:res.partner,notification_email_send:0 @@ -36,7 +36,7 @@ msgstr "" #: field:mail.compose.message,author_id:0 #: field:mail.message,author_id:0 msgid "Author" -msgstr "" +msgstr "Autor" #. module: mail #: view:mail.mail:0 @@ -51,7 +51,7 @@ msgstr "Destinatários da mensagem" #. module: mail #: view:mail.message:0 msgid "Comments" -msgstr "" +msgstr "Comentários" #. module: mail #: view:mail.alias:0 @@ -88,12 +88,12 @@ msgstr "" #. module: mail #: view:mail.group:0 msgid "Group Name" -msgstr "" +msgstr "Nome do Grupo" #. module: mail #: selection:mail.group,public:0 msgid "Public" -msgstr "" +msgstr "Público" #. module: mail #: view:mail.mail:0 @@ -130,7 +130,7 @@ msgstr "" #: code:addons/mail/static/src/xml/mail.xml:247 #, python-format msgid "show" -msgstr "" +msgstr "mostrar" #. module: mail #: help:mail.message.subtype,default:0 @@ -149,7 +149,7 @@ msgstr "" #: code:addons/mail/static/src/js/mail.js:778 #, python-format msgid "Do you really want to delete this message?" -msgstr "" +msgstr "Deseja mesmo apagar esta mensagem?" #. module: mail #: view:mail.message:0 @@ -160,7 +160,7 @@ msgstr "" #. module: mail #: view:mail.group:0 msgid "Search Groups" -msgstr "" +msgstr "Pesquisar grupos" #. module: mail #. openerp-web @@ -173,7 +173,7 @@ msgstr "" #: code:addons/mail/mail_message.py:646 #, python-format msgid "Access Denied" -msgstr "" +msgstr "Acesso negado" #. module: mail #: help:mail.group,image_medium:0 @@ -305,7 +305,7 @@ msgstr "" #. module: mail #: field:mail.message.subtype,name:0 msgid "Message Type" -msgstr "" +msgstr "Tipo de mensagem" #. module: mail #: field:mail.mail,auto_delete:0 @@ -325,14 +325,14 @@ msgstr "" #: code:addons/mail/static/src/xml/mail.xml:246 #, python-format msgid "show one more message" -msgstr "" +msgstr "mostrar mais uma mensagem" #. module: mail #: code:addons/mail/mail_mail.py:71 #: code:addons/mail/res_users.py:76 #, python-format msgid "Invalid Action!" -msgstr "" +msgstr "Ação inválida!" #. module: mail #. openerp-web @@ -375,7 +375,7 @@ msgstr "" #: selection:mail.compose.message,type:0 #: selection:mail.message,type:0 msgid "System notification" -msgstr "" +msgstr "Notificação do sistema" #. module: mail #: model:ir.model,name:mail.model_res_partner @@ -480,7 +480,7 @@ msgstr "" #: code:addons/mail/static/src/xml/mail.xml:82 #, python-format msgid "Delete this attachment" -msgstr "" +msgstr "Apagar este anexo" #. module: mail #: field:mail.compose.message,message_id:0 @@ -548,7 +548,7 @@ msgstr "Destinatários" #: code:addons/mail/static/src/xml/mail.xml:126 #, python-format msgid "<<<" -msgstr "" +msgstr "<<<" #. module: mail #. openerp-web @@ -560,7 +560,7 @@ msgstr "" #. module: mail #: field:mail.group,group_public_id:0 msgid "Authorized Group" -msgstr "" +msgstr "Grupo autorizado" #. module: mail #: view:mail.group:0 @@ -613,7 +613,7 @@ msgstr "" #: code:addons/mail/static/src/xml/mail.xml:61 #, python-format msgid "/web/binary/upload_attachment" -msgstr "" +msgstr "/web/binary/upload_attachment" #. module: mail #: model:ir.model,name:mail.model_mail_thread @@ -699,12 +699,12 @@ msgstr "" #. module: mail #: model:ir.model,name:mail.model_ir_ui_menu msgid "ir.ui.menu" -msgstr "" +msgstr "ir.ui.menu" #. module: mail #: view:mail.message:0 msgid "Has attachments" -msgstr "" +msgstr "Tem anexos" #. module: mail #: view:mail.mail:0 @@ -758,7 +758,7 @@ msgstr "" #. module: mail #: field:mail.mail,notification:0 msgid "Is Notification" -msgstr "" +msgstr "É notificação" #. module: mail #. openerp-web @@ -798,13 +798,13 @@ msgstr "" #: view:mail.wizard.invite:0 #, python-format msgid "or" -msgstr "" +msgstr "ou" #. module: mail #: help:mail.compose.message,vote_user_ids:0 #: help:mail.message,vote_user_ids:0 msgid "Users that voted for this message" -msgstr "" +msgstr "Utilizadores que votaram nesta mensagem" #. module: mail #: help:mail.group,alias_id:0 @@ -826,7 +826,7 @@ msgstr "Pesquisa de email" #. module: mail #: model:mail.message.subtype,name:mail.mt_issue_canceled msgid "Canceled" -msgstr "" +msgstr "Cancelado" #. module: mail #: field:mail.compose.message,child_ids:0 @@ -848,7 +848,7 @@ msgstr "" #. module: mail #: model:ir.model,name:mail.model_res_users msgid "Users" -msgstr "" +msgstr "Utilizadores" #. module: mail #: model:ir.model,name:mail.model_mail_message @@ -859,7 +859,7 @@ msgstr "" #: field:mail.vote,message_id:0 #: field:mail.wizard.invite,message:0 msgid "Message" -msgstr "" +msgstr "Mensagem" #. module: mail #: model:ir.actions.client,name:mail.action_mail_star_feeds @@ -877,7 +877,7 @@ msgstr "" #: field:mail.compose.message,body:0 #: field:mail.message,body:0 msgid "Contents" -msgstr "" +msgstr "Conteúdos" #. module: mail #: model:ir.actions.act_window,name:mail.action_view_mail_alias @@ -889,7 +889,7 @@ msgstr "" #: field:mail.compose.message,vote_user_ids:0 #: field:mail.message,vote_user_ids:0 msgid "Votes" -msgstr "" +msgstr "Votos" #. module: mail #: view:mail.group:0 @@ -899,17 +899,17 @@ msgstr "" #. module: mail #: field:mail.group,public:0 msgid "Privacy" -msgstr "" +msgstr "Privacidade" #. module: mail #: view:mail.mail:0 msgid "Notification" -msgstr "" +msgstr "Notificação" #. module: mail #: model:ir.ui.menu,name:mail.group_support_ir_ui_menu msgid "Support" -msgstr "" +msgstr "Suporte" #. module: mail #: view:mail.wizard.invite:0 @@ -929,7 +929,7 @@ msgstr "" #. module: mail #: model:ir.ui.menu,name:mail.mail_group_root msgid "My Groups" -msgstr "" +msgstr "Os meus grupos" #. module: mail #: model:ir.actions.client,help:mail.action_mail_archives_feeds @@ -948,7 +948,7 @@ msgstr "" #: view:mail.mail:0 #: field:mail.mail,state:0 msgid "Status" -msgstr "" +msgstr "Estado" #. module: mail #: view:mail.mail:0 @@ -976,7 +976,7 @@ msgstr "" #: field:mail.message,notification_ids:0 #: view:mail.notification:0 msgid "Notifications" -msgstr "" +msgstr "Notificações" #. module: mail #: view:mail.alias:0 @@ -1034,7 +1034,7 @@ msgstr "Utilizador" #. module: mail #: view:mail.group:0 msgid "Groups" -msgstr "" +msgstr "Grupos" #. module: mail #: view:mail.message:0 @@ -1074,7 +1074,7 @@ msgstr "" #: code:addons/mail/static/src/xml/mail.xml:107 #, python-format msgid "To:" -msgstr "" +msgstr "Para:" #. module: mail #. openerp-web @@ -1091,7 +1091,7 @@ msgstr "" #. module: mail #: field:mail.message.subtype,default:0 msgid "Default" -msgstr "" +msgstr "Predefinido" #. module: mail #. openerp-web @@ -1112,7 +1112,7 @@ msgstr "" #: field:mail.thread,message_summary:0 #: field:res.partner,message_summary:0 msgid "Summary" -msgstr "" +msgstr "Resumo" #. module: mail #: field:mail.compose.message,subtype_id:0 @@ -1120,7 +1120,7 @@ msgstr "" #: field:mail.message,subtype_id:0 #: view:mail.message.subtype:0 msgid "Subtype" -msgstr "" +msgstr "Subtipo" #. module: mail #: view:mail.group:0 @@ -1132,13 +1132,13 @@ msgstr "" #: code:addons/mail/static/src/xml/mail.xml:247 #, python-format msgid "more messages" -msgstr "" +msgstr "mais mensagens" #. module: mail #: code:addons/mail/update.py:93 #, python-format msgid "Error" -msgstr "" +msgstr "Erro" #. module: mail #. openerp-web @@ -1209,7 +1209,7 @@ msgstr "" #. module: mail #: model:ir.actions.client,name:mail.action_mail_group_feeds msgid "Discussion Group" -msgstr "" +msgstr "Grupo de discussão" #. module: mail #. openerp-web @@ -1227,7 +1227,7 @@ msgstr "" #. module: mail #: model:mail.message.subtype,name:mail.mt_comment msgid "Discussions" -msgstr "" +msgstr "Discussões" #. module: mail #. openerp-web @@ -1247,7 +1247,7 @@ msgstr "" #: view:mail.compose.message:0 #, python-format msgid "and" -msgstr "" +msgstr "e" #. module: mail #: help:mail.mail,body_html:0 @@ -1274,7 +1274,7 @@ msgstr "" #. module: mail #: view:mail.message:0 msgid "Content" -msgstr "" +msgstr "Conteúdo" #. module: mail #: field:mail.mail,email_to:0 @@ -1284,17 +1284,17 @@ msgstr "Para" #. module: mail #: model:ir.model,name:mail.model_mail_message_subtype msgid "mail_message_subtype" -msgstr "" +msgstr "mail_message_subtype" #. module: mail #: selection:mail.group,public:0 msgid "Private" -msgstr "" +msgstr "Privado" #. module: mail #: model:mail.message.subtype,name:mail.mt_issue_new msgid "New" -msgstr "" +msgstr "Novo" #. module: mail #: field:mail.compose.message,notified_partner_ids:0 @@ -1335,7 +1335,7 @@ msgstr "Identificador de mensagem exclusivo" #. module: mail #: field:mail.group,description:0 msgid "Description" -msgstr "" +msgstr "Descrição" #. module: mail #: model:mail.message.subtype,name:mail.mt_crm_stage @@ -1361,7 +1361,7 @@ msgstr "" #. module: mail #: selection:res.partner,notification_email_send:0 msgid "Never" -msgstr "" +msgstr "Nunca" #. module: mail #: field:mail.mail,mail_server_id:0 @@ -1429,7 +1429,7 @@ msgstr "" #: code:addons/mail/static/src/xml/mail.xml:113 #, python-format msgid "this document" -msgstr "" +msgstr "este documento" #. module: mail #: field:mail.compose.message,filter_id:0 @@ -1444,7 +1444,7 @@ msgstr "" #. module: mail #: field:mail.alias,alias_defaults:0 msgid "Default Values" -msgstr "" +msgstr "Valores predefinidos" #. module: mail #: help:base.config.settings,alias_domain:0 @@ -1457,14 +1457,14 @@ msgstr "" #: field:mail.compose.message,favorite_user_ids:0 #: field:mail.message,favorite_user_ids:0 msgid "Favorite" -msgstr "" +msgstr "Favorito" #. module: mail #. openerp-web #: code:addons/mail/static/src/xml/mail.xml:125 #, python-format msgid "others..." -msgstr "" +msgstr "outros..." #. module: mail #: view:mail.alias:0 @@ -1497,7 +1497,7 @@ msgstr "" #: view:mail.alias:0 #: field:mail.message.subtype,res_model:0 msgid "Model" -msgstr "" +msgstr "Modelo" #. module: mail #: view:mail.message:0 @@ -1617,14 +1617,14 @@ msgstr "" #: model:ir.actions.client,name:mail.action_mail_inbox_feeds #: model:ir.ui.menu,name:mail.mail_inboxfeeds msgid "Inbox" -msgstr "" +msgstr "Caixa de entrada" #. module: mail #. openerp-web #: code:addons/mail/static/src/xml/mail.xml:58 #, python-format msgid "File" -msgstr "" +msgstr "Ficheiro" #. module: mail #. openerp-web @@ -1637,7 +1637,7 @@ msgstr "" #: model:ir.actions.act_window,name:mail.action_view_message_subtype #: model:ir.ui.menu,name:mail.menu_message_subtype msgid "Subtypes" -msgstr "" +msgstr "Subtipos" #. module: mail #: model:ir.model,name:mail.model_mail_alias diff --git a/addons/mrp/i18n/pl.po b/addons/mrp/i18n/pl.po index f2956bb38ee..a3ac104fa3a 100644 --- a/addons/mrp/i18n/pl.po +++ b/addons/mrp/i18n/pl.po @@ -7,14 +7,14 @@ msgstr "" "Project-Id-Version: OpenERP Server 6.0dev\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-12-03 16:02+0000\n" -"PO-Revision-Date: 2011-12-06 11:05+0000\n" +"PO-Revision-Date: 2012-12-10 18:57+0000\n" "Last-Translator: Grzegorz Grzelak (OpenGLOBE.pl) \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:10+0000\n" -"X-Generator: Launchpad (build 16335)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:46+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: mrp #: help:mrp.config.settings,module_mrp_repair:0 @@ -79,7 +79,7 @@ msgstr "Produkty uszkodzone" #. module: mrp #: view:mrp.workcenter:0 msgid "Mrp Workcenter" -msgstr "" +msgstr "Centrum robocze" #. module: mrp #: model:ir.actions.act_window,name:mrp.mrp_routing_action @@ -102,7 +102,7 @@ msgstr "Dla produktów rejestrowanych i materiałów pomocniczych" #: help:mrp.production,message_unread:0 #: help:mrp.production.workcenter.line,message_unread:0 msgid "If checked new messages require your attention." -msgstr "" +msgstr "Jeśli zaznaczone, to wiadomość wymaga twojej uwagi" #. module: mrp #: help:mrp.routing.workcenter,cycle_nbr:0 @@ -205,13 +205,13 @@ msgstr "Dla kupowanych materiałów" #. module: mrp #: field:mrp.config.settings,module_mrp_operations:0 msgid "Allow detailed planning of work order" -msgstr "" +msgstr "Pozwala szczegółowo planować operację" #. module: mrp #: code:addons/mrp/mrp.py:648 #, python-format msgid "Cannot cancel manufacturing order!" -msgstr "" +msgstr "Nie można anulować zamówienia produkcji" #. module: mrp #: field:mrp.workcenter,costs_cycle_account_id:0 @@ -273,7 +273,7 @@ msgstr "Dane główne" #. module: mrp #: field:mrp.config.settings,module_mrp_byproduct:0 msgid "Produce several products from one manufacturing order" -msgstr "" +msgstr "Produkuj kilka produktów z jednego zamówienia produkcji" #. module: mrp #: report:mrp.production.order:0 @@ -309,7 +309,7 @@ msgstr "Odnośnik do pozycji w planie zewnętrznym" #. module: mrp #: model:res.groups,name:mrp.group_mrp_routings msgid "Manage Routings" -msgstr "" +msgstr "Zarządzaj marszrutami" #. module: mrp #: model:ir.model,name:mrp.model_mrp_product_produce @@ -452,7 +452,7 @@ msgstr "Zaplanowana data" #: code:addons/mrp/procurement.py:113 #, python-format msgid "Manufacturing Order %s created." -msgstr "" +msgstr "Zamówienie produkcji %s utworzono." #. module: mrp #: view:mrp.bom:0 @@ -467,6 +467,8 @@ msgid "" "Manufacturing order has been confirmed and is scheduled for " "the %s." msgstr "" +"Zamówienie produkcji zostało potwierdzone i jest zaplanowane " +"dla %s." #. module: mrp #: help:mrp.routing,location_id:0 @@ -493,7 +495,7 @@ msgstr "Struktura zestawień materiałowych" #: code:addons/mrp/mrp.py:1043 #, python-format msgid "Manufacturing order has been canceled." -msgstr "" +msgstr "Zamówienie produkcji zostało anulowane." #. module: mrp #: model:process.node,note:mrp.process_node_serviceproduct0 @@ -503,7 +505,7 @@ msgstr "Typ produktu jest serwis" #. module: mrp #: help:mrp.workcenter,costs_cycle:0 msgid "Specify Cost of Work Center per cycle." -msgstr "" +msgstr "Określ koszt centrum na cykl" #. module: mrp #: model:process.transition,name:mrp.process_transition_bom0 @@ -548,7 +550,7 @@ msgstr "Produkty do wyprodukowania" #. module: mrp #: view:mrp.config.settings:0 msgid "Apply" -msgstr "" +msgstr "Zastosuj" #. module: mrp #: view:mrp.routing:0 @@ -580,7 +582,7 @@ msgstr "Przeszukaj produkcję" #: help:mrp.routing.workcenter,sequence:0 msgid "" "Gives the sequence order when displaying a list of routing Work Centers." -msgstr "" +msgstr "Ustala kolejność wyświetlania w liście centrów." #. module: mrp #: field:mrp.bom,child_complete_ids:0 @@ -635,6 +637,7 @@ msgstr "" #, python-format msgid "Bill of Material has been created for %s product." msgstr "" +"Zestawienie materiałowe zostało utworzone dla produktu %s." #. module: mrp #: model:process.transition,name:mrp.process_transition_billofmaterialrouting0 @@ -715,6 +718,8 @@ msgstr "" #: constraint:mrp.bom:0 msgid "BoM line product should not be same as BoM product." msgstr "" +"Pozycja Zestawienia materiałowego nie może być taka sama jak Produkt " +"Zestawienia." #. module: mrp #: view:mrp.production:0 @@ -851,11 +856,13 @@ msgid "" "Fill this only if you want automatic analytic accounting entries on " "production orders." msgstr "" +"Wypełnij to, jeśli chcesz automatyczną księgowość analityczną do zamówień " +"produkcji." #. module: mrp #: view:mrp.production:0 msgid "Mark as Started" -msgstr "" +msgstr "Oznacz jako Rozpoczęte" #. module: mrp #: view:mrp.production:0 @@ -883,7 +890,7 @@ msgstr "Pilne" #. module: mrp #: view:mrp.production:0 msgid "Manufacturing Orders which are waiting for raw materials." -msgstr "" +msgstr "Zamówienia produkcji, które oczekują na materiały." #. module: mrp #: code:addons/mrp/mrp.py:284 @@ -1004,7 +1011,7 @@ msgstr "Minimalny zapas" #: code:addons/mrp/report/price.py:211 #, python-format msgid "Total Cost of %s %s" -msgstr "" +msgstr "Suma kosztów %s %s" #. module: mrp #: model:process.node,name:mrp.process_node_stockproduct0 @@ -1037,7 +1044,7 @@ msgstr "Grupa właściwości" #. module: mrp #: field:mrp.config.settings,group_mrp_routings:0 msgid "Manage routings and work orders " -msgstr "" +msgstr "Zarządza marszrutami i operacjami " #. module: mrp #: model:process.node,note:mrp.process_node_production0 @@ -1096,7 +1103,7 @@ msgstr "Zamówienia produkcji" #. module: mrp #: selection:mrp.production,state:0 msgid "Awaiting Raw Materials" -msgstr "" +msgstr "Oczekuje na materiały" #. module: mrp #: field:mrp.bom,position:0 @@ -1106,7 +1113,7 @@ msgstr "Odnośnik wewnątrzny" #. module: mrp #: field:mrp.production,product_uos_qty:0 msgid "Product UoS Quantity" -msgstr "" +msgstr "Ilość JS" #. module: mrp #: field:mrp.bom,name:0 @@ -1133,7 +1140,7 @@ msgstr "Tryb" #: help:mrp.production,message_ids:0 #: help:mrp.production.workcenter.line,message_ids:0 msgid "Messages and communication history" -msgstr "" +msgstr "Wiadomości i historia komunikacji" #. module: mrp #: field:mrp.workcenter.load,measure_unit:0 @@ -1230,7 +1237,7 @@ msgstr "Dziennik analityczny" #: code:addons/mrp/report/price.py:139 #, python-format msgid "Supplier Price per Unit of Measure" -msgstr "" +msgstr "Cena u dostawcy na JM" #. module: mrp #: selection:mrp.workcenter.load,time_unit:0 @@ -1242,7 +1249,7 @@ msgstr "Na tydzień" #: field:mrp.production,message_unread:0 #: field:mrp.production.workcenter.line,message_unread:0 msgid "Unread Messages" -msgstr "" +msgstr "Nieprzeczytane wiadomości" #. module: mrp #: model:process.transition,note:mrp.process_transition_stockmts0 @@ -1266,7 +1273,7 @@ msgstr "Uwagi" #. module: mrp #: view:mrp.production:0 msgid "Manufacturing Orders which are ready to start production." -msgstr "" +msgstr "Zamówienia produkcji gotowe do rozpoczęcia." #. module: mrp #: model:ir.model,name:mrp.model_mrp_bom @@ -1287,7 +1294,7 @@ msgstr "Wybierz jednostkę czasu" #: model:ir.ui.menu,name:mrp.menu_mrp_product_form #: view:mrp.config.settings:0 msgid "Products" -msgstr "" +msgstr "Produkty" #. module: mrp #: view:report.workcenter.load:0 @@ -1311,7 +1318,7 @@ msgstr "" #: code:addons/mrp/mrp.py:521 #, python-format msgid "Invalid Action!" -msgstr "" +msgstr "Niedozwolona akcja!" #. module: mrp #: model:process.transition,note:mrp.process_transition_producttostockrules0 @@ -1328,7 +1335,7 @@ msgstr "" #: code:addons/mrp/report/price.py:187 #, python-format msgid "Components Cost of %s %s" -msgstr "" +msgstr "Koszt komponentów %s %s" #. module: mrp #: selection:mrp.workcenter.load,time_unit:0 @@ -1357,7 +1364,7 @@ msgstr "" #: code:addons/mrp/mrp.py:374 #, python-format msgid "%s (copy)" -msgstr "" +msgstr "%s (kopia)" #. module: mrp #: model:ir.model,name:mrp.model_mrp_production_product_line @@ -1425,7 +1432,7 @@ msgstr "Zapotrzebowanie" #. module: mrp #: field:mrp.config.settings,module_product_manufacturer:0 msgid "Define manufacturers on products " -msgstr "" +msgstr "Zdefiniuj producentów produktu " #. module: mrp #: model:ir.actions.act_window,name:mrp.action_view_mrp_product_price_wizard @@ -1469,7 +1476,7 @@ msgstr "Konto dla czasu" #: field:mrp.production,product_uom:0 #: field:mrp.production.product.line,product_uom:0 msgid "Product Unit of Measure" -msgstr "" +msgstr "Jednostka miary produktu" #. module: mrp #: view:mrp.production:0 @@ -1490,7 +1497,7 @@ msgstr "Oczekiwanie" #: code:addons/mrp/mrp.py:1053 #, python-format msgid "Manufacturing order is in production." -msgstr "" +msgstr "Zamówienie produkcji zostało rozpoczęte." #. module: mrp #: field:mrp.bom,active:0 @@ -1533,7 +1540,7 @@ msgstr "" #: code:addons/mrp/mrp.py:1058 #, python-format msgid "Manufacturing order has been done." -msgstr "" +msgstr "Zamówienie produkcji zostało zakończone." #. module: mrp #: model:process.transition,name:mrp.process_transition_minimumstockprocure0 @@ -1559,12 +1566,12 @@ msgstr "Sporządza zapotrzebowania na materiały do produkcji." #: code:addons/mrp/mrp.py:1048 #, python-format msgid "Manufacturing order is ready to produce." -msgstr "" +msgstr "Zamówienie produkcji jest gotowe do rozpoczęcia." #. module: mrp #: field:mrp.production.product.line,product_uos_qty:0 msgid "Product UOS Quantity" -msgstr "" +msgstr "Ilość w JS" #. module: mrp #: field:mrp.workcenter,costs_general_account_id:0 @@ -1580,7 +1587,7 @@ msgstr "Numer ZS" #: code:addons/mrp/mrp.py:521 #, python-format msgid "Cannot delete a manufacturing order in state '%s'." -msgstr "" +msgstr "Nie można usunąć zamówienia produkcji w stanie '%s'." #. module: mrp #: selection:mrp.production,state:0 @@ -1733,7 +1740,7 @@ msgstr "Aprobuj" #. module: mrp #: view:mrp.config.settings:0 msgid "Order" -msgstr "" +msgstr "Zamówienie" #. module: mrp #: view:mrp.property.group:0 @@ -1773,7 +1780,7 @@ msgstr "" #: code:addons/mrp/mrp.py:1039 #, python-format msgid "Manufacturing order has been created." -msgstr "" +msgstr "Zamówienie produkcji zostało utworzone." #. module: mrp #: help:mrp.product.produce,mode:0 @@ -1788,7 +1795,7 @@ msgstr "" #. module: mrp #: model:ir.ui.menu,name:mrp.menu_mrp_production_order_action msgid "Orders Planning" -msgstr "" +msgstr "Planowanie zamówień" #. module: mrp #: field:mrp.workcenter,costs_cycle:0 @@ -1834,6 +1841,9 @@ msgid "" "are products themselves) can also have their own Bill of Material (multi-" "level)." msgstr "" +"Zestawienie materiałowe jest kompozycją produktu. Komponenty (które są też " +"produktami) mogą mieć również swoej zestawienie materiałowe (wielopoziomowe " +"zestawienie materiałowe)." #. module: mrp #: field:mrp.bom,company_id:0 @@ -1847,7 +1857,7 @@ msgstr "Firma" #. module: mrp #: view:mrp.bom:0 msgid "Default Unit of Measure" -msgstr "" +msgstr "Domyślna jednostka miary" #. module: mrp #: field:mrp.workcenter,time_cycle:0 @@ -1873,7 +1883,7 @@ msgstr "Reguła automatycznego zapotrzebowania" #: field:mrp.production,message_ids:0 #: field:mrp.production.workcenter.line,message_ids:0 msgid "Messages" -msgstr "" +msgstr "Wiadomości" #. module: mrp #: view:mrp.production:0 @@ -1886,7 +1896,7 @@ msgstr "Oblicz dane" #: code:addons/mrp/wizard/change_production_qty.py:88 #, python-format msgid "Error!" -msgstr "" +msgstr "Błąd!" #. module: mrp #: code:addons/mrp/report/price.py:139 @@ -1904,7 +1914,7 @@ msgstr "Struktura Zest. Mat." #. module: mrp #: field:mrp.config.settings,module_mrp_jit:0 msgid "Generate procurement in real time" -msgstr "" +msgstr "Generuj zapotrzebowanie w czasie rzeczywistym" #. module: mrp #: field:mrp.bom,date_stop:0 @@ -1930,7 +1940,7 @@ msgstr "Czas wyprodukowania" #: code:addons/mrp/mrp.py:284 #, python-format msgid "Warning" -msgstr "" +msgstr "Ostrzeżenie" #. module: mrp #: field:mrp.bom,product_uos_qty:0 @@ -1940,7 +1950,7 @@ msgstr "Ilość produktu w JS" #. module: mrp #: field:mrp.production,move_prod_id:0 msgid "Product Move" -msgstr "" +msgstr "Przesunięcie produktu" #. module: mrp #: model:ir.actions.act_window,help:mrp.action_report_in_out_picking_tree @@ -1949,6 +1959,8 @@ msgid "" "linked to manufacturing activities, receptions of products and delivery " "orders." msgstr "" +"Tygodniowe odchylenie zapasu pozwala ci określać zmiany stanu zapasów w " +"zależności od aktywności produkcyjnych, przyjęć i wydań." #. module: mrp #: view:mrp.product.produce:0 @@ -1973,6 +1985,8 @@ msgid "" "If the active field is set to False, it will allow you to hide the bills of " "material without removing it." msgstr "" +"Jeśli pole Aktywne jest niezaznaczone, to możesz ukryć Zastawienie " +"materiałowe bez jego usuwania." #. module: mrp #: field:mrp.bom,product_rounding:0 @@ -2044,7 +2058,7 @@ msgstr "Zapotrzebowania" #. module: mrp #: help:mrp.bom,product_rounding:0 msgid "Rounding applied on the product quantity." -msgstr "" +msgstr "Zaokrąglenie zastosowane do ilości" #. module: mrp #: model:process.node,note:mrp.process_node_stock0 @@ -2083,7 +2097,7 @@ msgstr "Czas dla tego kroku w godzinach" #. module: mrp #: field:mrp.config.settings,module_mrp_repair:0 msgid "Manage repairs of products " -msgstr "" +msgstr "Zarządzaj naprawami produktów " #. module: mrp #: help:mrp.config.settings,module_mrp_byproduct:0 @@ -2115,7 +2129,7 @@ msgstr "" #: code:addons/mrp/report/price.py:139 #, python-format msgid "Cost Price per Unit of Measure" -msgstr "" +msgstr "Cena - koszt na jednostkę miary" #. module: mrp #: field:report.mrp.inout,date:0 @@ -2174,7 +2188,7 @@ msgstr "Odn. Zest. Mat." #: field:mrp.production.workcenter.line,message_comment_ids:0 #: help:mrp.production.workcenter.line,message_comment_ids:0 msgid "Comments and emails" -msgstr "" +msgstr "Komentarze i emaile" #. module: mrp #: code:addons/mrp/mrp.py:784 @@ -2183,16 +2197,18 @@ msgid "" "You are going to produce total %s quantities of \"%s\".\n" "But you can only produce up to total %s quantities." msgstr "" +"Zamierasz wyprodukować %s produktu \"%s\".\n" +"Ale możesz wyprodukować tylko %s ." #. module: mrp #: model:process.node,note:mrp.process_node_stockproduct0 msgid "Product type is Stockable or Consumable." -msgstr "" +msgstr "Typ produktu jest Rejestrowany lub Pomocniczy" #. module: mrp #: selection:mrp.production,state:0 msgid "Production Started" -msgstr "" +msgstr "Produkcja rozpoczęta" #. module: mrp #: model:process.node,name:mrp.process_node_procureproducts0 @@ -2218,7 +2234,7 @@ msgstr "Zestawienia materiałowe" #: code:addons/mrp/mrp.py:625 #, python-format msgid "Cannot find a bill of material for this product." -msgstr "" +msgstr "Nie mozna odnaleźć zestawienia materiałowego dla tego produktu." #. module: mrp #: view:product.product:0 @@ -2231,7 +2247,7 @@ msgstr "" #. module: mrp #: field:mrp.config.settings,module_stock_no_autopicking:0 msgid "Manage manual picking to fulfill manufacturing orders " -msgstr "" +msgstr "Wykonaj ręcznie pobranie do zakończenia zamówienia produkcji " #. module: mrp #: view:mrp.routing.workcenter:0 @@ -2248,7 +2264,7 @@ msgstr "Produkcje" #: model:ir.model,name:mrp.model_stock_move_split #: view:mrp.production:0 msgid "Split in Serial Numbers" -msgstr "" +msgstr "Podziel na numery seryjne" #. module: mrp #: help:mrp.bom,product_uos:0 @@ -2337,7 +2353,7 @@ msgstr "" #: model:ir.actions.act_window,name:mrp.action_mrp_configuration #: view:mrp.config.settings:0 msgid "Configure Manufacturing" -msgstr "" +msgstr "Konfiguruj produkcję" #. module: mrp #: view:product.product:0 @@ -2345,11 +2361,15 @@ msgid "" "a manufacturing\n" " order" msgstr "" +"Zamówienie\n" +" produkcji" #. module: mrp #: field:mrp.config.settings,group_mrp_properties:0 msgid "Allow several bill of materials per products using properties" msgstr "" +"Stosuj kilka zestawień materiałowych dla tego samego produktu stosując " +"właściwości" #. module: mrp #: model:ir.actions.act_window,name:mrp.mrp_property_group_action diff --git a/addons/note/i18n/ro.po b/addons/note/i18n/ro.po new file mode 100644 index 00000000000..2f6b6061a01 --- /dev/null +++ b/addons/note/i18n/ro.po @@ -0,0 +1,288 @@ +# Romanian translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-03 16:02+0000\n" +"PO-Revision-Date: 2012-12-10 07:38+0000\n" +"Last-Translator: Fekete Mihai \n" +"Language-Team: Romanian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-12-11 04:49+0000\n" +"X-Generator: Launchpad (build 16356)\n" + +#. module: note +#: field:note.note,memo:0 +msgid "Note Content" +msgstr "Continut Nota" + +#. module: note +#: view:note.stage:0 +msgid "Stages of Notes" +msgstr "Etape Note" + +#. module: note +#: model:note.stage,name:note.demo_note_stage_04 +#: model:note.stage,name:note.note_stage_02 +msgid "This Week" +msgstr "Saptamana aceasta" + +#. module: note +#: model:ir.model,name:note.model_base_config_settings +msgid "base.config.settings" +msgstr "" + +#. module: note +#: model:ir.model,name:note.model_note_tag +msgid "Note Tag" +msgstr "Tag Nota" + +#. module: note +#: model:res.groups,name:note.group_note_fancy +msgid "Notes / Fancy mode" +msgstr "Note / Fancy mode" + +#. module: note +#: model:ir.model,name:note.model_note_note +#: view:note.note:0 +msgid "Note" +msgstr "Nota" + +#. module: note +#: view:note.note:0 +msgid "Group By..." +msgstr "Grupare dupa..." + +#. module: note +#: field:note.note,message_follower_ids:0 +msgid "Followers" +msgstr "Persoane interesate" + +#. module: note +#: model:ir.actions.act_window,help:note.action_note_note +msgid "" +"

\n" +" Click to add a personal note.\n" +"

\n" +" Use notes to organize personal tasks or notes. All\n" +" notes are private; no one else will be able to see them. " +"However\n" +" you can share some notes with other people by inviting " +"followers\n" +" on the note. (Useful for meeting minutes, especially if\n" +" you activate the pad feature for collaborative writings).\n" +"

\n" +" You can customize how you process your notes/tasks by adding,\n" +" removing or modifying columns.\n" +"

\n" +" " +msgstr "" + +#. module: note +#: model:note.stage,name:note.demo_note_stage_01 +#: model:note.stage,name:note.note_stage_01 +msgid "Today" +msgstr "" + +#. module: note +#: model:ir.model,name:note.model_res_users +msgid "Users" +msgstr "" + +#. module: note +#: view:note.note:0 +msgid "í" +msgstr "" + +#. module: note +#: view:note.stage:0 +msgid "Stage of Notes" +msgstr "" + +#. module: note +#: field:note.note,message_unread:0 +msgid "Unread Messages" +msgstr "" + +#. module: note +#: field:note.note,current_partner_id:0 +msgid "unknown" +msgstr "" + +#. module: note +#: view:note.note:0 +msgid "By sticky note Category" +msgstr "" + +#. module: note +#: help:note.note,message_unread:0 +msgid "If checked new messages require your attention." +msgstr "" + +#. module: note +#: field:note.stage,name:0 +msgid "Stage Name" +msgstr "" + +#. module: note +#: field:note.note,message_is_follower:0 +msgid "Is a Follower" +msgstr "" + +#. module: note +#: model:note.stage,name:note.demo_note_stage_02 +msgid "Tomorrow" +msgstr "" + +#. module: note +#: view:note.note:0 +#: field:note.note,open:0 +msgid "Active" +msgstr "" + +#. module: note +#: help:note.stage,user_id:0 +msgid "Owner of the note stage." +msgstr "" + +#. module: note +#: model:ir.ui.menu,name:note.menu_notes_stage +msgid "Categories" +msgstr "" + +#. module: note +#: field:note.note,message_comment_ids:0 +#: help:note.note,message_comment_ids:0 +msgid "Comments and emails" +msgstr "" + +#. module: note +#: field:note.tag,name:0 +msgid "Tag Name" +msgstr "" + +#. module: note +#: field:note.note,message_ids:0 +msgid "Messages" +msgstr "" + +#. module: note +#: view:base.config.settings:0 +#: model:ir.actions.act_window,name:note.action_note_note +#: model:ir.ui.menu,name:note.menu_note_notes +#: view:note.note:0 +#: model:note.stage,name:note.note_stage_04 +msgid "Notes" +msgstr "" + +#. module: note +#: model:note.stage,name:note.demo_note_stage_03 +#: model:note.stage,name:note.note_stage_03 +msgid "Later" +msgstr "" + +#. module: note +#: model:ir.model,name:note.model_note_stage +msgid "Note Stage" +msgstr "" + +#. module: note +#: field:note.note,message_summary:0 +msgid "Summary" +msgstr "" + +#. module: note +#: help:note.stage,sequence:0 +msgid "Used to order the note stages" +msgstr "" + +#. module: note +#: field:note.note,stage_ids:0 +msgid "Stages of Users" +msgstr "" + +#. module: note +#: field:note.note,name:0 +msgid "Note Summary" +msgstr "" + +#. module: note +#: model:ir.actions.act_window,name:note.action_note_stage +#: view:note.note:0 +msgid "Stages" +msgstr "" + +#. module: note +#: help:note.note,message_ids:0 +msgid "Messages and communication history" +msgstr "" + +#. module: note +#: view:note.note:0 +msgid "Delete" +msgstr "" + +#. module: note +#: field:note.note,color:0 +msgid "Color Index" +msgstr "" + +#. module: note +#: field:note.note,sequence:0 +#: field:note.stage,sequence:0 +msgid "Sequence" +msgstr "" + +#. module: note +#: field:note.note,tag_ids:0 +msgid "Tags" +msgstr "" + +#. module: note +#: view:note.note:0 +msgid "Archive" +msgstr "" + +#. module: note +#: field:base.config.settings,module_note_pad:0 +msgid "Use collaborative pads (etherpad)" +msgstr "" + +#. module: note +#: help:note.note,message_summary:0 +msgid "" +"Holds the Chatter summary (number of messages, ...). This summary is " +"directly in html format in order to be inserted in kanban views." +msgstr "" + +#. module: note +#: field:base.config.settings,group_note_fancy:0 +msgid "Use fancy layouts for notes" +msgstr "" + +#. module: note +#: field:note.stage,user_id:0 +msgid "Owner" +msgstr "" + +#. module: note +#: view:note.note:0 +#: field:note.note,stage_id:0 +msgid "Stage" +msgstr "" + +#. module: note +#: field:note.note,date_done:0 +msgid "Date done" +msgstr "" + +#. module: note +#: field:note.stage,fold:0 +msgid "Folded by Default" +msgstr "" diff --git a/addons/pad/i18n/hr.po b/addons/pad/i18n/hr.po index 565f664cb74..56900dec069 100644 --- a/addons/pad/i18n/hr.po +++ b/addons/pad/i18n/hr.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openobject-addons\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2012-12-03 16:03+0000\n" -"PO-Revision-Date: 2011-12-12 12:58+0000\n" -"Last-Translator: Tomislav Bosnjakovic \n" +"PO-Revision-Date: 2012-12-10 07:12+0000\n" +"Last-Translator: Goran Kliska \n" "Language-Team: Croatian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n" -"X-Generator: Launchpad (build 16335)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:49+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: pad #. openerp-web @@ -56,7 +56,7 @@ msgstr "" #. module: pad #: field:res.company,pad_server:0 msgid "Pad Server" -msgstr "" +msgstr "Pad poslužitelj" #. module: pad #: field:res.company,pad_key:0 diff --git a/addons/plugin_thunderbird/i18n/it.po b/addons/plugin_thunderbird/i18n/it.po index 4e40d9ae9cd..5735eaf0fdc 100644 --- a/addons/plugin_thunderbird/i18n/it.po +++ b/addons/plugin_thunderbird/i18n/it.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-10 04:39+0000\n" -"X-Generator: Launchpad (build 16341)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:49+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: plugin_thunderbird #: view:plugin_thunderbird.installer:0 diff --git a/addons/point_of_sale/i18n/hr.po b/addons/point_of_sale/i18n/hr.po index da9808079f5..525940f7bcb 100644 --- a/addons/point_of_sale/i18n/hr.po +++ b/addons/point_of_sale/i18n/hr.po @@ -7,14 +7,14 @@ msgstr "" "Project-Id-Version: OpenERP Server 6.0dev\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-12-03 16:02+0000\n" -"PO-Revision-Date: 2012-10-31 09:46+0000\n" -"Last-Translator: Krešimir Jeđud \n" +"PO-Revision-Date: 2012-12-10 07:14+0000\n" +"Last-Translator: Goran Kliska \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:15+0000\n" -"X-Generator: Launchpad (build 16335)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:46+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: point_of_sale #: field:report.transaction.pos,product_nb:0 @@ -111,7 +111,7 @@ msgstr "" #: view:report.pos.order:0 #: field:report.pos.order,user_id:0 msgid "Salesperson" -msgstr "" +msgstr "Prodavač" #. module: point_of_sale #: view:report.pos.order:0 @@ -176,7 +176,7 @@ msgstr "" #: code:addons/point_of_sale/static/src/xml/pos.xml:90 #, python-format msgid "+/-" -msgstr "" +msgstr "+/-" #. module: point_of_sale #: field:pos.ean_wizard,ean13_pattern:0 @@ -225,7 +225,7 @@ msgstr "" #: code:addons/point_of_sale/point_of_sale.py:741 #, python-format msgid "Configuration Error!" -msgstr "" +msgstr "Greška u konfiguraciji!" #. module: point_of_sale #: report:account.statement:0 @@ -290,7 +290,7 @@ msgstr "Za vratiti:" #. module: point_of_sale #: model:product.template,name:point_of_sale.coca_regular_2l_product_template msgid "Coca-Cola Regular 2L" -msgstr "" +msgstr "Coca-Cola Regular 2L" #. module: point_of_sale #: model:ir.actions.act_window,name:point_of_sale.action_trans_pos_tree_month @@ -300,7 +300,7 @@ msgstr "Prodaje po mjesecima" #. module: point_of_sale #: model:pos.category,name:point_of_sale.soda_orange msgid "Orange" -msgstr "" +msgstr "Naranča" #. module: point_of_sale #: model:ir.actions.act_window,name:point_of_sale.action_report_sales_by_user_pos_today @@ -347,7 +347,7 @@ msgstr "Izveštaj prodaje" #. module: point_of_sale #: model:pos.category,name:point_of_sale.beverage msgid "Beverages" -msgstr "" +msgstr "Napitci" #. module: point_of_sale #: model:ir.actions.act_window,name:point_of_sale.action_pos_session_opening @@ -358,7 +358,7 @@ msgstr "" #. module: point_of_sale #: model:product.template,name:point_of_sale.stella_50cl_product_template msgid "Stella Artois 50cl" -msgstr "" +msgstr "Stella Artois 50cl" #. module: point_of_sale #: view:pos.details:0 @@ -368,7 +368,7 @@ msgstr "Datumi" #. module: point_of_sale #: field:pos.category,parent_id:0 msgid "Parent Category" -msgstr "" +msgstr "Nadređena kategorija" #. module: point_of_sale #. openerp-web diff --git a/addons/portal_crm/i18n/hr.po b/addons/portal_crm/i18n/hr.po index 92296baeefb..2ab2a7c5de0 100644 --- a/addons/portal_crm/i18n/hr.po +++ b/addons/portal_crm/i18n/hr.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openobject-addons\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2012-12-03 16:03+0000\n" -"PO-Revision-Date: 2012-12-09 20:56+0000\n" +"PO-Revision-Date: 2012-12-10 06:48+0000\n" "Last-Translator: Goran Kliska \n" "Language-Team: Croatian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-10 04:39+0000\n" -"X-Generator: Launchpad (build 16341)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:49+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: portal_crm #: selection:portal_crm.crm_contact_us,type:0 @@ -188,37 +188,37 @@ msgstr "" #. module: portal_crm #: field:portal_crm.crm_contact_us,payment_mode:0 msgid "Payment Mode" -msgstr "" +msgstr "Način plaćanja" #. module: portal_crm #: selection:portal_crm.crm_contact_us,state:0 msgid "New" -msgstr "" +msgstr "Nova(i)" #. module: portal_crm #: field:portal_crm.crm_contact_us,type:0 msgid "Type" -msgstr "" +msgstr "Tip" #. module: portal_crm #: field:portal_crm.crm_contact_us,email_from:0 msgid "Email" -msgstr "" +msgstr "Email" #. module: portal_crm #: field:portal_crm.crm_contact_us,channel_id:0 msgid "Channel" -msgstr "" +msgstr "Kanal" #. module: portal_crm #: view:portal_crm.crm_contact_us:0 msgid "Name" -msgstr "" +msgstr "Naziv" #. module: portal_crm #: selection:portal_crm.crm_contact_us,priority:0 msgid "Lowest" -msgstr "" +msgstr "Najniži" #. module: portal_crm #: field:portal_crm.crm_contact_us,create_date:0 diff --git a/addons/portal_event/i18n/hr.po b/addons/portal_event/i18n/hr.po new file mode 100644 index 00000000000..89bfd991887 --- /dev/null +++ b/addons/portal_event/i18n/hr.po @@ -0,0 +1,59 @@ +# Croatian translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-03 16:03+0000\n" +"PO-Revision-Date: 2012-12-10 06:58+0000\n" +"Last-Translator: Goran Kliska \n" +"Language-Team: Croatian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-12-11 04:49+0000\n" +"X-Generator: Launchpad (build 16356)\n" + +#. module: portal_event +#: view:event.event:0 +msgid "Portal Settings" +msgstr "Postave portala" + +#. module: portal_event +#: model:ir.actions.act_window,help:portal_event.action_event_view +msgid "There are no public events." +msgstr "" + +#. module: portal_event +#: selection:event.event,visibility:0 +msgid "Private" +msgstr "Privatno" + +#. module: portal_event +#: model:ir.model,name:portal_event.model_event_event +msgid "Event" +msgstr "Event" + +#. module: portal_event +#: model:ir.actions.act_window,name:portal_event.action_event_view +#: model:ir.ui.menu,name:portal_event.portal_company_events +msgid "Events" +msgstr "Eventi" + +#. module: portal_event +#: field:event.event,visibility:0 +msgid "Visibility" +msgstr "Vidljivost" + +#. module: portal_event +#: help:event.event,visibility:0 +msgid "Event's visibility in the portal's contact page" +msgstr "" + +#. module: portal_event +#: selection:event.event,visibility:0 +msgid "Public" +msgstr "Javno" diff --git a/addons/portal_hr_employees/i18n/hr.po b/addons/portal_hr_employees/i18n/hr.po new file mode 100644 index 00000000000..02385f9f001 --- /dev/null +++ b/addons/portal_hr_employees/i18n/hr.po @@ -0,0 +1,95 @@ +# Croatian translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-03 16:03+0000\n" +"PO-Revision-Date: 2012-12-10 07:00+0000\n" +"Last-Translator: Goran Kliska \n" +"Language-Team: Croatian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-12-11 04:49+0000\n" +"X-Generator: Launchpad (build 16356)\n" + +#. module: portal_hr_employees +#: view:hr.employee:0 +msgid "Coach" +msgstr "Trener" + +#. module: portal_hr_employees +#: model:ir.actions.act_window,name:portal_hr_employees.action_team +#: view:portal_crm.crm_contact_us:0 +msgid "Meet the team" +msgstr "Upoznajte tim" + +#. module: portal_hr_employees +#: view:hr.employee:0 +msgid "Group By..." +msgstr "Grupiraj po..." + +#. module: portal_hr_employees +#: field:hr.employee,public_info:0 +msgid "unknown" +msgstr "nepoznato" + +#. module: portal_hr_employees +#: view:hr.employee:0 +msgid "Company" +msgstr "Organizacija" + +#. module: portal_hr_employees +#: selection:hr.employee,visibility:0 +msgid "Public" +msgstr "Javno" + +#. module: portal_hr_employees +#: help:hr.employee,visibility:0 +msgid "Employee's visibility in the portal's contact page" +msgstr "" + +#. module: portal_hr_employees +#: selection:hr.employee,visibility:0 +msgid "Private" +msgstr "Privatno" + +#. module: portal_hr_employees +#: view:hr.employee:0 +msgid "Manager" +msgstr "Voditelj" + +#. module: portal_hr_employees +#: model:ir.model,name:portal_hr_employees.model_hr_employee +msgid "Employee" +msgstr "Radnik" + +#. module: portal_hr_employees +#: view:hr.employee:0 +msgid "Job" +msgstr "Radno mjesto" + +#. module: portal_hr_employees +#: field:hr.employee,visibility:0 +msgid "Visibility" +msgstr "Vidljivost" + +#. module: portal_hr_employees +#: model:ir.model,name:portal_hr_employees.model_portal_crm_crm_contact_us +msgid "Contact form for the portal" +msgstr "" + +#. module: portal_hr_employees +#: view:hr.employee:0 +msgid "Department" +msgstr "Odjel" + +#. module: portal_hr_employees +#: view:hr.employee:0 +#: field:portal_crm.crm_contact_us,employee_ids:0 +msgid "Employees" +msgstr "Radnici" diff --git a/addons/portal_hr_employees/i18n/pt.po b/addons/portal_hr_employees/i18n/pt.po new file mode 100644 index 00000000000..028cae086e4 --- /dev/null +++ b/addons/portal_hr_employees/i18n/pt.po @@ -0,0 +1,95 @@ +# Portuguese translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-03 16:03+0000\n" +"PO-Revision-Date: 2012-12-10 17:52+0000\n" +"Last-Translator: Andrei Talpa (multibase.pt) \n" +"Language-Team: Portuguese \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-12-11 04:49+0000\n" +"X-Generator: Launchpad (build 16356)\n" + +#. module: portal_hr_employees +#: view:hr.employee:0 +msgid "Coach" +msgstr "" + +#. module: portal_hr_employees +#: model:ir.actions.act_window,name:portal_hr_employees.action_team +#: view:portal_crm.crm_contact_us:0 +msgid "Meet the team" +msgstr "" + +#. module: portal_hr_employees +#: view:hr.employee:0 +msgid "Group By..." +msgstr "Agrupar por..." + +#. module: portal_hr_employees +#: field:hr.employee,public_info:0 +msgid "unknown" +msgstr "desconhecido" + +#. module: portal_hr_employees +#: view:hr.employee:0 +msgid "Company" +msgstr "Empresa" + +#. module: portal_hr_employees +#: selection:hr.employee,visibility:0 +msgid "Public" +msgstr "Público" + +#. module: portal_hr_employees +#: help:hr.employee,visibility:0 +msgid "Employee's visibility in the portal's contact page" +msgstr "" + +#. module: portal_hr_employees +#: selection:hr.employee,visibility:0 +msgid "Private" +msgstr "Privado" + +#. module: portal_hr_employees +#: view:hr.employee:0 +msgid "Manager" +msgstr "" + +#. module: portal_hr_employees +#: model:ir.model,name:portal_hr_employees.model_hr_employee +msgid "Employee" +msgstr "" + +#. module: portal_hr_employees +#: view:hr.employee:0 +msgid "Job" +msgstr "" + +#. module: portal_hr_employees +#: field:hr.employee,visibility:0 +msgid "Visibility" +msgstr "Visibilidade" + +#. module: portal_hr_employees +#: model:ir.model,name:portal_hr_employees.model_portal_crm_crm_contact_us +msgid "Contact form for the portal" +msgstr "" + +#. module: portal_hr_employees +#: view:hr.employee:0 +msgid "Department" +msgstr "Departamento" + +#. module: portal_hr_employees +#: view:hr.employee:0 +#: field:portal_crm.crm_contact_us,employee_ids:0 +msgid "Employees" +msgstr "" diff --git a/addons/portal_project/i18n/hr.po b/addons/portal_project/i18n/hr.po new file mode 100644 index 00000000000..936be15e2e1 --- /dev/null +++ b/addons/portal_project/i18n/hr.po @@ -0,0 +1,37 @@ +# Croatian translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-03 10:25+0000\n" +"PO-Revision-Date: 2012-12-10 06:56+0000\n" +"Last-Translator: Goran Kliska \n" +"Language-Team: Croatian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-12-11 04:49+0000\n" +"X-Generator: Launchpad (build 16356)\n" + +#. module: portal_project +#: model:ir.actions.act_window,help:portal_project.open_view_project +msgid "" +"

\n" +" Click to start a new project.\n" +"

\n" +" " +msgstr "" +"

\n" +" Klik za kreiranje novog projekta .\n" +"

\n" +" " + +#. module: portal_project +#: model:ir.actions.act_window,name:portal_project.open_view_project +#: model:ir.ui.menu,name:portal_project.portal_services_projects +msgid "Projects" +msgstr "Projekti" diff --git a/addons/portal_project_issue/i18n/hr.po b/addons/portal_project_issue/i18n/hr.po new file mode 100644 index 00000000000..84f4058f28d --- /dev/null +++ b/addons/portal_project_issue/i18n/hr.po @@ -0,0 +1,40 @@ +# Croatian translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-03 10:25+0000\n" +"PO-Revision-Date: 2012-12-10 06:54+0000\n" +"Last-Translator: Goran Kliska \n" +"Language-Team: Croatian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-12-11 04:49+0000\n" +"X-Generator: Launchpad (build 16356)\n" + +#. module: portal_project_issue +#: view:project.issue:0 +msgid "Creation:" +msgstr "Kreirano:" + +#. module: portal_project_issue +#: model:ir.actions.act_window,help:portal_project_issue.project_issue_categ_act0 +msgid "" +"

\n" +" Click to create an issue.\n" +"

\n" +" You can track your issues from this menu and the action we\n" +" will take.\n" +"

\n" +" " +msgstr "" + +#. module: portal_project_issue +#: model:ir.actions.act_window,name:portal_project_issue.project_issue_categ_act0 +msgid "Issues" +msgstr "" diff --git a/addons/portal_sale/i18n/hr.po b/addons/portal_sale/i18n/hr.po new file mode 100644 index 00000000000..8e9cf56c849 --- /dev/null +++ b/addons/portal_sale/i18n/hr.po @@ -0,0 +1,395 @@ +# Croatian translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-03 16:03+0000\n" +"PO-Revision-Date: 2012-12-10 06:53+0000\n" +"Last-Translator: Goran Kliska \n" +"Language-Team: Croatian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-12-11 04:49+0000\n" +"X-Generator: Launchpad (build 16356)\n" + +#. module: portal_sale +#: model:ir.model,name:portal_sale.model_account_config_settings +msgid "account.config.settings" +msgstr "" + +#. module: portal_sale +#: model:ir.actions.act_window,help:portal_sale.portal_action_invoices +msgid "We haven't sent you any invoice." +msgstr "" + +#. module: portal_sale +#: model:ir.actions.act_window,help:portal_sale.action_orders_portal +msgid "We haven't sent you any sale order." +msgstr "" + +#. module: portal_sale +#: model:res.groups,name:portal_sale.group_payment_options +msgid "View Online Payment Options" +msgstr "" + +#. module: portal_sale +#: field:account.config.settings,group_payment_options:0 +msgid "Show payment buttons to employees too" +msgstr "" + +#. module: portal_sale +#: model:email.template,subject:portal_sale.email_template_edi_sale +msgid "" +"${object.company_id.name} ${object.state in ('draft', 'sent') and " +"'Quotation' or 'Order'} (Ref ${object.name or 'n/a' })" +msgstr "" + +#. module: portal_sale +#: model:ir.actions.act_window,help:portal_sale.action_quotations_portal +msgid "We haven't sent you any quotation." +msgstr "" + +#. module: portal_sale +#: model:ir.ui.menu,name:portal_sale.portal_sales_orders +msgid "Sales Orders" +msgstr "Prodajni nalozi" + +#. module: portal_sale +#: model:email.template,body_html:portal_sale.email_template_edi_invoice +msgid "" +"\n" +"
\n" +"\n" +"

Hello${object.partner_id.name and ' ' or ''}${object.partner_id.name " +"or ''},

\n" +"\n" +"

A new invoice is available for you:

\n" +" \n" +"

\n" +"   REFERENCES
\n" +"   Invoice number: ${object.number}
\n" +"   Invoice total: ${object.amount_total} " +"${object.currency_id.name}
\n" +"   Invoice date: ${object.date_invoice}
\n" +" % if object.origin:\n" +"   Order reference: ${object.origin}
\n" +" % endif\n" +" % if object.user_id:\n" +"   Your contact: ${object.user_id.name}\n" +" % endif\n" +"

\n" +"\n" +" <%\n" +" action = 'portal_sale.portal_action_invoices'\n" +" object.partner_id.signup_prepare()\n" +" signup_url = " +"object.partner_id._get_signup_url_for_action(action=action,view_type='form',r" +"es_id=object.id)[object.partner_id.id]\n" +" %>\n" +" % if signup_url:\n" +"

\n" +" You can access the invoice document and pay online via our Customer " +"Portal:\n" +"

\n" +" View Invoice\n" +" % endif\n" +" \n" +" % if object.company_id.paypal_account and object.type in ('out_invoice', " +"'in_refund'):\n" +" <% \n" +" comp_name = quote(object.company_id.name)\n" +" inv_number = quote(object.number)\n" +" paypal_account = quote(object.company_id.paypal_account)\n" +" inv_amount = quote(str(object.residual))\n" +" cur_name = quote(object.currency_id.name)\n" +" paypal_url = \"https://www.paypal.com/cgi-" +"bin/webscr?cmd=_xclick&business=%s&item_name=%s%%20Invoice%%20%s&" +"\" \\\n" +" " +"\"invoice=%s&amount=%s&currency_code=%s&button_subtype=services&a" +"mp;no_note=1&bn=OpenERP_Invoice_PayNow_%s\" % \\\n" +" " +"(paypal_account,comp_name,inv_number,inv_number,inv_amount,cur_name,cur_name)" +"\n" +" %>\n" +"
\n" +"

It is also possible to directly pay with Paypal:

\n" +" \n" +" \n" +" \n" +" % endif\n" +" \n" +"
\n" +"

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

\n" +"

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

\n" +"
\n" +"
\n" +"
\n" +"

\n" +" ${object.company_id.name}

\n" +"
\n" +"
\n" +" \n" +" % if object.company_id.street:\n" +" ${object.company_id.street}
\n" +" % endif\n" +" % if object.company_id.street2:\n" +" ${object.company_id.street2}
\n" +" % endif\n" +" % if object.company_id.city or object.company_id.zip:\n" +" ${object.company_id.zip} ${object.company_id.city}
\n" +" % endif\n" +" % if object.company_id.country_id:\n" +" ${object.company_id.state_id and ('%s, ' % " +"object.company_id.state_id.name) or ''} ${object.company_id.country_id.name " +"or ''}
\n" +" % endif\n" +"
\n" +" % if object.company_id.phone:\n" +"
\n" +" Phone:  ${object.company_id.phone}\n" +"
\n" +" % endif\n" +" % if object.company_id.website:\n" +"
\n" +" Web : ${object.company_id.website}\n" +"
\n" +" %endif\n" +"

\n" +"
\n" +"
\n" +" " +msgstr "" + +#. module: portal_sale +#: model:res.groups,comment:portal_sale.group_payment_options +msgid "" +"Members of this group see the online payment options\n" +"on Sale Orders and Customer Invoices. These options are meant for customers " +"who are accessing\n" +"their documents through the portal." +msgstr "" + +#. module: portal_sale +#: model:email.template,report_name:portal_sale.email_template_edi_invoice +msgid "" +"Invoice_${(object.number or '').replace('/','_')}_${object.state == 'draft' " +"and 'draft' or ''}" +msgstr "" +"Invoice_${(object.number or '').replace('/','_')}_${object.state == 'draft' " +"and 'draft' or ''}" + +#. module: portal_sale +#: model:email.template,subject:portal_sale.email_template_edi_invoice +msgid "${object.company_id.name} Invoice (Ref ${object.number or 'n/a' })" +msgstr "${object.company_id.name} Račun (Ref ${object.number or 'n/a' })" + +#. module: portal_sale +#: model:ir.model,name:portal_sale.model_mail_mail +msgid "Outgoing Mails" +msgstr "" + +#. module: portal_sale +#: model:ir.actions.act_window,name:portal_sale.action_quotations_portal +#: model:ir.ui.menu,name:portal_sale.portal_quotations +msgid "Quotations" +msgstr "Ponude" + +#. module: portal_sale +#: model:ir.model,name:portal_sale.model_sale_order +msgid "Sales Order" +msgstr "Prodajni nalog" + +#. module: portal_sale +#: field:account.invoice,portal_payment_options:0 +#: field:sale.order,portal_payment_options:0 +msgid "Portal Payment Options" +msgstr "" + +#. module: portal_sale +#: help:account.config.settings,group_payment_options:0 +msgid "" +"Show online payment options on Sale Orders and Customer Invoices to " +"employees. If not checked, these options are only visible to portal users." +msgstr "" + +#. module: portal_sale +#: model:ir.actions.act_window,name:portal_sale.portal_action_invoices +#: model:ir.ui.menu,name:portal_sale.portal_invoices +msgid "Invoices" +msgstr "Računi" + +#. module: portal_sale +#: view:account.config.settings:0 +msgid "Configure payment acquiring methods" +msgstr "" + +#. module: portal_sale +#: model:email.template,body_html:portal_sale.email_template_edi_sale +msgid "" +"\n" +"
\n" +"\n" +"

Hello${object.partner_id.name and ' ' or ''}${object.partner_id.name " +"or ''},

\n" +" \n" +"

Here is your ${object.state in ('draft', 'sent') and 'quotation' or " +"'order confirmation'} from ${object.company_id.name}:

\n" +"\n" +"

\n" +"   REFERENCES
\n" +"   Order number: ${object.name}
\n" +"   Order total: ${object.amount_total} " +"${object.pricelist_id.currency_id.name}
\n" +"   Order date: ${object.date_order}
\n" +" % if object.origin:\n" +"   Order reference: ${object.origin}
\n" +" % endif\n" +" % if object.client_order_ref:\n" +"   Your reference: ${object.client_order_ref}
\n" +" % endif\n" +" % if object.user_id:\n" +"   Your contact: ${object.user_id.name}\n" +" % endif\n" +"

\n" +"\n" +" <%\n" +" action = 'portal_sale.action_quotations_portal' if object.state in " +"('draft', 'sent') else 'portal_sale.action_orders_portal'\n" +" object.partner_id.signup_prepare()\n" +" signup_url = " +"object.partner_id._get_signup_url_for_action(action=action,view_type='form',r" +"es_id=object.id)[object.partner_id.id]\n" +" %>\n" +" % if signup_url:\n" +"

\n" +" You can access this document and pay online via our Customer Portal:\n" +"

\n" +" View ${object.state in ('draft', 'sent') " +"and 'Quotation' or 'Order'}\n" +" % endif\n" +"\n" +" % if object.order_policy in ('prepaid','manual') and " +"object.company_id.paypal_account and object.state != 'draft':\n" +" <%\n" +" comp_name = quote(object.company_id.name)\n" +" order_name = quote(object.name)\n" +" paypal_account = quote(object.company_id.paypal_account)\n" +" order_amount = quote(str(object.residual))\n" +" cur_name = quote(object.pricelist_id.currency_id.name)\n" +" paypal_url = \"https://www.paypal.com/cgi-" +"bin/webscr?cmd=_xclick&business=%s&item_name=%s%%20Order%%20%s\" \\\n" +" " +"\"&invoice=%s&amount=%s&currency_code=%s&button_subtype=servi" +"ces&no_note=1\" \\\n" +" \"&bn=OpenERP_Order_PayNow_%s\" % \\\n" +" " +"(paypal_account,comp_name,order_name,order_name,order_amount,cur_name,cur_nam" +"e)\n" +" %>\n" +"
\n" +"

It is also possible to directly pay with Paypal:

\n" +" \n" +" \n" +" \n" +" % endif\n" +"\n" +"
\n" +"

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

\n" +"

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

\n" +"
\n" +"
\n" +"
\n" +"

\n" +" ${object.company_id.name}

\n" +"
\n" +"
\n" +" \n" +" % if object.company_id.street:\n" +" ${object.company_id.street}
\n" +" % endif\n" +" % if object.company_id.street2:\n" +" ${object.company_id.street2}
\n" +" % endif\n" +" % if object.company_id.city or object.company_id.zip:\n" +" ${object.company_id.zip} ${object.company_id.city}
\n" +" % endif\n" +" % if object.company_id.country_id:\n" +" ${object.company_id.state_id and ('%s, ' % " +"object.company_id.state_id.name) or ''} ${object.company_id.country_id.name " +"or ''}
\n" +" % endif\n" +"
\n" +" % if object.company_id.phone:\n" +"
\n" +" Phone:  ${object.company_id.phone}\n" +"
\n" +" % endif\n" +" % if object.company_id.website:\n" +"
\n" +" Web : ${object.company_id.website}\n" +"
\n" +" %endif\n" +"

\n" +"
\n" +"
\n" +" " +msgstr "" + +#. module: portal_sale +#: model:ir.model,name:portal_sale.model_account_invoice +msgid "Invoice" +msgstr "" + +#. module: portal_sale +#: model:ir.actions.act_window,name:portal_sale.action_orders_portal +msgid "Sale Orders" +msgstr "" + +#. module: portal_sale +#: model:email.template,report_name:portal_sale.email_template_edi_sale +msgid "" +"${(object.name or '').replace('/','_')}_${object.state == 'draft' and " +"'draft' or ''}" +msgstr "" diff --git a/addons/process/i18n/hr.po b/addons/process/i18n/hr.po index 83119f58ed1..cd4ae4a432c 100644 --- a/addons/process/i18n/hr.po +++ b/addons/process/i18n/hr.po @@ -7,14 +7,14 @@ msgstr "" "Project-Id-Version: OpenERP Server 6.0dev\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-11-24 02:54+0000\n" -"PO-Revision-Date: 2010-08-03 08:48+0000\n" -"Last-Translator: Mantavya Gajjar (Open ERP) \n" +"PO-Revision-Date: 2012-12-10 08:27+0000\n" +"Last-Translator: Goran Kliska \n" "Language-Team: Vinteh\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-11-25 05:51+0000\n" -"X-Generator: Launchpad (build 16293)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:46+0000\n" +"X-Generator: Launchpad (build 16356)\n" "Language: hr\n" #. module: process @@ -39,7 +39,7 @@ msgstr "Povezani izbornik" #. module: process #: selection:process.node,kind:0 msgid "Status" -msgstr "" +msgstr "Status" #. module: process #: field:process.transition,action_ids:0 @@ -50,7 +50,7 @@ msgstr "Tipke" #: view:process.node:0 #: view:process.process:0 msgid "Group By..." -msgstr "" +msgstr "Grupiraj po..." #. module: process #: view:process.node:0 @@ -137,7 +137,7 @@ msgstr "Prijelazi Hodograma" #: code:addons/process/static/src/xml/process.xml:39 #, python-format msgid "Last modified by:" -msgstr "" +msgstr "Promijenio:" #. module: process #: field:process.transition.action,action:0 @@ -149,7 +149,7 @@ msgstr "ID Radnje" #: code:addons/process/static/src/xml/process.xml:7 #, python-format msgid "Process View" -msgstr "" +msgstr "Proces" #. module: process #: model:ir.model,name:process.model_process_transition @@ -171,7 +171,7 @@ msgstr "Prazan" #: model:ir.actions.act_window,name:process.action_process_form #: model:ir.ui.menu,name:process.menu_process_form msgid "Processes" -msgstr "" +msgstr "Procesi" #. module: process #: field:process.transition.action,transition_id:0 @@ -197,7 +197,7 @@ msgstr "Početni prijelazi" #: code:addons/process/static/src/xml/process.xml:54 #, python-format msgid "Related:" -msgstr "" +msgstr "Povezani:" #. module: process #: view:process.node:0 @@ -213,7 +213,7 @@ msgstr "Bilješke" #: code:addons/process/static/src/xml/process.xml:88 #, python-format msgid "Edit Process" -msgstr "" +msgstr "Uredi proces" #. module: process #. openerp-web @@ -252,7 +252,7 @@ msgstr "Radnja" #: code:addons/process/static/src/xml/process.xml:67 #, python-format msgid "Select Process" -msgstr "" +msgstr "Odaberi proces" #. module: process #: field:process.condition,model_states:0 @@ -300,7 +300,7 @@ msgstr "" #: view:process.node:0 #: view:process.process:0 msgid "Other Conditions" -msgstr "" +msgstr "Ostali uvjeti" #. module: process #: model:ir.ui.menu,name:process.menu_process @@ -339,7 +339,7 @@ msgstr "Čvorište neke vrste" #: code:addons/process/static/src/xml/process.xml:42 #, python-format msgid "Subflows:" -msgstr "" +msgstr "Podtokovi:" #. module: process #: view:process.node:0 diff --git a/addons/product/i18n/pl.po b/addons/product/i18n/pl.po index 3c74a0649d3..3524b92fe09 100644 --- a/addons/product/i18n/pl.po +++ b/addons/product/i18n/pl.po @@ -7,14 +7,14 @@ msgstr "" "Project-Id-Version: OpenERP Server 6.0dev_rc3\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-12-03 16:03+0000\n" -"PO-Revision-Date: 2012-05-10 18:01+0000\n" +"PO-Revision-Date: 2012-12-10 19:50+0000\n" "Last-Translator: Grzegorz Grzelak (OpenGLOBE.pl) \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:32+0000\n" -"X-Generator: Launchpad (build 16335)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:48+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: product #: field:product.packaging,rows:0 @@ -24,7 +24,7 @@ msgstr "Liczba warstw" #. module: product #: help:product.pricelist.item,base:0 msgid "Base price for computation." -msgstr "" +msgstr "Cena podstawowa do obliczeń" #. module: product #: help:product.product,seller_qty:0 @@ -39,7 +39,7 @@ msgstr "Dzień" #. module: product #: model:product.template,name:product.product_product_34_product_template msgid "Webcam" -msgstr "" +msgstr "Kamera internetowa" #. module: product #: field:product.product,incoming_qty:0 @@ -49,12 +49,12 @@ msgstr "Wchodzące" #. module: product #: view:product.product:0 msgid "Product Name" -msgstr "" +msgstr "Nazwa produktu" #. module: product #: view:product.template:0 msgid "Second Unit of Measure" -msgstr "" +msgstr "Druga jednostka miary" #. module: product #: help:res.partner,property_product_pricelist:0 @@ -95,6 +95,7 @@ msgstr "Nazwa reguły" msgid "" "Base price to compute the customer price. Sometimes called the catalog price." msgstr "" +"Cena podstawowa do obliczenia ceny dla klienta. Czasem zwana ceną katalogową." #. module: product #: model:product.template,name:product.product_product_3_product_template @@ -113,7 +114,7 @@ msgstr "" #: code:addons/product/product.py:206 #, python-format msgid "Warning!" -msgstr "" +msgstr "Ostrzeżenie !" #. module: product #: field:product.product,image_small:0 @@ -127,11 +128,13 @@ msgid "" "Conversion from Product UoM %s to Default UoM %s is not possible as they " "both belong to different Category!." msgstr "" +"Konwersja z jednostki %s do jednostki domyślnej %s jest niemożliwa ponieważ " +"należą one do innych kategorii!." #. module: product #: model:product.uom,name:product.product_uom_dozen msgid "Dozen" -msgstr "" +msgstr "Tuzin" #. module: product #: selection:product.template,cost_method:0 @@ -146,7 +149,7 @@ msgstr "Opisowa nazwa reguły dla tej pozycji cennika." #. module: product #: field:product.template,uos_coeff:0 msgid "Unit of Measure -> UOS Coeff" -msgstr "" +msgstr "JM -> współczynnik JS" #. module: product #: field:product.price_list,price_list:0 @@ -271,7 +274,7 @@ msgstr "Opakowania" #: help:product.product,active:0 msgid "" "If unchecked, it will allow you to hide the product without removing it." -msgstr "" +msgstr "Jeśli nie zaznaczone, to można ukryć produkt bez jego usuwania." #. module: product #: view:product.product:0 @@ -283,7 +286,7 @@ msgstr "Kategoria" #. module: product #: model:product.uom,name:product.product_uom_litre msgid "Litre" -msgstr "" +msgstr "Litr" #. module: product #: model:product.template,name:product.product_product_25_product_template @@ -387,7 +390,7 @@ msgstr "Długość opakowania" #. module: product #: field:product.product,message_summary:0 msgid "Summary" -msgstr "" +msgstr "Podsumowanie" #. module: product #: help:product.template,weight_net:0 @@ -439,12 +442,12 @@ msgstr "Wysokość" #. module: product #: view:product.product:0 msgid "Procurements" -msgstr "" +msgstr "Zapotrzebowania" #. module: product #: model:res.groups,name:product.group_mrp_properties msgid "Manage Properties of Product" -msgstr "" +msgstr "Właściwości produktu" #. module: product #: help:product.uom,factor:0 @@ -453,6 +456,9 @@ msgid "" "Measure for this category:\n" "1 * (reference unit) = ratio * (this unit)" msgstr "" +"Ile razy większa lub mniejsza jest ta jednostka w porównaniu do jednostki " +"referencyjnej z tej kategorii:\n" +"1 * (jednostka referencyjna) = współczynnik * (ta jednostka)" #. module: product #: model:ir.model,name:product.model_pricelist_partnerinfo @@ -543,7 +549,7 @@ msgstr "karton 20x20x40" #. module: product #: field:product.template,warranty:0 msgid "Warranty" -msgstr "" +msgstr "Gwarancja" #. module: product #: view:product.pricelist.item:0 @@ -560,7 +566,7 @@ msgstr "" #. module: product #: model:res.groups,name:product.group_purchase_pricelist msgid "Purchase Pricelists" -msgstr "" +msgstr "Cennik zakupu" #. module: product #: model:product.template,name:product.product_product_5_product_template @@ -585,7 +591,7 @@ msgstr "Szerokość opakowania" #: code:addons/product/product.py:359 #, python-format msgid "Unit of Measure categories Mismatch!" -msgstr "" +msgstr "Niezgodne kategorie jednostek miary !" #. module: product #: model:product.template,name:product.product_product_36_product_template @@ -610,7 +616,7 @@ msgstr "" #. module: product #: help:product.pricelist.item,price_max_margin:0 msgid "Specify the maximum amount of margin over the base price." -msgstr "" +msgstr "Określ maksymalną wartość marży ponad ceną bazową." #. module: product #: constraint:product.pricelist.item:0 @@ -701,7 +707,7 @@ msgstr "" #. module: product #: field:product.product,qty_available:0 msgid "Quantity On Hand" -msgstr "" +msgstr "Ilość Na Stanie" #. module: product #: field:product.price.type,name:0 @@ -711,12 +717,12 @@ msgstr "Nazwa ceny" #. module: product #: help:product.product,message_unread:0 msgid "If checked new messages require your attention." -msgstr "" +msgstr "Jeśli zaznaczone, to wiadomość wymaga twojej uwagi" #. module: product #: model:product.category,name:product.product_category_1 msgid "Saleable" -msgstr "" +msgstr "Do sprzedaży" #. module: product #: model:ir.actions.act_window,name:product.action_product_price_list @@ -730,12 +736,12 @@ msgstr "Cennik" #. module: product #: field:product.product,virtual_available:0 msgid "Forecasted Quantity" -msgstr "" +msgstr "Przewidywana ilość" #. module: product #: view:product.product:0 msgid "Purchase" -msgstr "" +msgstr "Zakupy" #. module: product #: model:product.template,name:product.product_product_33_product_template @@ -750,7 +756,7 @@ msgstr "Dostawcy" #. module: product #: model:res.groups,name:product.group_sale_pricelist msgid "Sales Pricelists" -msgstr "" +msgstr "Cennik sprzedaży" #. module: product #: view:product.pricelist.item:0 @@ -778,6 +784,8 @@ msgid "" "Cost price of the product used for standard stock valuation in accounting " "and used as a base price on purchase orders." msgstr "" +"Cena - koszt jest stosowana do standardowej wyceny zapasów oraz jako cena " +"bazowa przy zakupach." #. module: product #: field:product.category,child_id:0 @@ -861,7 +869,7 @@ msgstr "" #. module: product #: view:product.template:0 msgid "days" -msgstr "" +msgstr "dni" #. module: product #: model:process.node,name:product.process_node_supplier0 @@ -888,6 +896,9 @@ msgid "" "Coefficient to convert default Unit of Measure to Unit of Sale\n" " uos = uom * coeff" msgstr "" +"Współczynnik do konwersji Jednostki Miary (JM - uom) na Jednostkę Sprzedaży " +"(JS - uos)\n" +" JS = JM * współcz." #. module: product #: model:ir.actions.act_window,name:product.product_category_action_form @@ -948,6 +959,9 @@ msgid "" "Measure in this category:\n" "1 * (this unit) = ratio * (reference unit)" msgstr "" +"Ile razy ta Jednostka miary jest większa niż jednostka referencyjna z tej " +"kategorii:\n" +"1 * (ta jednostka) = współczynnik * (jednostka referencyjna)" #. module: product #: view:product.template:0 @@ -965,12 +979,12 @@ msgstr "Nie możesz mieć 2 cenników, które na siebie zachodzą !" msgid "" "Specify the minimum quantity that needs to be bought/sold for the rule to " "apply." -msgstr "" +msgstr "Podaj minimalną ilość dla tej reguły." #. module: product #: help:product.pricelist.version,date_start:0 msgid "First valid date for the version." -msgstr "" +msgstr "Pierwsza dozwolona data dla tej wersji." #. module: product #: help:product.supplierinfo,delay:0 @@ -1057,6 +1071,8 @@ msgid "" "Product Unit of Measure if not empty, in the default unit of measure of the " "product otherwise." msgstr "" +"Minimalna ilość do zakupu od tego dostawcy wyrażona w jednostce dostawcy " +"jeśli zdefiniowana. Jeśli nie zdefiniowana, to jednostce domyślnej." #. module: product #: view:product.product:0 @@ -1098,7 +1114,7 @@ msgstr "Dopłata do ceny" #: field:product.product,code:0 #: field:product.product,default_code:0 msgid "Internal Reference" -msgstr "" +msgstr "Odnośnik wewnątrzny" #. module: product #: model:product.template,name:product.product_product_8_product_template @@ -1130,12 +1146,12 @@ msgstr "Nazwa" #. module: product #: model:product.category,name:product.product_category_4 msgid "Computers" -msgstr "" +msgstr "Komputery" #. module: product #: help:product.product,message_ids:0 msgid "Messages and communication history" -msgstr "" +msgstr "Wiadomości i historia komunikacji" #. module: product #: model:product.uom,name:product.product_uom_kgm @@ -1155,7 +1171,7 @@ msgstr "" #. module: product #: field:product.template,standard_price:0 msgid "Cost" -msgstr "" +msgstr "Koszt" #. module: product #: help:product.category,sequence:0 @@ -1200,13 +1216,13 @@ msgstr "Jednostka sprzedaży (JS)" #. module: product #: field:product.product,message_unread:0 msgid "Unread Messages" -msgstr "" +msgstr "Nieprzeczytane wiadomości" #. module: product #: model:ir.actions.act_window,name:product.product_uom_categ_form_action #: model:ir.ui.menu,name:product.menu_product_uom_categ_form_action msgid "Unit of Measure Categories" -msgstr "" +msgstr "Kategorie jednostek miary" #. module: product #: help:product.product,seller_id:0 @@ -1223,7 +1239,7 @@ msgstr "Usługi" #. module: product #: help:product.product,ean13:0 msgid "International Article Number used for product identification." -msgstr "" +msgstr "Międzynarodowy numer artykułu stosowany do identyfikacji" #. module: product #: model:ir.actions.act_window,help:product.product_category_action @@ -1247,6 +1263,9 @@ msgid "" "you may deactivate this product from the 'Procurements' tab and create a new " "one." msgstr "" +"Nowa jednostka miary '%s' musi należeć do tej samej kategorii '%s' jak " +"poprzednia jednostka '%s'. Jeśli chcesz zmienić jednostkę, to możesz " +"deaktywować produkt w zakładce 'Zapotrzebowania' i utworzyć nowy produkt." #. module: product #: model:ir.actions.act_window,name:product.product_normal_action @@ -1273,7 +1292,7 @@ msgstr "Liczba warstw na palecie lub w kontenerze" #. module: product #: help:product.pricelist.item,price_min_margin:0 msgid "Specify the minimum amount of margin over the base price." -msgstr "" +msgstr "Określ minimalną marżę dla ceny bazowej." #. module: product #: field:product.template,weight_net:0 @@ -1304,17 +1323,19 @@ msgid "" "Specify a product category if this rule only applies to products belonging " "to this category or its children categories. Keep empty otherwise." msgstr "" +"Podaj kategorię produktu, jeśli ta reguła ma dotyczyć produktów z całej " +"kategorii i kategorii podrzędnych. W przeciwnym przypadku pozostaw puste." #. module: product #: view:product.product:0 msgid "Inventory" -msgstr "" +msgstr "Zapasy" #. module: product #: code:addons/product/product.py:736 #, python-format msgid "%s (copy)" -msgstr "" +msgstr "%s (kopia)" #. module: product #: model:product.template,name:product.product_product_2_product_template @@ -1409,7 +1430,7 @@ msgstr "Wagi" #. module: product #: view:product.product:0 msgid "Description for Quotations" -msgstr "" +msgstr "Opis do oferty" #. module: product #: help:pricelist.partnerinfo,price:0 @@ -1417,6 +1438,9 @@ msgid "" "This price will be considered as a price for the supplier Unit of Measure if " "any or the default Unit of Measure of the product otherwise" msgstr "" +"Ta cena będzie podana dla jednostki miary dostawcy, jeśli ta jest podana. " +"Jeśli jednostki dostawcy nie ma, to będzie brana pod uwagę jednostka " +"domyślna produktu." #. module: product #: field:product.template,uom_po_id:0 @@ -1444,7 +1468,7 @@ msgstr "Jednostka" #: code:addons/product/product.py:588 #, python-format msgid "Product has been created." -msgstr "" +msgstr "Produkt został utworzony." #. module: product #: field:product.pricelist.version,date_start:0 @@ -1493,7 +1517,7 @@ msgstr "" #. module: product #: view:product.pricelist:0 msgid "Products Price" -msgstr "" +msgstr "Ceny produktół" #. module: product #: model:product.template,description:product.product_product_26_product_template @@ -1512,7 +1536,7 @@ msgstr "Precyzja zaokrąglania" #. module: product #: view:product.product:0 msgid "Consumable products" -msgstr "" +msgstr "Produkty pomocnicze" #. module: product #: model:product.template,name:product.product_product_21_product_template @@ -1528,7 +1552,7 @@ msgstr "" #. module: product #: help:product.pricelist.version,date_end:0 msgid "Last valid date for the version." -msgstr "" +msgstr "Ostatnia data ważności wersji." #. module: product #: model:product.template,name:product.product_product_19_product_template @@ -1577,7 +1601,7 @@ msgstr "Kod EAN opakowania" #. module: product #: help:product.supplierinfo,product_uom:0 msgid "This comes from the product form." -msgstr "" +msgstr "To pochodzi z formularza produktu" #. module: product #: field:product.packaging,weight_ul:0 @@ -1603,7 +1627,7 @@ msgstr "" #: model:ir.actions.act_window,name:product.product_price_type_action #: model:ir.ui.menu,name:product.menu_product_price_type msgid "Price Types" -msgstr "" +msgstr "Typy cen" #. module: product #: help:product.template,uom_id:0 @@ -1614,7 +1638,7 @@ msgstr "" #. module: product #: view:product.product:0 msgid "Sales" -msgstr "" +msgstr "Sprzedaż" #. module: product #: model:ir.actions.act_window,help:product.product_uom_categ_form_action @@ -1637,11 +1661,14 @@ msgid "" "The minimal quantity to trigger this rule, expressed in the supplier Unit of " "Measure if any or in the default Unit of Measure of the product otherrwise." msgstr "" +"Minimalna ilość dla tej reguły wyrażona w jednostce miary dostawcy, jeśli ta " +"jednostka jest. Jeśli jej nie ma, to ilość jest wyrażona w jednostce " +"domyślnej dla porduktu." #. module: product #: selection:product.uom,uom_type:0 msgid "Smaller than the reference Unit of Measure" -msgstr "" +msgstr "Mniejsza niż jednostka referencyjna" #. module: product #: model:product.pricelist,name:product.list0 @@ -1658,6 +1685,8 @@ msgstr "Kod produktu u dostawcy" msgid "" "If unchecked, it will allow you to hide the pricelist without removing it." msgstr "" +"Jeśli nie ma zaznaczenia, to cennik może być ukryty bez konieczności " +"usuwania." #. module: product #: selection:product.ul,type:0 @@ -1684,7 +1713,7 @@ msgstr "Produkt" #. module: product #: view:product.product:0 msgid "Price:" -msgstr "" +msgstr "Cena:" #. module: product #: field:product.template,weight:0 @@ -1725,12 +1754,12 @@ msgstr "Przypisuje priorytet do dostawcy produktu w listy.." #. module: product #: constraint:product.pricelist.item:0 msgid "Error! The minimum margin should be lower than the maximum margin." -msgstr "" +msgstr "Błąd! Marża minimalna powinna być mniejsza niż marża maksymalna." #. module: product #: model:res.groups,name:product.group_uos msgid "Manage Secondary Unit of Measure" -msgstr "" +msgstr "Określ drugą jednostkę" #. module: product #: help:product.uom,rounding:0 @@ -1762,7 +1791,7 @@ msgstr "" #. module: product #: selection:product.uom,uom_type:0 msgid "Bigger than the reference Unit of Measure" -msgstr "" +msgstr "Większa od jedostki referencyjnej" #. module: product #: model:product.template,name:product.product_product_consultant_product_template @@ -1773,7 +1802,7 @@ msgstr "Usługa" #. module: product #: view:product.template:0 msgid "Internal Description" -msgstr "" +msgstr "Opis wewnętrzny" #. module: product #: model:product.template,name:product.product_product_48_product_template @@ -1786,12 +1815,15 @@ msgid "" "Sepcify a unit of measure here if invoicing is made in another unit of " "measure than inventory. Keep empty to use the default unit of measure." msgstr "" +"Podaj jednostkę tutaj, jeśli chcesz wystawiać faktury w innej jednostce niż " +"domyślna. Jeśli chcesz w fakturach stosować jednostkę domyślną, to zostaw to " +"pole puste." #. module: product #: code:addons/product/product.py:206 #, python-format msgid "Cannot change the category of existing Unit of Measure '%s'." -msgstr "" +msgstr "Nie mozna zmieniać kategorii istniejącej jednostki miary '%s'." #. module: product #: help:product.packaging,height:0 @@ -1847,13 +1879,13 @@ msgstr "Produkt jest skonfigurowany jako przykład przepływu wydaj/pobierz" #. module: product #: field:product.product,message_ids:0 msgid "Messages" -msgstr "" +msgstr "Wiadomości" #. module: product #: code:addons/product/product.py:174 #, python-format msgid "Error!" -msgstr "" +msgstr "Błąd!" #. module: product #: field:product.packaging,length:0 @@ -1868,7 +1900,7 @@ msgstr "Długość" #. module: product #: model:product.category,name:product.product_category_8 msgid "Components" -msgstr "" +msgstr "Komponenty" #. module: product #: model:ir.model,name:product.model_product_pricelist_type @@ -1884,12 +1916,13 @@ msgstr "" #. module: product #: field:product.product,color:0 msgid "Color Index" -msgstr "" +msgstr "Indeks kolorów" #. module: product #: help:product.template,sale_ok:0 msgid "Specify if the product can be selected in a sales order line." msgstr "" +"Określ, czy produkt ma być wybierany w pozycjach zamówienia sprzedaży." #. module: product #: view:product.product:0 @@ -1978,7 +2011,7 @@ msgstr "" #. module: product #: view:product.product:0 msgid "Sale Conditions" -msgstr "" +msgstr "Warunki sprzedaży" #. module: product #: view:product.packaging:0 @@ -1994,7 +2027,7 @@ msgstr "Nazwa cennika" #. module: product #: view:product.product:0 msgid "Description for Suppliers" -msgstr "" +msgstr "Opis dla dostawców" #. module: product #: field:product.supplierinfo,delay:0 @@ -2004,7 +2037,7 @@ msgstr "Czas dostawy" #. module: product #: view:product.product:0 msgid "months" -msgstr "" +msgstr "miesięcy" #. module: product #: help:product.uom,active:0 @@ -2080,7 +2113,7 @@ msgstr "" #. module: product #: view:product.product:0 msgid "Context..." -msgstr "" +msgstr "Kontekst..." #. module: product #: field:product.packaging,ul:0 @@ -2116,7 +2149,7 @@ msgstr "" #. module: product #: field:product.product,image:0 msgid "Image" -msgstr "" +msgstr "Obrazek" #. module: product #: view:product.uom.categ:0 @@ -2140,12 +2173,12 @@ msgstr "Opisy" #. module: product #: model:res.groups,name:product.group_stock_packaging msgid "Manage Product Packaging" -msgstr "" +msgstr "Opakowania produktu" #. module: product #: model:product.category,name:product.product_category_2 msgid "Internal" -msgstr "" +msgstr "Wewnętrzne" #. module: product #: model:product.template,name:product.product_product_45_product_template @@ -2170,6 +2203,8 @@ msgid "" "At least one pricelist has no active version !\n" "Please create or activate one." msgstr "" +"Co najmniej jeden cennik nie ma aktywnej wersji !\n" +"Utwórz ją albo aktywuj." #. module: product #: field:product.pricelist.item,price_max_margin:0 @@ -2222,7 +2257,7 @@ msgstr "Zmienna" #: field:product.product,message_comment_ids:0 #: help:product.product,message_comment_ids:0 msgid "Comments and emails" -msgstr "" +msgstr "Komentarze i emaile" #. module: product #: field:product.template,rental:0 @@ -2242,7 +2277,7 @@ msgstr "Min. marża ceny" #. module: product #: model:res.groups,name:product.group_uom msgid "Manage Multiple Units of Measure" -msgstr "" +msgstr "Określ wiele jednostek miary" #. module: product #: help:product.packaging,weight:0 @@ -2252,7 +2287,7 @@ msgstr "Waga całej palety, opakowania lub pudełka." #. module: product #: view:product.uom:0 msgid "e.g: 1 * (this unit) = ratio * (reference unit)" -msgstr "" +msgstr "tzn: 1 * (ta jednostka) = wspólczynnik * (jednostka referencyjna)" #. module: product #: model:product.template,description:product.product_product_25_product_template @@ -2361,12 +2396,12 @@ msgstr "" #. module: product #: selection:product.uom,uom_type:0 msgid "Reference Unit of Measure for this category" -msgstr "" +msgstr "Referencyjna jednostka miary dla tej kategorii" #. module: product #: field:product.supplierinfo,product_uom:0 msgid "Supplier Unit of Measure" -msgstr "" +msgstr "Jednostka miary dostawcy" #. module: product #: view:product.product:0 @@ -2497,12 +2532,12 @@ msgstr "" #. module: product #: view:product.uom:0 msgid "e.g: 1 * (reference unit) = ratio * (this unit)" -msgstr "" +msgstr "tzn: 1 * (jednostka referencyjna) = współczynnik * (ta jednostka)" #. module: product #: help:product.supplierinfo,qty:0 msgid "This is a quantity which is converted into Default Unit of Measure." -msgstr "" +msgstr "To jest ilość skonwertowana do domyślnej jednostki miary." #. module: product #: help:product.template,volume:0 diff --git a/addons/product_expiry/i18n/hr.po b/addons/product_expiry/i18n/hr.po index dee4b7089af..6c765d97761 100644 --- a/addons/product_expiry/i18n/hr.po +++ b/addons/product_expiry/i18n/hr.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openobject-addons\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2012-12-03 16:04+0000\n" -"PO-Revision-Date: 2010-09-29 10:30+0000\n" -"Last-Translator: OpenERP Administrators \n" +"PO-Revision-Date: 2012-12-10 07:04+0000\n" +"Last-Translator: Goran Kliska \n" "Language-Team: Croatian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:45+0000\n" -"X-Generator: Launchpad (build 16335)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:49+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: product_expiry #: model:product.template,name:product_expiry.product_product_from_product_template @@ -54,7 +54,7 @@ msgstr "" #. module: product_expiry #: model:ir.model,name:product_expiry.model_product_product msgid "Product" -msgstr "" +msgstr "Proizvod" #. module: product_expiry #: help:product.product,use_time:0 @@ -78,7 +78,7 @@ msgstr "" #. module: product_expiry #: model:ir.model,name:product_expiry.model_stock_production_lot msgid "Serial Number" -msgstr "" +msgstr "Serijski broj" #. module: product_expiry #: help:product.product,alert_time:0 diff --git a/addons/product_margin/i18n/hr.po b/addons/product_margin/i18n/hr.po index 1f991340b53..92632810be1 100644 --- a/addons/product_margin/i18n/hr.po +++ b/addons/product_margin/i18n/hr.po @@ -7,14 +7,14 @@ msgstr "" "Project-Id-Version: OpenERP Server 5.0.4\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-12-03 16:04+0000\n" -"PO-Revision-Date: 2010-09-29 08:39+0000\n" -"Last-Translator: OpenERP Administrators \n" +"PO-Revision-Date: 2012-12-10 07:05+0000\n" +"Last-Translator: Goran Kliska \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:42+0000\n" -"X-Generator: Launchpad (build 16335)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:48+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: product_margin #: view:product.product:0 @@ -73,7 +73,7 @@ msgstr "Prosj. jedinična cijena" #. module: product_margin #: model:ir.model,name:product_margin.model_product_product msgid "Product" -msgstr "" +msgstr "Proizvod" #. module: product_margin #: view:product.product:0 @@ -235,7 +235,7 @@ msgstr "Marže proizvoda" #. module: product_margin #: view:product.margin:0 msgid "General Information" -msgstr "" +msgstr "Opći podaci" #. module: product_margin #: field:product.product,purchase_gap:0 @@ -270,7 +270,7 @@ msgstr "Suma količina na fakturama kupca" #. module: product_margin #: view:product.margin:0 msgid "or" -msgstr "" +msgstr "ili" #. module: product_margin #: model:ir.model,name:product_margin.model_product_margin diff --git a/addons/project_issue/i18n/pt.po b/addons/project_issue/i18n/pt.po index 0b2a65d4fcf..5e937f6527d 100644 --- a/addons/project_issue/i18n/pt.po +++ b/addons/project_issue/i18n/pt.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openobject-addons\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2012-12-03 16:02+0000\n" -"PO-Revision-Date: 2010-12-11 14:47+0000\n" -"Last-Translator: OpenERP Administrators \n" +"PO-Revision-Date: 2012-12-10 17:58+0000\n" +"Last-Translator: Andrei Talpa (multibase.pt) \n" "Language-Team: Portuguese \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:49+0000\n" -"X-Generator: Launchpad (build 16335)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:49+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: project_issue #: model:project.category,name:project_issue.project_issue_category_03 @@ -257,7 +257,7 @@ msgstr "" #. module: project_issue #: view:project.issue:0 msgid "Edit..." -msgstr "" +msgstr "Editar..." #. module: project_issue #: view:project.issue:0 @@ -334,7 +334,7 @@ msgstr "Menor" #: code:addons/project_issue/project_issue.py:358 #, python-format msgid "%s (copy)" -msgstr "" +msgstr "%s (cópia)" #. module: project_issue #: view:project.issue:0 @@ -485,7 +485,7 @@ msgstr "Atualizar Data" #. module: project_issue #: view:project.issue:0 msgid "Project:" -msgstr "" +msgstr "Projeto:" #. module: project_issue #: view:project.issue:0 @@ -590,12 +590,12 @@ msgstr "Normal" #. module: project_issue #: field:project.project,issue_count:0 msgid "unknown" -msgstr "" +msgstr "desconhecido" #. module: project_issue #: view:project.issue:0 msgid "Category:" -msgstr "" +msgstr "Categoria:" #. module: project_issue #: selection:project.issue.report,month:0 @@ -705,7 +705,7 @@ msgstr "Atribuído a" #. module: project_issue #: view:project.config.settings:0 msgid "Configure" -msgstr "" +msgstr "Configurar" #. module: project_issue #: view:project.issue:0 diff --git a/addons/purchase/i18n/sl.po b/addons/purchase/i18n/sl.po index bfc429ca74d..aa446831c3b 100644 --- a/addons/purchase/i18n/sl.po +++ b/addons/purchase/i18n/sl.po @@ -7,14 +7,14 @@ msgstr "" "Project-Id-Version: OpenERP Server 6.0dev\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-12-03 16:02+0000\n" -"PO-Revision-Date: 2012-12-08 17:09+0000\n" +"PO-Revision-Date: 2012-12-10 14:20+0000\n" "Last-Translator: Vida Potočnik \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-09 04:38+0000\n" -"X-Generator: Launchpad (build 16341)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:46+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: purchase #: model:res.groups,name:purchase.group_analytic_accounting @@ -117,7 +117,7 @@ msgstr "" #: code:addons/purchase/purchase.py:573 #, python-format msgid "You must first cancel all receptions related to this purchase order." -msgstr "" +msgstr "Najprej morate preklicati vse prevzeme, vezane na ta nabavni nalog." #. module: purchase #: model:ir.model,name:purchase.model_res_partner @@ -500,7 +500,7 @@ msgstr "" #. module: purchase #: model:ir.ui.menu,name:purchase.menu_action_picking_tree_in_move msgid "Incoming Products" -msgstr "" +msgstr "Naročeni produkti" #. module: purchase #: view:purchase.config.settings:0 @@ -547,7 +547,7 @@ msgstr "" #. module: purchase #: selection:purchase.order,state:0 msgid "RFQ Sent" -msgstr "" +msgstr "Zahteva za ponudbo poslana" #. module: purchase #: view:purchase.order:0 @@ -595,7 +595,7 @@ msgstr "Prejeto" #. module: purchase #: view:purchase.order:0 msgid "Purchase order which are in draft state" -msgstr "" +msgstr "Osnutki nabavnih nalogov" #. module: purchase #: view:product.product:0 @@ -661,7 +661,7 @@ msgstr "Pokaže da je bilo izbiranje končano" #: code:addons/purchase/purchase.py:572 #, python-format msgid "Unable to cancel this purchase order." -msgstr "" +msgstr "Tega nabavnega naloga ni mogoče preklicati." #. module: purchase #: model:ir.ui.menu,name:purchase.menu_procurement_management_invoice @@ -693,6 +693,8 @@ msgid "" "Unique number of the purchase order, computed automatically when the " "purchase order is created." msgstr "" +"Enolična številka nabavnega naloga. ki se izračuna samodejno ob njegovem " +"kreiranju." #. module: purchase #: model:ir.ui.menu,name:purchase.menu_product_pricelist_action2_purchase @@ -720,13 +722,13 @@ msgstr "Cenik" #. module: purchase #: selection:purchase.order,state:0 msgid "Draft PO" -msgstr "" +msgstr "Osnutek nabavnega naloga" #. module: purchase #: model:process.node,name:purchase.process_node_draftpurchaseorder0 #: model:process.node,name:purchase.process_node_draftpurchaseorder1 msgid "RFQ" -msgstr "" +msgstr "Zahteva za ponudbo" #. module: purchase #: report:purchase.order:0 @@ -788,7 +790,7 @@ msgstr "" #: code:addons/purchase/purchase.py:1158 #, python-format msgid "Draft Purchase Order created" -msgstr "" +msgstr "Osnutek nabavnega naloga je kreiran" #. module: purchase #: model:ir.ui.menu,name:purchase.menu_product_category_config_purchase @@ -855,7 +857,7 @@ msgstr "Podjetja" #. module: purchase #: view:purchase.order.group:0 msgid "Are you sure you want to merge these orders?" -msgstr "" +msgstr "Ali res želite združiti te nabavne naloge?" #. module: purchase #: field:account.config.settings,module_purchase_analytic_plans:0 @@ -946,12 +948,12 @@ msgstr "Odobreno dne" #: code:addons/purchase/purchase.py:806 #, python-format msgid "Request for quotation created." -msgstr "" +msgstr "Zahteva za ponudbo kreirana." #. module: purchase #: view:product.product:0 msgid "a draft purchase order" -msgstr "" +msgstr "osnutek nabavnega naloga" #. module: purchase #: view:purchase.report:0 @@ -1011,7 +1013,7 @@ msgstr "" #: field:purchase.order,warehouse_id:0 #: field:stock.picking.in,warehouse_id:0 msgid "Destination Warehouse" -msgstr "" +msgstr "Ciljno skladišče" #. module: purchase #: code:addons/purchase/purchase.py:973 @@ -1019,7 +1021,7 @@ msgstr "" msgid "" "Selected Unit of Measure does not belong to the same category as the product " "Unit of Measure." -msgstr "" +msgstr "Izbrana enota mere ne pripada isti skupini kot enota mere produkta." #. module: purchase #: model:ir.ui.menu,name:purchase.menu_purchase_unit_measure_purchase @@ -1047,7 +1049,7 @@ msgstr "Nadzorna plošča za nakup" #: code:addons/purchase/purchase.py:566 #, python-format msgid "First cancel all receptions related to this purchase order." -msgstr "" +msgstr "Najprej prekličite vse prevzeme, vezane na ta nabavni nalog." #. module: purchase #: view:purchase.order:0 @@ -1069,7 +1071,7 @@ msgstr "" #. module: purchase #: view:purchase.order:0 msgid "Approved purchase order" -msgstr "" +msgstr "Potrjeni nabavni nalogi" #. module: purchase #: view:purchase.report:0 @@ -1142,7 +1144,7 @@ msgstr "Vrednost artiklov" #. module: purchase #: view:purchase.order:0 msgid "Purchase orders which are in exception state" -msgstr "" +msgstr "Nabavni nalogi s satusom \"izjeme\"" #. module: purchase #: model:process.node,note:purchase.process_node_draftpurchaseorder0 @@ -1248,7 +1250,7 @@ msgstr "" #. module: purchase #: view:purchase.order:0 msgid "Receive Invoice" -msgstr "" +msgstr "Prejeti račun" #. module: purchase #: view:purchase.order:0 @@ -1286,7 +1288,7 @@ msgstr "Stanje naročila" #. module: purchase #: field:purchase.order.line,product_uom:0 msgid "Product Unit of Measure" -msgstr "" +msgstr "Enota mere produkta" #. module: purchase #: model:ir.actions.act_window,help:purchase.purchase_pricelist_version_action @@ -1317,12 +1319,12 @@ msgstr "Naročilo naj naredi račun" #: code:addons/purchase/purchase.py:1133 #, python-format msgid "PO: %s" -msgstr "" +msgstr "Nabavni nalog: %s" #. module: purchase #: view:purchase.order:0 msgid "Send by EMail" -msgstr "" +msgstr "Pošlji po e-pošti" #. module: purchase #: code:addons/purchase/purchase.py:498 @@ -1571,7 +1573,7 @@ msgstr "" #. module: purchase #: selection:purchase.order,invoice_method:0 msgid "Based on Purchase Order lines" -msgstr "" +msgstr "Na osnovi pozicij nabavnega naloga" #. module: purchase #: field:purchase.order,amount_total:0 @@ -1597,12 +1599,12 @@ msgstr "Cilj" #. module: purchase #: field:purchase.order,dest_address_id:0 msgid "Customer Address (Direct Delivery)" -msgstr "" +msgstr "Naslov kupca (neposredna dostava)" #. module: purchase #: model:ir.actions.client,name:purchase.action_client_purchase_menu msgid "Open Purchase Menu" -msgstr "" +msgstr "Odpri meni nabave" #. module: purchase #: model:ir.model,name:purchase.model_mail_compose_message @@ -1650,7 +1652,7 @@ msgstr "Različica cenika privzetih naročil" #. module: purchase #: selection:purchase.order,invoice_method:0 msgid "Based on generated draft invoice" -msgstr "" +msgstr "Na osnovi kreiranega osnutka računa" #. module: purchase #: model:ir.actions.act_window,name:purchase.action_stock_move_report_po @@ -2151,7 +2153,7 @@ msgstr "Sklic vašega naročila" #. module: purchase #: report:purchase.order:0 msgid "Purchase Order Confirmation N°" -msgstr "" +msgstr "Potrditev nabavnega naloga št." #. module: purchase #: view:purchase.order:0 diff --git a/addons/purchase_double_validation/i18n/hr.po b/addons/purchase_double_validation/i18n/hr.po index 07f6afd7fcc..a6cf61bb869 100644 --- a/addons/purchase_double_validation/i18n/hr.po +++ b/addons/purchase_double_validation/i18n/hr.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openobject-addons\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2012-11-24 02:54+0000\n" -"PO-Revision-Date: 2011-12-12 07:16+0000\n" -"Last-Translator: Tomislav Bosnjakovic \n" +"PO-Revision-Date: 2012-12-10 06:50+0000\n" +"Last-Translator: Goran Kliska \n" "Language-Team: Croatian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-11-25 06:31+0000\n" -"X-Generator: Launchpad (build 16293)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:49+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: purchase_double_validation #: model:ir.model,name:purchase_double_validation.model_purchase_config_settings @@ -25,7 +25,7 @@ msgstr "" #. module: purchase_double_validation #: view:purchase.order:0 msgid "Purchase orders which are not approved yet." -msgstr "" +msgstr "Neodobreni nalozi." #. module: purchase_double_validation #: field:purchase.config.settings,limit_amount:0 @@ -36,12 +36,12 @@ msgstr "" #: view:board.board:0 #: model:ir.actions.act_window,name:purchase_double_validation.purchase_waiting msgid "Purchase Orders Waiting Approval" -msgstr "" +msgstr "Nalozi koji čekaju odobrenje" #. module: purchase_double_validation #: view:purchase.order:0 msgid "To Approve" -msgstr "" +msgstr "Za odobriti" #. module: purchase_double_validation #: help:purchase.config.settings,limit_amount:0 diff --git a/addons/sale/i18n/de.po b/addons/sale/i18n/de.po index ac92aa26c53..4564b4f22d8 100644 --- a/addons/sale/i18n/de.po +++ b/addons/sale/i18n/de.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-10 04:38+0000\n" -"X-Generator: Launchpad (build 16341)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:48+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: sale #: model:ir.model,name:sale.model_account_config_settings diff --git a/addons/sale/i18n/es_AR.po b/addons/sale/i18n/es_AR.po index 972215fda66..4f84f06b414 100644 --- a/addons/sale/i18n/es_AR.po +++ b/addons/sale/i18n/es_AR.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-10 04:38+0000\n" -"X-Generator: Launchpad (build 16341)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:48+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: sale #: model:ir.model,name:sale.model_account_config_settings diff --git a/addons/sale/i18n/it.po b/addons/sale/i18n/it.po index 7d67a3a9b31..095addb2c42 100644 --- a/addons/sale/i18n/it.po +++ b/addons/sale/i18n/it.po @@ -7,14 +7,14 @@ msgstr "" "Project-Id-Version: OpenERP Server 6.0dev_rc3\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-12-03 16:02+0000\n" -"PO-Revision-Date: 2012-12-09 14:03+0000\n" +"PO-Revision-Date: 2012-12-10 19:36+0000\n" "Last-Translator: Davide Corio - agilebg.com \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-10 04:38+0000\n" -"X-Generator: Launchpad (build 16341)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:48+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: sale #: model:ir.model,name:sale.model_account_config_settings @@ -268,7 +268,7 @@ msgstr "I Miei Preventivi" #. module: sale #: field:sale.config.settings,module_warning:0 msgid "Allow configuring alerts by customer or products" -msgstr "" +msgstr "Consente di configurare avvisi per cliente o per prodotto" #. module: sale #: field:sale.shop,name:0 @@ -299,7 +299,7 @@ msgstr "Preventivo " #: code:addons/sale/wizard/sale_make_invoice_advance.py:106 #, python-format msgid "Advance of %s %%" -msgstr "" +msgstr "Anticipo su %s %%" #. module: sale #: model:ir.actions.act_window,name:sale.action_order_tree2 @@ -421,7 +421,7 @@ msgstr "In corso" #: code:addons/sale/sale.py:667 #, python-format msgid "Draft Invoice of %s %s waiting for validation." -msgstr "" +msgstr "Fattura bozza per %s %s in attesa di validazione." #. module: sale #: model:process.transition,note:sale.process_transition_confirmquotation0 @@ -437,6 +437,8 @@ msgstr "" #, python-format msgid "You cannot cancel a sale order line that has already been invoiced." msgstr "" +"Non è possibile annullare una riga di ordine di vendita se è già stata " +"fatturata." #. module: sale #: report:sale.order:0 @@ -469,7 +471,7 @@ msgstr "" #. module: sale #: model:res.groups,name:sale.group_delivery_invoice_address msgid "Addresses in Sale Orders" -msgstr "" +msgstr "Indirizzi negli Ordini di Vendita" #. module: sale #: field:sale.order,project_id:0 @@ -479,7 +481,7 @@ msgstr "Contratto / Conto Analitico" #. module: sale #: model:res.groups,name:sale.group_invoice_so_lines msgid "Enable Invoicing Sale order lines" -msgstr "" +msgstr "Abilita la fatturazione da righe d'ordine di vendita" #. module: sale #: view:sale.report:0 @@ -534,7 +536,7 @@ msgstr "Marzo" #. module: sale #: model:ir.actions.server,name:sale.actions_server_sale_order_read msgid "Sale: Mark read" -msgstr "" +msgstr "Vendite: Segna come letto" #. module: sale #: help:sale.order,amount_total:0 @@ -564,7 +566,7 @@ msgstr "Indirizzo di Fatturazione" #. module: sale #: field:sale.order.line,product_uom:0 msgid "Unit of Measure " -msgstr "" +msgstr "Unità di Misura " #. module: sale #: field:sale.config.settings,time_unit:0 @@ -614,7 +616,7 @@ msgstr "" #. module: sale #: view:sale.order:0 msgid "Print" -msgstr "" +msgstr "Stampa" #. module: sale #: report:sale.order:0 @@ -699,18 +701,18 @@ msgstr "" #: code:addons/sale/wizard/sale_make_invoice_advance.py:95 #, python-format msgid "Configuration Error!" -msgstr "" +msgstr "Errore di configurazione!" #. module: sale #: view:sale.order:0 msgid "Send by Email" -msgstr "" +msgstr "Invia tramite Email" #. module: sale #: code:addons/sale/res_config.py:89 #, python-format msgid "Hour" -msgstr "" +msgstr "Ora" #. module: sale #: view:sale.order:0 diff --git a/addons/stock/i18n/sl.po b/addons/stock/i18n/sl.po index 1ba30d7c9d3..97d589fd599 100644 --- a/addons/stock/i18n/sl.po +++ b/addons/stock/i18n/sl.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-10 04:36+0000\n" -"X-Generator: Launchpad (build 16341)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:46+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: stock #: field:stock.inventory.line.split,line_exist_ids:0 diff --git a/addons/survey/i18n/hr.po b/addons/survey/i18n/hr.po index 22e5d04f042..a7ec1dc02b5 100644 --- a/addons/survey/i18n/hr.po +++ b/addons/survey/i18n/hr.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openobject-addons\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2012-12-03 16:04+0000\n" -"PO-Revision-Date: 2012-05-10 17:46+0000\n" -"Last-Translator: Raphael Collet (OpenERP) \n" +"PO-Revision-Date: 2012-12-10 08:30+0000\n" +"Last-Translator: Goran Kliska \n" "Language-Team: Vinteh\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:45+0000\n" -"X-Generator: Launchpad (build 16335)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:49+0000\n" +"X-Generator: Launchpad (build 16356)\n" "Language: hr\n" #. module: survey @@ -48,7 +48,7 @@ msgstr "Text Validation" #. module: survey #: view:survey:0 msgid "Answers:" -msgstr "" +msgstr "Odgovori:" #. module: survey #: view:survey:0 @@ -59,7 +59,7 @@ msgstr "Pozvani korisnik" #. module: survey #: model:survey.type,name:survey.survey_type1 msgid "Human Resources" -msgstr "" +msgstr "Ljudski resursi" #. module: survey #: selection:survey.answer,type:0 @@ -360,7 +360,7 @@ msgstr "" #. module: survey #: field:survey,color:0 msgid "Color Index" -msgstr "" +msgstr "Indeks boje" #. module: survey #: selection:survey.question,type:0 @@ -439,7 +439,7 @@ msgstr "Tekst" #. module: survey #: view:survey:0 msgid "Edit..." -msgstr "" +msgstr "Uredi ..." #. module: survey #: selection:survey.print,paper_size:0 @@ -534,7 +534,7 @@ msgstr "Faza plana ocjenjivanja" #: code:addons/survey/wizard/survey_send_invitation.py:202 #, python-format msgid "Unknown" -msgstr "" +msgstr "Nepoznato" #. module: survey #: view:survey:0 @@ -553,7 +553,7 @@ msgstr "Ispis" #. module: survey #: view:survey:0 msgid "New" -msgstr "" +msgstr "Nova(i)" #. module: survey #: field:survey.question,make_comment_field:0 @@ -577,7 +577,7 @@ msgstr "E-mail" #: code:addons/survey/survey.py:261 #, python-format msgid "%s (copy)" -msgstr "" +msgstr "%s (kopija)" #. module: survey #: selection:survey.response,state:0 @@ -603,7 +603,7 @@ msgstr "" #. module: survey #: model:ir.ui.menu,name:survey.menu_reporting msgid "Reporting" -msgstr "" +msgstr "Izvještaji" #. module: survey #: model:ir.actions.act_window,name:survey.act_survey_answer @@ -739,7 +739,7 @@ msgstr "" #. module: survey #: view:survey:0 msgid "Delete" -msgstr "" +msgstr "Briši" #. module: survey #: field:survey.response.answer,comment_field:0 @@ -1571,7 +1571,7 @@ msgstr "Sum of all choices" #. module: survey #: view:survey.request:0 msgid "Request" -msgstr "" +msgstr "Zahtjev" #. module: survey #: model:ir.model,name:survey.model_survey_response @@ -1635,7 +1635,7 @@ msgstr "Error message" #: code:addons/survey/wizard/survey_send_invitation.py:74 #, python-format msgid "Warning!" -msgstr "" +msgstr "Upozorenje!" #. module: survey #: code:addons/survey/survey.py:467 @@ -1657,7 +1657,7 @@ msgstr "Izbor menija" #. module: survey #: field:survey,id:0 msgid "ID" -msgstr "" +msgstr "ID" #. module: survey #: code:addons/survey/survey.py:489 diff --git a/addons/survey/i18n/pt.po b/addons/survey/i18n/pt.po index 1eb0c55dd64..a0dbaf26c33 100644 --- a/addons/survey/i18n/pt.po +++ b/addons/survey/i18n/pt.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openobject-addons\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2012-12-03 16:04+0000\n" -"PO-Revision-Date: 2012-05-10 17:16+0000\n" -"Last-Translator: Raphael Collet (OpenERP) \n" +"PO-Revision-Date: 2012-12-10 18:21+0000\n" +"Last-Translator: Andrei Talpa (multibase.pt) \n" "Language-Team: Portuguese \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:45+0000\n" -"X-Generator: Launchpad (build 16335)\n" +"X-Launchpad-Export-Date: 2012-12-11 04:49+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: survey #: view:survey.response.line:0 @@ -48,7 +48,7 @@ msgstr "Validação de texto" #. module: survey #: view:survey:0 msgid "Answers:" -msgstr "" +msgstr "Respostas:" #. module: survey #: view:survey:0 @@ -59,7 +59,7 @@ msgstr "Utilizador convidado" #. module: survey #: model:survey.type,name:survey.survey_type1 msgid "Human Resources" -msgstr "" +msgstr "Recursos humanos" #. module: survey #: selection:survey.answer,type:0 @@ -359,7 +359,7 @@ msgstr "" #. module: survey #: field:survey,color:0 msgid "Color Index" -msgstr "" +msgstr "Índice de cor" #. module: survey #: selection:survey.question,type:0 @@ -438,7 +438,7 @@ msgstr "Texto" #. module: survey #: view:survey:0 msgid "Edit..." -msgstr "" +msgstr "Editar..." #. module: survey #: selection:survey.print,paper_size:0 @@ -1715,7 +1715,7 @@ msgstr "Fracionário" #: view:survey.print.statistics:0 #: view:survey.send.invitation:0 msgid "or" -msgstr "" +msgstr "ou" #. module: survey #: field:survey,title:0 diff --git a/addons/web/i18n/de.po b/addons/web/i18n/de.po index 0d14114b433..f8439d25e4b 100644 --- a/addons/web/i18n/de.po +++ b/addons/web/i18n/de.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openerp-web\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2012-11-30 18:13+0000\n" -"PO-Revision-Date: 2012-02-07 19:41+0000\n" -"Last-Translator: Ferdinand @ Camptocamp \n" +"PO-Revision-Date: 2012-12-10 23:11+0000\n" +"Last-Translator: Felix Schubert \n" "Language-Team: German \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-01 05:20+0000\n" -"X-Generator: Launchpad (build 16319)\n" +"X-Launchpad-Export-Date: 2012-12-11 05:03+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: web #. openerp-web @@ -29,14 +29,14 @@ msgstr "Standardsprache:" #: code:addons/web/static/src/js/coresetup.js:593 #, python-format msgid "%d minutes ago" -msgstr "" +msgstr "vor %d Minuten" #. module: web #. openerp-web #: code:addons/web/static/src/js/coresetup.js:621 #, python-format msgid "Still loading...
Please be patient." -msgstr "" +msgstr "Lädt immer noch ...
Bitte haben Sie noch etwas Geduld." #. module: web #. openerp-web @@ -75,7 +75,7 @@ msgstr "Master Passwort" #: code:addons/web/static/src/xml/base.xml:274 #, python-format msgid "Change Master Password" -msgstr "" +msgstr "Hauptpasswort ändern" #. module: web #. openerp-web @@ -103,7 +103,7 @@ msgstr "" #: code:addons/web/static/src/js/coresetup.js:594 #, python-format msgid "about an hour ago" -msgstr "" +msgstr "vor einer Stunde" #. module: web #. openerp-web @@ -112,7 +112,7 @@ msgstr "" #: code:addons/web/static/src/xml/base.xml:216 #, python-format msgid "Backup Database" -msgstr "" +msgstr "Sicherung der Datenbank" #. module: web #. openerp-web @@ -134,7 +134,7 @@ msgstr "Hier ist eine Vorschau der Datei die nicht importiert werden konnte" #: code:addons/web/static/src/js/coresetup.js:592 #, python-format msgid "about a minute ago" -msgstr "" +msgstr "vor einer Minute" #. module: web #. openerp-web @@ -213,7 +213,7 @@ msgstr "" #: code:addons/web/static/src/xml/base.xml:1583 #, python-format msgid "Share with all users" -msgstr "" +msgstr "Mit allen Benutzern teilen" #. module: web #. openerp-web @@ -277,14 +277,14 @@ msgstr "Datei hochladen" #: code:addons/web/static/src/js/coresetup.js:598 #, python-format msgid "about a month ago" -msgstr "" +msgstr "letzten Monat" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:1575 #, python-format msgid "Custom Filters" -msgstr "" +msgstr "Benutzerdefinierte Filter" #. module: web #. openerp-web @@ -305,14 +305,14 @@ msgstr "OpenERP SA" #: code:addons/web/static/src/js/search.js:1555 #, python-format msgid "Custom Filter" -msgstr "" +msgstr "Benutzerdefinierter Filter" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:159 #, python-format msgid "Duplicate Database" -msgstr "" +msgstr "Datenbank kopieren" #. module: web #. openerp-web @@ -390,7 +390,7 @@ msgstr "" #: code:addons/web/static/src/xml/base.xml:1245 #, python-format msgid "...Upload in progress..." -msgstr "" +msgstr "... Wird hochgeladen ..." #. module: web #. openerp-web @@ -411,7 +411,7 @@ msgstr "" #: code:addons/web/static/src/js/view_form.js:4811 #, python-format msgid "File upload" -msgstr "" +msgstr "Datei hochladen" #. module: web #. openerp-web @@ -447,7 +447,7 @@ msgstr "" #: code:addons/web/static/src/xml/base.xml:413 #, python-format msgid "Activate the developer mode" -msgstr "" +msgstr "Entwickler Modus aktivieren" #. module: web #. openerp-web @@ -461,14 +461,14 @@ msgstr "Lade (%d)" #: code:addons/web/static/src/js/search.js:1116 #, python-format msgid "GroupBy" -msgstr "" +msgstr "Gruppieren nach" #. module: web #. openerp-web #: code:addons/web/static/src/js/view_list.js:684 #, python-format msgid "You must select at least one record." -msgstr "" +msgstr "Sie müssen mindestens einen Datensatz auswählen" #. module: web #. openerp-web @@ -496,7 +496,7 @@ msgstr "Relation:" #: code:addons/web/static/src/js/coresetup.js:591 #, python-format msgid "less than a minute ago" -msgstr "" +msgstr "vor weniger als einer Minute" #. module: web #. openerp-web @@ -594,7 +594,7 @@ msgstr "Für weitere Informationen besuchen Sie:" #: code:addons/web/static/src/xml/base.xml:1834 #, python-format msgid "Add All Info..." -msgstr "" +msgstr "All Informationen hinzufügen..." #. module: web #. openerp-web @@ -645,7 +645,7 @@ msgstr "ist größer als" #: code:addons/web/static/src/xml/base.xml:540 #, python-format msgid "View" -msgstr "Sicht" +msgstr "Ansicht" #. module: web #. openerp-web @@ -737,7 +737,7 @@ msgstr "EMail-Fehler" #: code:addons/web/static/src/js/coresetup.js:596 #, python-format msgid "a day ago" -msgstr "" +msgstr "Vor 1 Tag" #. module: web #. openerp-web @@ -762,6 +762,10 @@ msgid "" "\n" "Are you sure you want to leave this page ?" msgstr "" +"Achtung! Der Datensatz wurde geändert. Ihre Änderungen werden verloren " +"gehen!\n" +"\n" +"Sind Sie sicher, dass Sie diese Seite verlassen wollen?" #. module: web #. openerp-web @@ -782,7 +786,7 @@ msgstr "Technische Übersetzung" #: code:addons/web/static/src/xml/base.xml:1772 #, python-format msgid "Delimiter:" -msgstr "Feldtrenner:" +msgstr "Trennzeichen:" #. module: web #. openerp-web @@ -803,7 +807,7 @@ msgstr "" #: code:addons/web/static/src/xml/base.xml:1439 #, python-format msgid "-- Actions --" -msgstr "-- Vorgänge --" +msgstr "-- Actions --" #. module: web #. openerp-web @@ -841,7 +845,7 @@ msgstr "Kann Nachricht nicht an ungültige EMail-Adresse senden" #: code:addons/web/static/src/xml/base.xml:614 #, python-format msgid "Add..." -msgstr "" +msgstr "Hinzufügen ..." #. module: web #. openerp-web @@ -863,7 +867,7 @@ msgstr "" #: code:addons/web/static/src/xml/base.xml:185 #, python-format msgid "Drop Database" -msgstr "" +msgstr "Datenbank löschen" #. module: web #. openerp-web @@ -884,7 +888,7 @@ msgstr "Modifikatoren:" #: code:addons/web/static/src/xml/base.xml:605 #, python-format msgid "Delete this attachment" -msgstr "" +msgstr "Diesen Anhang löschen" #. module: web #. openerp-web @@ -902,7 +906,7 @@ msgstr "Speichern" #: code:addons/web/static/src/xml/base.xml:355 #, python-format msgid "More" -msgstr "" +msgstr "Mehr" #. module: web #. openerp-web @@ -992,7 +996,7 @@ msgstr "Zeige Log (%s)" #: code:addons/web/static/src/xml/base.xml:558 #, python-format msgid "Creation Date:" -msgstr "Datum Erstellung" +msgstr "Erstellungsdatum:" #. module: web #: code:addons/web/controllers/main.py:806 @@ -1006,7 +1010,7 @@ msgstr "" #: code:addons/web/static/src/js/view_form.js:4810 #, python-format msgid "The selected file exceed the maximum file size of %s." -msgstr "" +msgstr "Die ausgewählte Datei überschreitet die maximale Dateigröße von %s." #. module: web #. openerp-web @@ -1076,7 +1080,7 @@ msgstr "(keine Bezeichnung)" #: code:addons/web/static/src/js/coresetup.js:597 #, python-format msgid "%d days ago" -msgstr "" +msgstr "vor %d Tagen" #. module: web #. openerp-web @@ -1101,7 +1105,7 @@ msgstr "Abbrechen" #: code:addons/web/static/src/xml/base.xml:9 #, python-format msgid "Loading..." -msgstr "Lade..." +msgstr "Lädt…" #. module: web #. openerp-web @@ -1130,7 +1134,7 @@ msgstr "Unbekannter Operator %s in Domain %s" #: code:addons/web/static/src/js/view_form.js:426 #, python-format msgid "%d / %d" -msgstr "" +msgstr "%d / %d" #. module: web #. openerp-web @@ -1163,7 +1167,7 @@ msgstr "" #: code:addons/web/static/src/js/coresetup.js:599 #, python-format msgid "%d months ago" -msgstr "" +msgstr "vor %d Monaten" #. module: web #. openerp-web @@ -1192,7 +1196,7 @@ msgstr "" #: code:addons/web/static/src/xml/base.xml:248 #, python-format msgid "Restore Database" -msgstr "" +msgstr "Datenbank wiederherstellen" #. module: web #. openerp-web @@ -1271,7 +1275,7 @@ msgstr "Sie müssen mindestens einen Datensatz auswählen" #: code:addons/web/static/src/js/coresetup.js:622 #, python-format msgid "Don't leave yet,
it's still loading..." -msgstr "" +msgstr "Nicht verlassen.
Ladevorgang noch aktiv" #. module: web #. openerp-web @@ -1320,14 +1324,14 @@ msgstr "" #: code:addons/web/static/src/js/view_form.js:4846 #, python-format msgid "Save As..." -msgstr "" +msgstr "Speichern als..." #. module: web #. openerp-web #: code:addons/web/static/src/js/view_form.js:4971 #, python-format msgid "Could not display the selected image." -msgstr "" +msgstr "Das ausgewählte Bild kann nicht angezeigt werden" #. module: web #. openerp-web @@ -1385,7 +1389,7 @@ msgstr "Standardwert ändern:" #: code:addons/web/static/src/xml/base.xml:171 #, python-format msgid "Original database name:" -msgstr "" +msgstr "Original Datenbank Name :" #. module: web #. openerp-web @@ -1395,7 +1399,7 @@ msgstr "" #: code:addons/web/static/src/js/search.js:1940 #, python-format msgid "is equal to" -msgstr "Ist gleich" +msgstr "ist gleich" #. module: web #. openerp-web @@ -1409,7 +1413,7 @@ msgstr "" #: code:addons/web/static/src/xml/base.xml:1594 #, python-format msgid "Advanced Search" -msgstr "" +msgstr "Erweiterte Suche" #. module: web #. openerp-web @@ -1423,7 +1427,7 @@ msgstr "Bestätigen Sie das neue Master Passwort:" #: code:addons/web/static/src/js/coresetup.js:625 #, python-format msgid "Maybe you should consider reloading the application by pressing F5..." -msgstr "" +msgstr "Viellecht sollten Sie die Anwendung mit F5 neu laden" #. module: web #. openerp-web @@ -1455,7 +1459,7 @@ msgstr "Import-Einstellungen" #: code:addons/web/static/src/js/view_form.js:2909 #, python-format msgid "Add %s" -msgstr "" +msgstr "%s hinzufügen" #. module: web #. openerp-web @@ -1480,6 +1484,7 @@ msgstr "Schließen" msgid "" "You may not believe it,
but the application is actually loading..." msgstr "" +"Sie werden es kaum glauben,
aber die Anwendung wird gerade geladen." #. module: web #. openerp-web @@ -1493,7 +1498,7 @@ msgstr "CSV Datei:" #: code:addons/web/static/src/js/search.js:1743 #, python-format msgid "Advanced" -msgstr "" +msgstr "Erweitert" #. module: web #. openerp-web @@ -1541,7 +1546,7 @@ msgstr "" #: code:addons/web/static/src/xml/base.xml:1403 #, python-format msgid "Advanced Search..." -msgstr "" +msgstr "Erweiterte Suche ..." #. module: web #. openerp-web @@ -1592,21 +1597,21 @@ msgstr "Größe:" #: code:addons/web/static/src/xml/base.xml:1799 #, python-format msgid "--- Don't Import ---" -msgstr "" +msgstr "--- Nicht importieren ---" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:1654 #, python-format msgid "Import-Compatible Export" -msgstr "" +msgstr "Import-kompatibler Export" #. module: web #. openerp-web #: code:addons/web/static/src/js/coresetup.js:601 #, python-format msgid "%d years ago" -msgstr "" +msgstr "vor %d Jahren" #. module: web #. openerp-web @@ -1657,7 +1662,7 @@ msgstr "Ansichten verwalten" #: code:addons/web/static/src/xml/base.xml:1776 #, python-format msgid "Encoding:" -msgstr "Zeichenkodierung:" +msgstr "Zeichensatz:" #. module: web #. openerp-web @@ -1678,8 +1683,7 @@ msgstr "" #: code:addons/web/static/src/js/data_export.js:361 #, python-format msgid "Please select fields to save export list..." -msgstr "" -"Bitte wählen Sie die Felder die in der Exportliste gespeichert werden sollen." +msgstr "Bitte wählen Sie die zu exportierenden Felder..." #. module: web #. openerp-web @@ -1826,7 +1830,7 @@ msgstr "Hochladen ..." #: code:addons/web/static/src/xml/base.xml:1828 #, python-format msgid "Name:" -msgstr "" +msgstr "Name:" #. module: web #. openerp-web @@ -1936,14 +1940,14 @@ msgstr "Objekt:" #: code:addons/web/static/src/js/chrome.js:326 #, python-format msgid "Loading" -msgstr "" +msgstr "Laden" #. module: web #. openerp-web #: code:addons/web/static/src/js/coresetup.js:600 #, python-format msgid "about a year ago" -msgstr "" +msgstr "letztes Jahr" #. module: web #. openerp-web @@ -1969,7 +1973,7 @@ msgstr "" #: code:addons/web/static/src/js/coresetup.js:595 #, python-format msgid "%d hours ago" -msgstr "" +msgstr "vor %d Stunden" #. module: web #. openerp-web @@ -2012,7 +2016,7 @@ msgstr "Ok" #: code:addons/web/static/src/js/views.js:1163 #, python-format msgid "Uploading..." -msgstr "" +msgstr "Hochladen …" #. module: web #. openerp-web @@ -2086,7 +2090,7 @@ msgstr "" #: code:addons/web/static/src/xml/base.xml:1578 #, python-format msgid "Save current filter" -msgstr "" +msgstr "Aktuellen Filter speichern" #. module: web #. openerp-web @@ -2136,7 +2140,7 @@ msgstr "Feld Ansicht Definition" #: code:addons/web/static/src/xml/base.xml:149 #, python-format msgid "Confirm password:" -msgstr "Passwort wiederholen:" +msgstr "Passwort bestätigen:" #. module: web #. openerp-web @@ -2187,7 +2191,7 @@ msgstr "ist falsch" #: code:addons/web/static/src/xml/base.xml:404 #, python-format msgid "About OpenERP" -msgstr "" +msgstr "Über OpenERP" #. module: web #. openerp-web @@ -2207,14 +2211,14 @@ msgstr "" #: code:addons/web/static/src/xml/base.xml:304 #, python-format msgid "Database Management" -msgstr "" +msgstr "Datenbank Verwaltung" #. module: web #. openerp-web #: code:addons/web/static/src/js/view_form.js:4971 #, python-format msgid "Image" -msgstr "" +msgstr "Bild" #. module: web #. openerp-web @@ -2235,7 +2239,7 @@ msgstr "" #: code:addons/web/static/src/js/search.js:1285 #, python-format msgid "not a valid integer" -msgstr "ungültiger Trigger" +msgstr "ungültiger Integer" #. module: web #. openerp-web @@ -2293,7 +2297,7 @@ msgstr "" #: code:addons/web/static/src/js/coresetup.js:620 #, python-format msgid "Still loading..." -msgstr "" +msgstr "Noch am Laden..." #. module: web #. openerp-web @@ -2328,7 +2332,7 @@ msgstr "" #: code:addons/web/static/src/js/view_form.js:4846 #, python-format msgid "The field is empty, there's nothing to save !" -msgstr "" +msgstr "Das Feld ist leer, sie können nichts speichern!" #. module: web #. openerp-web @@ -2356,7 +2360,7 @@ msgstr "" #: code:addons/web/static/src/xml/base.xml:928 #, python-format msgid "Widget:" -msgstr "Bedienelement:" +msgstr "Widget:" #. module: web #. openerp-web @@ -2391,7 +2395,7 @@ msgstr "Workflow bearbeiten" #: code:addons/web/static/src/js/views.js:1172 #, python-format msgid "Do you really want to delete this attachment ?" -msgstr "" +msgstr "Wollen Sie diesen Anhang wirklich löschen?" #. module: web #. openerp-web @@ -2434,7 +2438,7 @@ msgstr "Client Fehler" #: code:addons/web/static/src/js/views.js:996 #, python-format msgid "Print" -msgstr "" +msgstr "Drucken" #. module: web #. openerp-web @@ -2476,7 +2480,7 @@ msgstr "Speichern & Beenden" #: code:addons/web/static/src/js/view_form.js:2818 #, python-format msgid "Search More..." -msgstr "" +msgstr "Mehr suchen" #. module: web #. openerp-web diff --git a/addons/web/i18n/es.po b/addons/web/i18n/es.po index c098fde8581..4a0ed8a67a3 100644 --- a/addons/web/i18n/es.po +++ b/addons/web/i18n/es.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openerp-web\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2012-11-30 18:13+0000\n" -"PO-Revision-Date: 2012-12-07 19:11+0000\n" -"Last-Translator: Eduardo Alberto Calvo \n" +"PO-Revision-Date: 2012-12-10 20:41+0000\n" +"Last-Translator: Santi (Pexego) \n" "Language-Team: Spanish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-08 05:21+0000\n" -"X-Generator: Launchpad (build 16341)\n" +"X-Launchpad-Export-Date: 2012-12-11 05:03+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: web #. openerp-web @@ -43,7 +43,7 @@ msgstr "Todavía está cargando ...
Por favor, sea paciente." #: code:addons/web/static/src/js/search.js:1834 #, python-format msgid "%(field)s %(operator)s \"%(value)s\"" -msgstr "" +msgstr "%(field)s %(operator)s \"%(value)s\"" #. module: web #. openerp-web @@ -676,7 +676,7 @@ msgstr "" #: code:addons/web/static/src/js/view_form.js:1238 #, python-format msgid "Field '%s' specified in view could not be found." -msgstr "" +msgstr "No se encontró el campo '%s' especificado en la vista." #. module: web #. openerp-web @@ -1298,7 +1298,7 @@ msgstr "Búsqueda incorrecta" #: code:addons/web/static/src/js/view_list.js:959 #, python-format msgid "Could not find id in dataset" -msgstr "" +msgstr "No se ha podido encontrar un dataset" #. module: web #. openerp-web @@ -1661,7 +1661,7 @@ msgstr "Por favor ingrese su nueva contraseña" #: code:addons/web/static/src/js/views.js:1463 #, python-format msgid "Could not parse string to xml" -msgstr "" +msgstr "No se ha podido parsear la cadena a xml" #. module: web #. openerp-web @@ -2049,14 +2049,14 @@ msgstr "Cargar datos de demostración:" #: code:addons/web/static/src/js/dates.js:26 #, python-format msgid "'%s' is not a valid datetime" -msgstr "" +msgstr "'%s' no es un fecha y hora válido" #. module: web #. openerp-web #: code:addons/web/static/src/js/views.js:1471 #, python-format msgid "Could not find a DOM Parser: %s" -msgstr "" +msgstr "No se pudo encontrar un parser DOM: %s" #. module: web #. openerp-web @@ -2443,7 +2443,7 @@ msgstr "La base de datos %s ha sido eliminada" #: code:addons/web/static/src/xml/base.xml:456 #, python-format msgid "User's timezone" -msgstr "" +msgstr "Zona horaria del usuario" #. module: web #. openerp-web diff --git a/addons/web/i18n/hu.po b/addons/web/i18n/hu.po index 438a16e5aef..0f476162c9c 100644 --- a/addons/web/i18n/hu.po +++ b/addons/web/i18n/hu.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openerp-web\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2012-11-30 18:13+0000\n" -"PO-Revision-Date: 2012-11-28 10:21+0000\n" -"Last-Translator: Juhász Krisztián \n" +"PO-Revision-Date: 2012-12-10 13:43+0000\n" +"Last-Translator: krnkris \n" "Language-Team: Hungarian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-01 05:20+0000\n" -"X-Generator: Launchpad (build 16319)\n" +"X-Launchpad-Export-Date: 2012-12-11 05:03+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: web #. openerp-web @@ -59,7 +59,7 @@ msgstr "kisebb vagy egyenlő, mint" #: code:addons/web/static/src/js/chrome.js:393 #, python-format msgid "Please enter your previous password" -msgstr "" +msgstr "Kérem írja be az előző jelszavát" #. module: web #. openerp-web @@ -82,28 +82,28 @@ msgstr "Mester jelszó változtatása" #: code:addons/web/static/src/js/chrome.js:491 #, python-format msgid "Do you really want to delete the database: %s ?" -msgstr "" +msgstr "Tényleg törölni szeretné az adatbázist: %s ?" #. module: web #. openerp-web #: code:addons/web/static/src/js/search.js:1400 #, python-format msgid "Search %(field)s at: %(value)s" -msgstr "" +msgstr "Keresett %(field)s itt: %(value)s" #. module: web #. openerp-web #: code:addons/web/static/src/js/chrome.js:537 #, python-format msgid "Access Denied" -msgstr "" +msgstr "Hozzáférés megtagadva" #. module: web #. openerp-web #: code:addons/web/static/src/js/coresetup.js:594 #, python-format msgid "about an hour ago" -msgstr "" +msgstr "körülbelül egy órája" #. module: web #. openerp-web @@ -120,7 +120,7 @@ msgstr "Adatbázis mentése" #: code:addons/web/static/src/js/dates.js:53 #, python-format msgid "'%s' is not a valid date" -msgstr "" +msgstr "'%s' egy nem érvényes dátum" #. module: web #. openerp-web @@ -147,7 +147,7 @@ msgstr "Fájl" #: code:addons/web/controllers/main.py:842 #, python-format msgid "You cannot leave any password empty." -msgstr "" +msgstr "Nem hagyhat egyetlen jelszót sem üresen" #. module: web #. openerp-web @@ -185,14 +185,14 @@ msgstr "Sikeres adatbázis visszaállítás" #: code:addons/web/static/src/xml/base.xml:415 #, python-format msgid "Version" -msgstr "" +msgstr "Verzió" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:564 #, python-format msgid "Latest Modification Date:" -msgstr "" +msgstr "Utolsó módosítás dátuma:" #. module: web #. openerp-web @@ -200,13 +200,14 @@ msgstr "" #, python-format msgid "M2O search fields do not currently handle multiple default values" msgstr "" +"M2O kereső mező nem támogatja jelenleg a többszörös alapértelmezett értékeket" #. module: web #. openerp-web #: code:addons/web/static/src/js/view_form.js:1242 #, python-format msgid "Widget type '%s' is not implemented" -msgstr "" +msgstr "'%s' vezérő típus nem beillesztett" #. module: web #. openerp-web @@ -228,14 +229,14 @@ msgstr "Űrlap" #: code:addons/web/static/src/xml/base.xml:1299 #, python-format msgid "(no string)" -msgstr "" +msgstr "(nincs érték)" #. module: web #. openerp-web #: code:addons/web/static/src/js/formats.js:282 #, python-format msgid "'%s' is not a correct time" -msgstr "" +msgstr "'%s' nem érvényes idő meghatározás" #. module: web #. openerp-web @@ -270,7 +271,7 @@ msgstr "Nem definiált" #: code:addons/web/static/src/js/view_form.js:4831 #, python-format msgid "File Upload" -msgstr "" +msgstr "File feltöltés" #. module: web #. openerp-web @@ -291,7 +292,7 @@ msgstr "Egyedi szűrők" #: code:addons/web/static/src/xml/base.xml:1311 #, python-format msgid "Button Type:" -msgstr "" +msgstr "Gomb típusa:" #. module: web #. openerp-web @@ -305,7 +306,7 @@ msgstr "OpenERP SA Company" #: code:addons/web/static/src/js/search.js:1555 #, python-format msgid "Custom Filter" -msgstr "" +msgstr "Egyéni szűrés" #. module: web #. openerp-web @@ -334,7 +335,7 @@ msgstr "Jelszó megváltoztatása" #: code:addons/web/static/src/js/view_form.js:3382 #, python-format msgid "View type '%s' is not supported in One2Many." -msgstr "" +msgstr "'%s' nézet típus nem támogatott a One2Many által." #. module: web #. openerp-web @@ -349,7 +350,7 @@ msgstr "Letöltés" #: code:addons/web/static/src/js/formats.js:266 #, python-format msgid "'%s' is not a correct datetime" -msgstr "" +msgstr "'%s' em érvényes dátum időpont" #. module: web #. openerp-web @@ -363,7 +364,7 @@ msgstr "Csoport" #: code:addons/web/static/src/xml/base.xml:905 #, python-format msgid "Unhandled widget" -msgstr "" +msgstr "Nem támogatott vezérlők" #. module: web #. openerp-web @@ -377,13 +378,13 @@ msgstr "Kiválasztás:" #: code:addons/web/static/src/js/view_form.js:869 #, python-format msgid "The following fields are invalid:" -msgstr "" +msgstr "A következő mezők érvénytelenek:" #. module: web #: code:addons/web/controllers/main.py:863 #, python-format msgid "Languages" -msgstr "" +msgstr "Nyelvek" #. module: web #. openerp-web @@ -404,7 +405,7 @@ msgstr "Import" #: code:addons/web/static/src/js/chrome.js:543 #, python-format msgid "Could not restore the database" -msgstr "" +msgstr "Nem lehet visszaállítani az adatbázist" #. module: web #. openerp-web @@ -418,7 +419,7 @@ msgstr "Fájl feltöltés" #: code:addons/web/static/src/js/view_form.js:3775 #, python-format msgid "Action Button" -msgstr "" +msgstr "Végrehajtás gomb" #. module: web #. openerp-web @@ -440,7 +441,7 @@ msgstr "tartalmazza" #: code:addons/web/static/src/js/coresetup.js:624 #, python-format msgid "Take a minute to get a coffee,
because it's loading..." -msgstr "" +msgstr "Hagy szünetet és menj kávézni,
mert most töltődik..." #. module: web #. openerp-web @@ -475,7 +476,7 @@ msgstr "Legalább egy a rekordot válasszon ki." #: code:addons/web/static/src/xml/base.xml:529 #, python-format msgid "View Log (perm_read)" -msgstr "" +msgstr "Napló megnézése (perm_read)" #. module: web #. openerp-web @@ -489,7 +490,7 @@ msgstr "Alapértelmezett értékek beállítása" #: code:addons/web/static/src/xml/base.xml:956 #, python-format msgid "Relation:" -msgstr "" +msgstr "Kapcsolat:" #. module: web #. openerp-web @@ -510,14 +511,14 @@ msgstr "Feltétel:" #: code:addons/web/static/src/js/view_form.js:1707 #, python-format msgid "Unsupported operator %s in domain %s" -msgstr "" +msgstr "Nem támogatott operátor %s ebben a domainban %s" #. module: web #. openerp-web #: code:addons/web/static/src/js/formats.js:242 #, python-format msgid "'%s' is not a correct float" -msgstr "" +msgstr "'%s' nem korrekt folyamat" #. module: web #. openerp-web @@ -531,28 +532,28 @@ msgstr "Visszaállítva" #: code:addons/web/static/src/js/view_list.js:392 #, python-format msgid "%d-%d of %d" -msgstr "" +msgstr "%d-%d ebből %d" #. module: web #. openerp-web #: code:addons/web/static/src/js/view_form.js:2841 #, python-format msgid "Create and Edit..." -msgstr "" +msgstr "Létrehoz és szerkeszt..." #. module: web #. openerp-web #: code:addons/web/static/src/js/pyeval.js:731 #, python-format msgid "Unknown nonliteral type " -msgstr "" +msgstr "Ismeretlen nem szó szerinti típus " #. module: web #. openerp-web #: code:addons/web/static/src/js/view_form.js:2345 #, python-format msgid "Resource error" -msgstr "" +msgstr "Forrás hiba" #. module: web #. openerp-web @@ -566,14 +567,14 @@ msgstr "nem egyenlő" #: code:addons/web/static/src/xml/base.xml:544 #, python-format msgid "Print Workflow" -msgstr "" +msgstr "Folyamatlista nyomtatása" #. module: web #. openerp-web #: code:addons/web/static/src/js/chrome.js:396 #, python-format msgid "Please confirm your new password" -msgstr "" +msgstr "Kérem erősítse meg a jelszavát" #. module: web #. openerp-web @@ -587,14 +588,14 @@ msgstr "UTF-8" #: code:addons/web/static/src/xml/base.xml:421 #, python-format msgid "For more information visit" -msgstr "" +msgstr "További információért látogassa meg" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:1834 #, python-format msgid "Add All Info..." -msgstr "" +msgstr "Adja meg az összes információt..." #. module: web #. openerp-web @@ -608,21 +609,21 @@ msgstr "Export formátumok" #: code:addons/web/static/src/xml/base.xml:952 #, python-format msgid "On change:" -msgstr "" +msgstr "Változtatáshoz:" #. module: web #. openerp-web #: code:addons/web/static/src/js/views.js:863 #, python-format msgid "Model %s fields" -msgstr "" +msgstr "Minta %s területek" #. module: web #. openerp-web #: code:addons/web/static/src/js/view_list.js:890 #, python-format msgid "Setting 'id' attribute on existing record %s" -msgstr "" +msgstr "'id' tulajdonság beállítása a meglévő %s rekordon" #. module: web #. openerp-web @@ -659,21 +660,21 @@ msgstr "Szűrő mentése" #: code:addons/web/static/src/xml/base.xml:1319 #, python-format msgid "Action ID:" -msgstr "" +msgstr "Művelet azonosító:" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:453 #, python-format msgid "Your user's preference timezone does not match your browser timezone:" -msgstr "" +msgstr "A felhasználói időzónája nem egyezik a böngésző időzónájával:" #. module: web #. openerp-web #: code:addons/web/static/src/js/view_form.js:1238 #, python-format msgid "Field '%s' specified in view could not be found." -msgstr "" +msgstr "'%s' a nézetben előírt mező nem található." #. module: web #. openerp-web @@ -694,14 +695,14 @@ msgstr "Régi jelszó:" #: code:addons/web/static/src/js/formats.js:113 #, python-format msgid "Bytes,Kb,Mb,Gb,Tb,Pb,Eb,Zb,Yb" -msgstr "" +msgstr "Byte-ok,Kb,Mb,Gb,Tb,Pb,Eb,Zb,Yb" #. module: web #. openerp-web #: code:addons/web/static/src/js/chrome.js:481 #, python-format msgid "The database has been duplicated." -msgstr "" +msgstr "Az adatbázis meg lett kettőzve" #. module: web #. openerp-web @@ -762,6 +763,9 @@ msgid "" "\n" "Are you sure you want to leave this page ?" msgstr "" +"Figyelem, a rekord módosítva lett, a változtatások el lettek utasítva.\n" +"\n" +"Biztos el akarja hagyni ezt az oldalt ?" #. module: web #. openerp-web @@ -775,21 +779,21 @@ msgstr "Keresés: " #: code:addons/web/static/src/xml/base.xml:538 #, python-format msgid "Technical translation" -msgstr "" +msgstr "Technikai fordítás" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:1772 #, python-format msgid "Delimiter:" -msgstr "" +msgstr "Elválasztó:" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:458 #, python-format msgid "Browser's timezone" -msgstr "" +msgstr "Böngésző idő zónája" #. module: web #. openerp-web @@ -820,7 +824,7 @@ msgstr "Hozzáadás" #: code:addons/web/static/src/xml/base.xml:530 #, python-format msgid "Toggle Form Layout Outline" -msgstr "" +msgstr "Kiemelés elrendezési forma átkapcsolás" #. module: web #. openerp-web @@ -834,14 +838,14 @@ msgstr "OpenERP.com" #: code:addons/web/static/src/js/view_form.js:2316 #, python-format msgid "Can't send email to invalid e-mail address" -msgstr "" +msgstr "Nem tud e-mailt küldeni az érvénytelen e-mail-re" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:614 #, python-format msgid "Add..." -msgstr "" +msgstr "Hozzáadás…" #. module: web #. openerp-web @@ -855,7 +859,7 @@ msgstr "Beállítások" #: code:addons/web/static/src/js/view_form.js:434 #, python-format msgid "Wrong on change format: %s" -msgstr "" +msgstr "Formátum megváltoztatása hiba: %s" #. module: web #. openerp-web @@ -870,14 +874,14 @@ msgstr "Adatbázis eldobása" #: code:addons/web/static/src/xml/base.xml:462 #, python-format msgid "Click here to change your user's timezone." -msgstr "" +msgstr "Kattintson ide a felhasználói időzóna megváltoztatásához" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:944 #, python-format msgid "Modifiers:" -msgstr "" +msgstr "Módosítók:" #. module: web #. openerp-web @@ -916,21 +920,21 @@ msgstr "Felhasználónév" #: code:addons/web/static/src/js/chrome.js:481 #, python-format msgid "Duplicating database" -msgstr "" +msgstr "Adatbázis megkettőzése" #. module: web #. openerp-web #: code:addons/web/static/src/js/chrome.js:563 #, python-format msgid "Password has been changed successfully" -msgstr "" +msgstr "A jelsző megváltoztatása sikeresen végrehajtva" #. module: web #. openerp-web #: code:addons/web/static/src/js/view_list_editable.js:781 #, python-format msgid "The form's data can not be discarded" -msgstr "" +msgstr "A forma adatait nem lehet változtatni" #. module: web #. openerp-web @@ -965,6 +969,10 @@ msgid "" "\n" "%s" msgstr "" +"Helyi értékelési hiba\n" +"%s\n" +"\n" +"%s" #. module: web #. openerp-web @@ -992,14 +1000,14 @@ msgstr "Logok nézete (%s)" #: code:addons/web/static/src/xml/base.xml:558 #, python-format msgid "Creation Date:" -msgstr "" +msgstr "Létrehozás dátuma:" #. module: web #: code:addons/web/controllers/main.py:806 #: code:addons/web/controllers/main.py:851 #, python-format msgid "Error, password not changed !" -msgstr "" +msgstr "Hiba, a jelszó nem változott meg!" #. module: web #. openerp-web @@ -1014,14 +1022,14 @@ msgstr "A kiválasztott fájl meghaladja a maximális %s fájlméretet." #: code:addons/web/static/src/xml/base.xml:1275 #, python-format msgid "/web/binary/upload_attachment" -msgstr "" +msgstr "/web/binary/upload_attachment" #. module: web #. openerp-web #: code:addons/web/static/src/js/chrome.js:563 #, python-format msgid "Changed Password" -msgstr "" +msgstr "Megváltoztatott jelszó" #. module: web #. openerp-web @@ -1039,7 +1047,7 @@ msgstr "Keresés" #: code:addons/web/static/src/js/view_form.js:4313 #, python-format msgid "Open: " -msgstr "" +msgstr "Megnyitás: " #. module: web #. openerp-web @@ -1055,21 +1063,21 @@ msgstr "Biztonsági mentés" #: code:addons/web/static/src/js/dates.js:80 #, python-format msgid "'%s' is not a valid time" -msgstr "" +msgstr "'%s' nem érvényes itő formátum" #. module: web #. openerp-web #: code:addons/web/static/src/js/formats.js:274 #, python-format msgid "'%s' is not a correct date" -msgstr "" +msgstr "'%s' nem érvényes dátum" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:911 #, python-format msgid "(nolabel)" -msgstr "" +msgstr "(cimke nélkül)" #. module: web #. openerp-web @@ -1083,7 +1091,7 @@ msgstr "%d nappal ezelőtt" #: code:addons/web/static/src/xml/base.xml:1449 #, python-format msgid "(Any existing filter with the same name will be replaced)" -msgstr "" +msgstr "(Ugynazzal a névvel futó szűrő ki lesz cserélve)" #. module: web #. openerp-web @@ -1108,7 +1116,7 @@ msgstr "Betöltés…" #: code:addons/web/static/src/xml/base.xml:561 #, python-format msgid "Latest Modification by:" -msgstr "" +msgstr "Utoljára módosító személy:" #. module: web #. openerp-web @@ -1116,14 +1124,14 @@ msgstr "" #: code:addons/web/static/src/xml/base.xml:466 #, python-format msgid "Timezone mismatch" -msgstr "" +msgstr "Időzóna nem megfelelő" #. module: web #. openerp-web #: code:addons/web/static/src/js/view_form.js:1661 #, python-format msgid "Unknown operator %s in domain %s" -msgstr "" +msgstr "Nem ismert operátor %s ebben a domainben %s" #. module: web #. openerp-web @@ -1137,7 +1145,7 @@ msgstr "%d / %d" #: code:addons/web/static/src/xml/base.xml:1757 #, python-format msgid "2. Check your file format" -msgstr "" +msgstr "2. Ellenőrizze a fájlformátumot" #. module: web #. openerp-web @@ -1154,6 +1162,10 @@ msgid "" "Select a .CSV file to import. If you need a sample of file to import,\n" " you should use the export tool with the \"Import Compatible\" option." msgstr "" +"Válassz egy .CSV fájlt az importáláshoz. Ha egy példa fájlt akar " +"importálni,\n" +" akkor egy exportálási eszközt használjon a \"kompatibilis import\" " +"kiválasztással együtt." #. module: web #. openerp-web @@ -1168,7 +1180,7 @@ msgstr "%d hónapja" #: code:addons/web/static/src/xml/base.xml:308 #, python-format msgid "Drop" -msgstr "" +msgstr "Eldobás" #. module: web #. openerp-web @@ -1181,7 +1193,7 @@ msgstr "Összetett szűrés hozzáadása" #: code:addons/web/controllers/main.py:844 #, python-format msgid "The new password and its confirmation must be identical." -msgstr "" +msgstr "Az új jelszónak és a jelszó megerősítésnek azonosnak kell lennie" #. module: web #. openerp-web @@ -1196,14 +1208,14 @@ msgstr "Adatbázis Visszaállítása" #: code:addons/web/static/src/js/chrome.js:645 #, python-format msgid "Login" -msgstr "" +msgstr "Bejelentkezés" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:420 #, python-format msgid "Licenced under the terms of" -msgstr "" +msgstr "Engedély ezen feltételek alapján" #. module: web #. openerp-web @@ -1239,7 +1251,7 @@ msgstr "Csoportosítás: %s" #: code:addons/web/static/src/js/view_form.js:151 #, python-format msgid "No data provided." -msgstr "" +msgstr "Nincs adat szolgáltatva." #. module: web #. openerp-web @@ -1282,7 +1294,7 @@ msgstr "Helytelen keresés" #: code:addons/web/static/src/js/view_list.js:959 #, python-format msgid "Could not find id in dataset" -msgstr "" +msgstr "Nem található azonosító az adatban" #. module: web #. openerp-web @@ -1310,7 +1322,7 @@ msgstr "%(page)d/%(page_count)d" #: code:addons/web/static/src/js/chrome.js:397 #, python-format msgid "The confirmation does not match the password" -msgstr "" +msgstr "A megerősítés nem egyezik a jelszóval" #. module: web #. openerp-web @@ -1324,7 +1336,7 @@ msgstr "Mentés másként..." #: code:addons/web/static/src/js/view_form.js:4971 #, python-format msgid "Could not display the selected image." -msgstr "" +msgstr "A kiválasztott ábra nem megjeleníthető." #. module: web #. openerp-web @@ -1341,13 +1353,15 @@ msgid "" "For use if CSV files have titles on multiple lines, skips more than a single " "line during import" msgstr "" +"CSV fájl használatakor ha többsoros címet adott meg, importálás alatt " +"kihagyja a többi sort" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:393 #, python-format msgid "99+" -msgstr "" +msgstr "99+" #. module: web #. openerp-web @@ -1361,7 +1375,7 @@ msgstr ".CSV fájl importálása" #: code:addons/web/static/src/js/chrome.js:645 #, python-format msgid "No database selected !" -msgstr "" +msgstr "Nincs adatbázis kiválasztva !" #. module: web #. openerp-web @@ -1399,7 +1413,7 @@ msgstr "egyenlő" #: code:addons/web/static/src/js/views.js:1455 #, python-format msgid "Could not serialize XML" -msgstr "" +msgstr "Nem tudja sorbarendezni az XML" #. module: web #. openerp-web @@ -1413,14 +1427,14 @@ msgstr "Részletes Keresés" #: code:addons/web/static/src/xml/base.xml:290 #, python-format msgid "Confirm new master password:" -msgstr "" +msgstr "Erősítse meg a mester jelszavat:" #. module: web #. openerp-web #: code:addons/web/static/src/js/coresetup.js:625 #, python-format msgid "Maybe you should consider reloading the application by pressing F5..." -msgstr "" +msgstr "Talán töltse újra az alkalmazást az F5 megnyomásával..." #. module: web #. openerp-web @@ -1452,7 +1466,7 @@ msgstr "Importálási beállítások" #: code:addons/web/static/src/js/view_form.js:2909 #, python-format msgid "Add %s" -msgstr "" +msgstr "%s hozzáadása" #. module: web #. openerp-web @@ -1476,7 +1490,7 @@ msgstr "Bezárás" #, python-format msgid "" "You may not believe it,
but the application is actually loading..." -msgstr "" +msgstr "Nem fogja elhinni,
de az alkalmazás most töltődik..." #. module: web #. openerp-web @@ -1490,7 +1504,7 @@ msgstr "CSV Fájl:" #: code:addons/web/static/src/js/search.js:1743 #, python-format msgid "Advanced" -msgstr "" +msgstr "Speciális" #. module: web #. openerp-web @@ -1503,14 +1517,14 @@ msgstr "Fa" #: code:addons/web/controllers/main.py:766 #, python-format msgid "Could not drop database !" -msgstr "" +msgstr "Nem tudja törölni az adatbázis !" #. module: web #. openerp-web #: code:addons/web/static/src/js/formats.js:227 #, python-format msgid "'%s' is not a correct integer" -msgstr "" +msgstr "'%s' nem korrekt egész szám" #. module: web #. openerp-web @@ -1524,14 +1538,14 @@ msgstr "Minden felhasználó" #: code:addons/web/static/src/js/view_form.js:1669 #, python-format msgid "Unknown field %s in domain %s" -msgstr "" +msgstr "Nem ismert mező %s a domainben %s" #. module: web #. openerp-web #: code:addons/web/static/src/js/views.js:1421 #, python-format msgid "Node [%s] is not a JSONified XML node" -msgstr "" +msgstr "a csomópont [%s] nem egy JSONified XML csomópont" #. module: web #. openerp-web @@ -1545,7 +1559,7 @@ msgstr "Részletes keresés…" #: code:addons/web/static/src/js/chrome.js:499 #, python-format msgid "Dropping database" -msgstr "" +msgstr "Adatbázis törölve" #. module: web #. openerp-web @@ -1553,7 +1567,7 @@ msgstr "" #: code:addons/web/static/src/xml/base.xml:441 #, python-format msgid "Powered by" -msgstr "" +msgstr "Támogatja" #. module: web #. openerp-web @@ -1568,14 +1582,14 @@ msgstr "Igen" #: code:addons/web/static/src/js/view_form.js:4831 #, python-format msgid "There was a problem while uploading your file" -msgstr "" +msgstr "Probléma merült fel a fájl feltöltése közben" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:552 #, python-format msgid "XML ID:" -msgstr "" +msgstr "XML azonosító:" #. module: web #. openerp-web @@ -1610,7 +1624,7 @@ msgstr "%d éve" #: code:addons/web/static/src/js/view_list.js:1019 #, python-format msgid "Unknown m2m command %s" -msgstr "" +msgstr "Ismeretlen m2m parancs %s" #. module: web #. openerp-web @@ -1633,14 +1647,14 @@ msgstr "Új adatbázis neve:" #: code:addons/web/static/src/js/chrome.js:394 #, python-format msgid "Please enter your new password" -msgstr "" +msgstr "Kérem adja meg az új jelszavát" #. module: web #. openerp-web #: code:addons/web/static/src/js/views.js:1463 #, python-format msgid "Could not parse string to xml" -msgstr "" +msgstr "Nem tudja elemezni a sort az XML-hez" #. module: web #. openerp-web @@ -1654,42 +1668,42 @@ msgstr "Nézetek kezelése" #: code:addons/web/static/src/xml/base.xml:1776 #, python-format msgid "Encoding:" -msgstr "" +msgstr "Kódolás:" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:1783 #, python-format msgid "Lines to skip" -msgstr "" +msgstr "Sorok kihagyjása" #. module: web #. openerp-web #: code:addons/web/static/src/js/view_form.js:2831 #, python-format msgid "Create \"%s\"" -msgstr "" +msgstr "Létrehoz \"%s\"" #. module: web #. openerp-web #: code:addons/web/static/src/js/data_export.js:361 #, python-format msgid "Please select fields to save export list..." -msgstr "" +msgstr "Kérem válasszon mezőket az export lista mentéséhez..." #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:418 #, python-format msgid "Copyright © 2004-TODAY OpenERP SA. All Rights Reserved." -msgstr "" +msgstr "Copyright © 2004-TODAY OpenERP SA. Minden jog fenntartva." #. module: web #. openerp-web #: code:addons/web/static/src/js/view_form.js:2345 #, python-format msgid "This resource is empty" -msgstr "" +msgstr "Ez a forrás üres" #. module: web #. openerp-web @@ -1703,7 +1717,7 @@ msgstr "Elérhető mezők" #: code:addons/web/static/src/xml/base.xml:1810 #, python-format msgid "The import failed due to:" -msgstr "" +msgstr "Az importálás megállt mert:" #. module: web #. openerp-web @@ -1711,7 +1725,7 @@ msgstr "" #: code:addons/web/static/src/xml/base.xml:533 #, python-format msgid "JS Tests" -msgstr "" +msgstr "JS Teszt" #. module: web #. openerp-web @@ -1725,7 +1739,7 @@ msgstr "Mentés mint:" #: code:addons/web/static/src/js/search.js:929 #, python-format msgid "Filter on: %s" -msgstr "" +msgstr "Szűrő ezen: %s" #. module: web #. openerp-web @@ -1740,14 +1754,14 @@ msgstr "Létrehozás: " #: code:addons/web/static/src/xml/base.xml:534 #, python-format msgid "View Fields" -msgstr "" +msgstr "Mezők nézete" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:330 #, python-format msgid "Confirm New Password:" -msgstr "" +msgstr "Új jelszó megerősítése:" #. module: web #. openerp-web @@ -1761,7 +1775,7 @@ msgstr "Biztos, hogy törölni akarja ezeket a rekordokat?" #: code:addons/web/static/src/xml/base.xml:936 #, python-format msgid "Context:" -msgstr "" +msgstr "Összefüggés:" #. module: web #. openerp-web @@ -1836,7 +1850,7 @@ msgstr "Névjegy" #: code:addons/web/static/src/xml/base.xml:1406 #, python-format msgid "Search Again" -msgstr "" +msgstr "Keresés ismét" #. module: web #. openerp-web @@ -1868,13 +1882,15 @@ msgid "" "Grouping on field '%s' is not possible because that field does not appear in " "the list view." msgstr "" +"Csoportosítás a '%s' mezön nem lehetséges mert ez a mező nem látható a " +"lista nézetben." #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:531 #, python-format msgid "Set Defaults" -msgstr "" +msgstr "Alapértelmezések beállítása" #. module: web #. openerp-web @@ -1896,7 +1912,7 @@ msgstr "" #: code:addons/web/static/src/js/view_form.js:319 #, python-format msgid "The record could not be found in the database." -msgstr "" +msgstr "A rekord nem található az adatbázisban" #. module: web #. openerp-web @@ -1910,21 +1926,21 @@ msgstr "Szűrő neve:" #: code:addons/web/static/src/xml/base.xml:924 #, python-format msgid "Type:" -msgstr "" +msgstr "Típus:" #. module: web #. openerp-web #: code:addons/web/static/src/js/chrome.js:538 #, python-format msgid "Incorrect super-administrator password" -msgstr "" +msgstr "Nem érvényes szuper-adminisztrátor jelszó" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:920 #, python-format msgid "Object:" -msgstr "" +msgstr "Tárgy:" #. module: web #. openerp-web @@ -1959,6 +1975,8 @@ msgid "" "The type of the field '%s' must be a many2many field with a relation to " "'ir.attachment' model." msgstr "" +"A '%s' mező típusnak many2many mezőnek kell lennie az 'ir.attachment' " +"modellhez való kapcsolattal." #. module: web #. openerp-web @@ -1973,21 +1991,21 @@ msgstr "%d órával ezelőtt" #: code:addons/web/static/src/js/view_form.js:4295 #, python-format msgid "Add: " -msgstr "" +msgstr "Hozzátesz: " #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:1833 #, python-format msgid "Quick Add" -msgstr "" +msgstr "Gyors hozzáadás" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:1780 #, python-format msgid "Latin 1" -msgstr "" +msgstr "Latin 1" #. module: web #. openerp-web @@ -2023,14 +2041,14 @@ msgstr "Bemutató adatok betöltése:" #: code:addons/web/static/src/js/dates.js:26 #, python-format msgid "'%s' is not a valid datetime" -msgstr "" +msgstr "'%s' nem érvényes dátum idő" #. module: web #. openerp-web #: code:addons/web/static/src/js/views.js:1471 #, python-format msgid "Could not find a DOM Parser: %s" -msgstr "" +msgstr "Nem található egy DOM elemző: %s" #. module: web #. openerp-web @@ -2075,7 +2093,7 @@ msgstr "igaz" #: code:addons/web/static/src/js/view_form.js:3880 #, python-format msgid "Add an item" -msgstr "" +msgstr "Elem hozzáadása" #. module: web #. openerp-web @@ -2096,7 +2114,7 @@ msgstr "Jóváhagyás" #: code:addons/web/static/src/js/data_export.js:126 #, python-format msgid "Please enter save field list name" -msgstr "" +msgstr "Kérem megadni a mentés mező lista nevét" #. module: web #. openerp-web @@ -2110,14 +2128,14 @@ msgstr "\"%s\" letöltése" #: code:addons/web/static/src/js/view_form.js:324 #, python-format msgid "New" -msgstr "" +msgstr "Új" #. module: web #. openerp-web #: code:addons/web/static/src/js/view_list.js:1746 #, python-format msgid "Can't convert value %s to context" -msgstr "" +msgstr "Nem lehet az %s értéket összefűzni" #. module: web #. openerp-web @@ -2125,7 +2143,7 @@ msgstr "" #: code:addons/web/static/src/xml/base.xml:535 #, python-format msgid "Fields View Get" -msgstr "" +msgstr "Bevétel mezők nézete" #. module: web #. openerp-web @@ -2155,7 +2173,7 @@ msgstr "Gomb" #: code:addons/web/static/src/xml/base.xml:418 #, python-format msgid "OpenERP is a trademark of the" -msgstr "" +msgstr "Az OpenERP ennek a védjegye" #. module: web #. openerp-web @@ -2190,13 +2208,13 @@ msgstr "OpenERP - Névjegy" #: code:addons/web/static/src/js/formats.js:297 #, python-format msgid "'%s' is not a correct date, datetime nor time" -msgstr "" +msgstr "'%s' nem érvényes dátum, dátum idő és nem idő" #. module: web #: code:addons/web/controllers/main.py:1306 #, python-format msgid "No content found for field '%s' on '%s:%s'" -msgstr "" +msgstr "Nincs értéke a mezőknek '%s' ezen '%s:%s'" #. module: web #. openerp-web @@ -2210,7 +2228,7 @@ msgstr "Adatbázis Kezelő" #: code:addons/web/static/src/js/view_form.js:4971 #, python-format msgid "Image" -msgstr "" +msgstr "Kép" #. module: web #. openerp-web @@ -2224,14 +2242,14 @@ msgstr "Adatbázisok kezelése" #: code:addons/web/static/src/js/pyeval.js:765 #, python-format msgid "Evaluation Error" -msgstr "" +msgstr "Értékelési hiba" #. module: web #. openerp-web #: code:addons/web/static/src/js/search.js:1285 #, python-format msgid "not a valid integer" -msgstr "" +msgstr "Nem létező egyész szám" #. module: web #. openerp-web @@ -2257,7 +2275,7 @@ msgstr "Nem" #: code:addons/web/static/src/js/formats.js:309 #, python-format msgid "'%s' is not convertible to date, datetime nor time" -msgstr "" +msgstr "'%s' nem átalakítható dátummá, dátum idővé vagy idővé" #. module: web #. openerp-web @@ -2282,7 +2300,7 @@ msgstr "Elvetés" #: code:addons/web/static/src/xml/base.xml:1599 #, python-format msgid "Add a condition" -msgstr "" +msgstr "Feltétel hozzáadása" #. module: web #. openerp-web @@ -2297,48 +2315,49 @@ msgstr "Még tölt..." #, python-format msgid "Incorrect value for field %(fieldname)s: [%(value)s] is %(message)s" msgstr "" +"Nemérévnyesíthető érték a mezőhöz %(fieldname)s: [%(value)s] az %(message)s" #. module: web #. openerp-web #: code:addons/web/static/src/js/view_form.js:3776 #, python-format msgid "The o2m record must be saved before an action can be used" -msgstr "" +msgstr "Az o2m rekordot menteni kell mielőtt cselekvéshez használná" #. module: web #. openerp-web #: code:addons/web/static/src/js/chrome.js:509 #, python-format msgid "Backed" -msgstr "" +msgstr "Mentéshez másol" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:1585 #, python-format msgid "Use by default" -msgstr "" +msgstr "Alapértelmezésben használt" #. module: web #. openerp-web #: code:addons/web/static/src/js/view_form.js:4846 #, python-format msgid "The field is empty, there's nothing to save !" -msgstr "" +msgstr "A mező üres, nincs mit menteni !" #. module: web #. openerp-web #: code:addons/web/static/src/js/view_list.js:1327 #, python-format msgid "%s (%d)" -msgstr "" +msgstr "%s (%d)" #. module: web #. openerp-web #: code:addons/web/static/src/js/search.js:777 #, python-format msgid "triggered from search view" -msgstr "" +msgstr "a keresés nézetből elindított" #. module: web #. openerp-web @@ -2352,14 +2371,14 @@ msgstr "Szűrő" #: code:addons/web/static/src/xml/base.xml:928 #, python-format msgid "Widget:" -msgstr "" +msgstr "Grafikus vezérlő elem:" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:542 #, python-format msgid "Edit Action" -msgstr "" +msgstr "Művelet szerkesztése" #. module: web #. openerp-web @@ -2380,7 +2399,7 @@ msgstr "csak saját" #: code:addons/web/static/src/xml/base.xml:543 #, python-format msgid "Edit Workflow" -msgstr "" +msgstr "Folyamat szerkesztése" #. module: web #. openerp-web @@ -2394,7 +2413,7 @@ msgstr "Valóban törölni szeretné ezt a mellékletet?" #: code:addons/web/static/src/js/views.js:838 #, python-format msgid "Technical Translation" -msgstr "" +msgstr "Műszaki fordítás" #. module: web #. openerp-web @@ -2408,14 +2427,14 @@ msgstr "Mező:" #: code:addons/web/static/src/js/chrome.js:499 #, python-format msgid "The database %s has been dropped" -msgstr "" +msgstr "Az %s adatbázis törölve lett" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:456 #, python-format msgid "User's timezone" -msgstr "" +msgstr "Felhasználói időzóna" #. module: web #. openerp-web @@ -2437,7 +2456,7 @@ msgstr "Nyomtatás" #: code:addons/web/static/src/xml/base.xml:1306 #, python-format msgid "Special:" -msgstr "" +msgstr "Speciális:" #. module: web #: code:addons/web/controllers/main.py:850 @@ -2445,13 +2464,14 @@ msgstr "" msgid "" "The old password you provided is incorrect, your password was not changed." msgstr "" +"A régi jelszó amit beírt nem megfelelő, a jelszava nem változott meg." #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:555 #, python-format msgid "Creation User:" -msgstr "" +msgstr "Felhasználó létrehozás:" #. module: web #. openerp-web @@ -2472,7 +2492,7 @@ msgstr "Mentés & Bezárás" #: code:addons/web/static/src/js/view_form.js:2818 #, python-format msgid "Search More..." -msgstr "" +msgstr "Még több keresése..." #. module: web #. openerp-web @@ -2510,7 +2530,7 @@ msgstr "Dátum választása" #: code:addons/web/static/src/js/search.js:1251 #, python-format msgid "Search %(field)s for: %(value)s" -msgstr "" +msgstr "Keresés %(field)s erre: %(value)s" #. module: web #. openerp-web @@ -2531,7 +2551,7 @@ msgstr "Adatbázis létrehozása" #: code:addons/web/static/src/xml/base.xml:420 #, python-format msgid "GNU Affero General Public License" -msgstr "" +msgstr "GNU Affero General Public License" #. module: web #. openerp-web @@ -2545,7 +2565,7 @@ msgstr "Elválasztó:" #: code:addons/web/static/src/xml/base.xml:300 #, python-format msgid "Back to Login" -msgstr "" +msgstr "Vissza a bejelentkezéshez" #. module: web #. openerp-web diff --git a/addons/web/i18n/mn.po b/addons/web/i18n/mn.po index d012a7a1e91..51ef3305fe1 100644 --- a/addons/web/i18n/mn.po +++ b/addons/web/i18n/mn.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openerp-web\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2012-11-30 18:13+0000\n" -"PO-Revision-Date: 2012-11-26 09:06+0000\n" +"PO-Revision-Date: 2012-12-10 08:54+0000\n" "Last-Translator: gobi \n" "Language-Team: Mongolian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-01 05:20+0000\n" -"X-Generator: Launchpad (build 16319)\n" +"X-Launchpad-Export-Date: 2012-12-11 05:03+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: web #. openerp-web @@ -59,7 +59,7 @@ msgstr "бага буюу тэнцүү" #: code:addons/web/static/src/js/chrome.js:393 #, python-format msgid "Please enter your previous password" -msgstr "" +msgstr "Өмнө нууц үгээ оруулна уу" #. module: web #. openerp-web @@ -82,7 +82,7 @@ msgstr "Мастер Нууц Үг Солих" #: code:addons/web/static/src/js/chrome.js:491 #, python-format msgid "Do you really want to delete the database: %s ?" -msgstr "" +msgstr "Та үнэхээр энэн өгөгдлийн баазыг устгамаар байна уу: %s ?" #. module: web #. openerp-web @@ -96,7 +96,7 @@ msgstr "%(field)s талбарыг: %(value)s-д хайх" #: code:addons/web/static/src/js/chrome.js:537 #, python-format msgid "Access Denied" -msgstr "" +msgstr "Хандалтыг татгалзав" #. module: web #. openerp-web @@ -120,7 +120,7 @@ msgstr "Өгөгдлийн Бааз Нөөцлөх" #: code:addons/web/static/src/js/dates.js:53 #, python-format msgid "'%s' is not a valid date" -msgstr "" +msgstr "'%s' нь огноо биш байна" #. module: web #. openerp-web @@ -147,7 +147,7 @@ msgstr "Файл" #: code:addons/web/controllers/main.py:842 #, python-format msgid "You cannot leave any password empty." -msgstr "" +msgstr "Ямарваа нууц үгийг хоосон үлдээж болохгүй" #. module: web #. openerp-web @@ -208,7 +208,7 @@ msgstr "" #: code:addons/web/static/src/js/view_form.js:1242 #, python-format msgid "Widget type '%s' is not implemented" -msgstr "" +msgstr "'%s' төрөлийн виджет хийгдээгүй" #. module: web #. openerp-web @@ -237,7 +237,7 @@ msgstr "(тэмдэгт мөр үгүй)" #: code:addons/web/static/src/js/formats.js:282 #, python-format msgid "'%s' is not a correct time" -msgstr "" +msgstr "'%s' нь цаг биш байна" #. module: web #. openerp-web @@ -336,7 +336,7 @@ msgstr "Нууц үг солих" #: code:addons/web/static/src/js/view_form.js:3382 #, python-format msgid "View type '%s' is not supported in One2Many." -msgstr "" +msgstr "'%s' төрөлийн харагдац нь Нэг нь Олонтой дотор дэмжигдэхгүй" #. module: web #. openerp-web @@ -351,7 +351,7 @@ msgstr "Татах" #: code:addons/web/static/src/js/formats.js:266 #, python-format msgid "'%s' is not a correct datetime" -msgstr "" +msgstr "'%s' нь огноо, цаг биш" #. module: web #. openerp-web @@ -379,13 +379,13 @@ msgstr "Сонголт:" #: code:addons/web/static/src/js/view_form.js:869 #, python-format msgid "The following fields are invalid:" -msgstr "" +msgstr "Дараах талбарууд буруу:" #. module: web #: code:addons/web/controllers/main.py:863 #, python-format msgid "Languages" -msgstr "" +msgstr "Хэлүүд" #. module: web #. openerp-web diff --git a/addons/web/i18n/pl.po b/addons/web/i18n/pl.po index 5f5e26e60f7..00b950ac364 100644 --- a/addons/web/i18n/pl.po +++ b/addons/web/i18n/pl.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openerp-web\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2012-11-30 18:13+0000\n" -"PO-Revision-Date: 2012-12-09 20:29+0000\n" -"Last-Translator: Rafał Perczyński \n" +"PO-Revision-Date: 2012-12-10 13:05+0000\n" +"Last-Translator: Grzegorz Grzelak (OpenGLOBE.pl) \n" "Language-Team: Polish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-10 04:44+0000\n" -"X-Generator: Launchpad (build 16341)\n" +"X-Launchpad-Export-Date: 2012-12-11 05:03+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: web #. openerp-web @@ -89,7 +89,7 @@ msgstr "Jesteś pewny że chcesz usunąć bazę danych: %s ?" #: code:addons/web/static/src/js/search.js:1400 #, python-format msgid "Search %(field)s at: %(value)s" -msgstr "" +msgstr "Szukanie %(field)s z: %(value)s" #. module: web #. openerp-web @@ -200,20 +200,21 @@ msgstr "Data ostatniej modyfikacji:" #, python-format msgid "M2O search fields do not currently handle multiple default values" msgstr "" +"Wyszukiwanie pól M2O nie obsługuje obecnie wielokrotnych wartości domyślnych." #. module: web #. openerp-web #: code:addons/web/static/src/js/view_form.js:1242 #, python-format msgid "Widget type '%s' is not implemented" -msgstr "" +msgstr "Kontrolka typu '%s' jest niezaimplementowana" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:1583 #, python-format msgid "Share with all users" -msgstr "" +msgstr "Współdziel z wszystkimi użytkownikami" #. module: web #. openerp-web @@ -334,7 +335,7 @@ msgstr "Zmień hasło" #: code:addons/web/static/src/js/view_form.js:3382 #, python-format msgid "View type '%s' is not supported in One2Many." -msgstr "" +msgstr "Typ widoku '%s' jest nieobsługiwany w One2Many." #. module: web #. openerp-web @@ -377,7 +378,7 @@ msgstr "Wybór:" #: code:addons/web/static/src/js/view_form.js:869 #, python-format msgid "The following fields are invalid:" -msgstr "" +msgstr "Nastęþujące pola sa niepoprawne:" #. module: web #: code:addons/web/controllers/main.py:863 @@ -390,7 +391,7 @@ msgstr "Języki" #: code:addons/web/static/src/xml/base.xml:1245 #, python-format msgid "...Upload in progress..." -msgstr "" +msgstr "...Wysyłanie..." #. module: web #. openerp-web @@ -404,7 +405,7 @@ msgstr "Importuj" #: code:addons/web/static/src/js/chrome.js:543 #, python-format msgid "Could not restore the database" -msgstr "" +msgstr "Nie można odtworzyć bazy danych" #. module: web #. openerp-web @@ -418,7 +419,7 @@ msgstr "Wysyłanie pliku" #: code:addons/web/static/src/js/view_form.js:3775 #, python-format msgid "Action Button" -msgstr "" +msgstr "Przycisk akcji" #. module: web #. openerp-web @@ -440,14 +441,14 @@ msgstr "zawiera" #: code:addons/web/static/src/js/coresetup.js:624 #, python-format msgid "Take a minute to get a coffee,
because it's loading..." -msgstr "" +msgstr "Przerwa na kawę,
ładowanie potrwa..." #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:413 #, python-format msgid "Activate the developer mode" -msgstr "" +msgstr "Aktywuj tryb deweloperski" #. module: web #. openerp-web @@ -461,14 +462,14 @@ msgstr "Pobieram (%d)" #: code:addons/web/static/src/js/search.js:1116 #, python-format msgid "GroupBy" -msgstr "" +msgstr "Grupuj wg" #. module: web #. openerp-web #: code:addons/web/static/src/js/view_list.js:684 #, python-format msgid "You must select at least one record." -msgstr "" +msgstr "Musisz wybrać co najmniej jeden rekord." #. module: web #. openerp-web @@ -496,7 +497,7 @@ msgstr "Powiązania:" #: code:addons/web/static/src/js/coresetup.js:591 #, python-format msgid "less than a minute ago" -msgstr "" +msgstr "mniej niż minutę temu" #. module: web #. openerp-web @@ -517,7 +518,7 @@ msgstr "Nieobsługiwany operator %s w domenie %s" #: code:addons/web/static/src/js/formats.js:242 #, python-format msgid "'%s' is not a correct float" -msgstr "" +msgstr "'%s' nie jest poprawną liczbą zmiennoprzecinkową (float)" #. module: web #. openerp-web @@ -538,21 +539,21 @@ msgstr "" #: code:addons/web/static/src/js/view_form.js:2841 #, python-format msgid "Create and Edit..." -msgstr "" +msgstr "Utwórz i Edytuj..." #. module: web #. openerp-web #: code:addons/web/static/src/js/pyeval.js:731 #, python-format msgid "Unknown nonliteral type " -msgstr "" +msgstr "Nieznany typ nonliteral " #. module: web #. openerp-web #: code:addons/web/static/src/js/view_form.js:2345 #, python-format msgid "Resource error" -msgstr "" +msgstr "Błąd zasobów" #. module: web #. openerp-web @@ -566,14 +567,14 @@ msgstr "nie jest" #: code:addons/web/static/src/xml/base.xml:544 #, python-format msgid "Print Workflow" -msgstr "" +msgstr "Drukuj obieg" #. module: web #. openerp-web #: code:addons/web/static/src/js/chrome.js:396 #, python-format msgid "Please confirm your new password" -msgstr "" +msgstr "Potwierdź nowe hasło" #. module: web #. openerp-web @@ -587,14 +588,14 @@ msgstr "" #: code:addons/web/static/src/xml/base.xml:421 #, python-format msgid "For more information visit" -msgstr "" +msgstr "Więcej informacji na" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:1834 #, python-format msgid "Add All Info..." -msgstr "" +msgstr "Dodaj info..." #. module: web #. openerp-web @@ -615,14 +616,14 @@ msgstr "Przy zmianie:" #: code:addons/web/static/src/js/views.js:863 #, python-format msgid "Model %s fields" -msgstr "" +msgstr "Pola modelu %s" #. module: web #. openerp-web #: code:addons/web/static/src/js/view_list.js:890 #, python-format msgid "Setting 'id' attribute on existing record %s" -msgstr "" +msgstr "Ustawianie atrybutu 'id' na istniejącym rekordzie %s" #. module: web #. openerp-web @@ -667,13 +668,14 @@ msgstr "ID akcji:" #, python-format msgid "Your user's preference timezone does not match your browser timezone:" msgstr "" +"Twoja preferencyjna strefa czasowa nie odpowiada strefie przeglądarki:" #. module: web #. openerp-web #: code:addons/web/static/src/js/view_form.js:1238 #, python-format msgid "Field '%s' specified in view could not be found." -msgstr "" +msgstr "Pole '%s' podane w widoku nie może być odnalezione." #. module: web #. openerp-web @@ -701,14 +703,14 @@ msgstr "" #: code:addons/web/static/src/js/chrome.js:481 #, python-format msgid "The database has been duplicated." -msgstr "" +msgstr "Baza została zduplikowana" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:1600 #, python-format msgid "Apply" -msgstr "" +msgstr "Zastosuj" #. module: web #. openerp-web @@ -730,14 +732,14 @@ msgstr "Zapisz jako" #: code:addons/web/static/src/js/view_form.js:2316 #, python-format msgid "E-mail error" -msgstr "" +msgstr "Błąd email" #. module: web #. openerp-web #: code:addons/web/static/src/js/coresetup.js:596 #, python-format msgid "a day ago" -msgstr "" +msgstr "dzień temu" #. module: web #. openerp-web @@ -762,6 +764,9 @@ msgid "" "\n" "Are you sure you want to leave this page ?" msgstr "" +"Uwaga rekord został zmodyfikowany. Twoje zmiany zostaną utracone.\n" +"\n" +"Na pewno chcesz opuścić tę stronę ?" #. module: web #. openerp-web @@ -775,7 +780,7 @@ msgstr "Szukaj: " #: code:addons/web/static/src/xml/base.xml:538 #, python-format msgid "Technical translation" -msgstr "" +msgstr "Tłumaczenie techniczne" #. module: web #. openerp-web @@ -789,21 +794,21 @@ msgstr "Znak dziesiętny:" #: code:addons/web/static/src/xml/base.xml:458 #, python-format msgid "Browser's timezone" -msgstr "" +msgstr "Strefa czasowa przeglądarki" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:1580 #, python-format msgid "Filter name" -msgstr "" +msgstr "Nazwa filtru" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:1439 #, python-format msgid "-- Actions --" -msgstr "" +msgstr "-- Akcje --" #. module: web #. openerp-web @@ -820,7 +825,7 @@ msgstr "Dodaj" #: code:addons/web/static/src/xml/base.xml:530 #, python-format msgid "Toggle Form Layout Outline" -msgstr "" +msgstr "Przełącz układ formularza" #. module: web #. openerp-web @@ -834,14 +839,14 @@ msgstr "" #: code:addons/web/static/src/js/view_form.js:2316 #, python-format msgid "Can't send email to invalid e-mail address" -msgstr "" +msgstr "Nie można wysłac maila do niepoprawnego adresu" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:614 #, python-format msgid "Add..." -msgstr "" +msgstr "Dodaj..." #. module: web #. openerp-web @@ -855,7 +860,7 @@ msgstr "Preferencje" #: code:addons/web/static/src/js/view_form.js:434 #, python-format msgid "Wrong on change format: %s" -msgstr "" +msgstr "Błędny format 'on change': %s" #. module: web #. openerp-web @@ -863,14 +868,14 @@ msgstr "" #: code:addons/web/static/src/xml/base.xml:185 #, python-format msgid "Drop Database" -msgstr "" +msgstr "Usuń bazę danych" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:462 #, python-format msgid "Click here to change your user's timezone." -msgstr "" +msgstr "Kliknij, aby zmienić strefę czasową użytkownika" #. module: web #. openerp-web @@ -884,7 +889,7 @@ msgstr "Modyfikatorzy:" #: code:addons/web/static/src/xml/base.xml:605 #, python-format msgid "Delete this attachment" -msgstr "" +msgstr "Usuń ten załącznik" #. module: web #. openerp-web @@ -902,7 +907,7 @@ msgstr "Zapisz" #: code:addons/web/static/src/xml/base.xml:355 #, python-format msgid "More" -msgstr "" +msgstr "Więcej" #. module: web #. openerp-web @@ -916,21 +921,21 @@ msgstr "Nazwa użytkownika" #: code:addons/web/static/src/js/chrome.js:481 #, python-format msgid "Duplicating database" -msgstr "" +msgstr "Duplikowanie bazy danych" #. module: web #. openerp-web #: code:addons/web/static/src/js/chrome.js:563 #, python-format msgid "Password has been changed successfully" -msgstr "" +msgstr "Hasło zostało zmienione" #. module: web #. openerp-web #: code:addons/web/static/src/js/view_list_editable.js:781 #, python-format msgid "The form's data can not be discarded" -msgstr "" +msgstr "Dane formularza nie mogą być usunięte" #. module: web #. openerp-web @@ -999,14 +1004,14 @@ msgstr "Data utworzenia:" #: code:addons/web/controllers/main.py:851 #, python-format msgid "Error, password not changed !" -msgstr "" +msgstr "Błąd, hasło niezmienione !" #. module: web #. openerp-web #: code:addons/web/static/src/js/view_form.js:4810 #, python-format msgid "The selected file exceed the maximum file size of %s." -msgstr "" +msgstr "Wybrany plik przekracza maksymalny rozmiar %s." #. module: web #. openerp-web @@ -1021,7 +1026,7 @@ msgstr "" #: code:addons/web/static/src/js/chrome.js:563 #, python-format msgid "Changed Password" -msgstr "" +msgstr "Zmienione hasło" #. module: web #. openerp-web @@ -1055,14 +1060,14 @@ msgstr "Kopia zapasowa bazy danych" #: code:addons/web/static/src/js/dates.js:80 #, python-format msgid "'%s' is not a valid time" -msgstr "" +msgstr "\"%s\" nie jest poprawnym czasem" #. module: web #. openerp-web #: code:addons/web/static/src/js/formats.js:274 #, python-format msgid "'%s' is not a correct date" -msgstr "" +msgstr "'%s' nie jest poprawną datą" #. module: web #. openerp-web @@ -1076,7 +1081,7 @@ msgstr "" #: code:addons/web/static/src/js/coresetup.js:597 #, python-format msgid "%d days ago" -msgstr "" +msgstr "%d dni temu" #. module: web #. openerp-web @@ -1116,7 +1121,7 @@ msgstr "Ostatnio modyfikowano przez:" #: code:addons/web/static/src/xml/base.xml:466 #, python-format msgid "Timezone mismatch" -msgstr "" +msgstr "Niezgodność stref czasowych" #. module: web #. openerp-web @@ -1163,7 +1168,7 @@ msgstr "" #: code:addons/web/static/src/js/coresetup.js:599 #, python-format msgid "%d months ago" -msgstr "" +msgstr "%d miesięcy temu" #. module: web #. openerp-web @@ -1184,7 +1189,7 @@ msgstr "Dodaj filtr" #: code:addons/web/controllers/main.py:844 #, python-format msgid "The new password and its confirmation must be identical." -msgstr "" +msgstr "Nowe hasło i jego potwierdzenie muszą być jednakowe." #. module: web #. openerp-web @@ -1192,7 +1197,7 @@ msgstr "" #: code:addons/web/static/src/xml/base.xml:248 #, python-format msgid "Restore Database" -msgstr "" +msgstr "Odtwórz danych" #. module: web #. openerp-web @@ -1228,21 +1233,21 @@ msgstr "Typ eksportu:" #: code:addons/web/static/src/xml/base.xml:406 #, python-format msgid "Log out" -msgstr "" +msgstr "Wyloguj" #. module: web #. openerp-web #: code:addons/web/static/src/js/search.js:1092 #, python-format msgid "Group by: %s" -msgstr "" +msgstr "Grupuj wg: %s" #. module: web #. openerp-web #: code:addons/web/static/src/js/view_form.js:151 #, python-format msgid "No data provided." -msgstr "" +msgstr "Nie dostarczono danych" #. module: web #. openerp-web @@ -1271,7 +1276,7 @@ msgstr "Musisz wybrać co najmniej jeden rekord." #: code:addons/web/static/src/js/coresetup.js:622 #, python-format msgid "Don't leave yet,
it's still loading..." -msgstr "" +msgstr "Nie wychodź jeszcze,
ciągle pobieram..." #. module: web #. openerp-web @@ -1313,21 +1318,21 @@ msgstr "" #: code:addons/web/static/src/js/chrome.js:397 #, python-format msgid "The confirmation does not match the password" -msgstr "" +msgstr "Hasła się nie zgadzają" #. module: web #. openerp-web #: code:addons/web/static/src/js/view_form.js:4846 #, python-format msgid "Save As..." -msgstr "" +msgstr "Zapisz jako..." #. module: web #. openerp-web #: code:addons/web/static/src/js/view_form.js:4971 #, python-format msgid "Could not display the selected image." -msgstr "" +msgstr "Nie można wyświetlić wybranego obrazka." #. module: web #. openerp-web @@ -1366,7 +1371,7 @@ msgstr "1. Importuj plik .CSV" #: code:addons/web/static/src/js/chrome.js:645 #, python-format msgid "No database selected !" -msgstr "" +msgstr "Nie wybrano bazy !" #. module: web #. openerp-web @@ -1387,7 +1392,7 @@ msgstr "Zmień domyślne:" #: code:addons/web/static/src/xml/base.xml:171 #, python-format msgid "Original database name:" -msgstr "" +msgstr "Oryginalna nazwa bazy:" #. module: web #. openerp-web @@ -1404,14 +1409,14 @@ msgstr "jest równe" #: code:addons/web/static/src/js/views.js:1455 #, python-format msgid "Could not serialize XML" -msgstr "" +msgstr "Nie mozna serializować XML" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:1594 #, python-format msgid "Advanced Search" -msgstr "" +msgstr "Wyszukiwanie zaawansowane" #. module: web #. openerp-web @@ -1425,7 +1430,7 @@ msgstr "Potwierdź nowe hasło nadrzędne:" #: code:addons/web/static/src/js/coresetup.js:625 #, python-format msgid "Maybe you should consider reloading the application by pressing F5..." -msgstr "" +msgstr "Może powinieneś przeładować aplikację naciskając F5..." #. module: web #. openerp-web @@ -1457,7 +1462,7 @@ msgstr "Opcje importu" #: code:addons/web/static/src/js/view_form.js:2909 #, python-format msgid "Add %s" -msgstr "" +msgstr "Dodanie %s" #. module: web #. openerp-web @@ -1481,7 +1486,7 @@ msgstr "Zamknij" #, python-format msgid "" "You may not believe it,
but the application is actually loading..." -msgstr "" +msgstr "Może nie wierzysz,
ale aplikacja ciągle się ładuje..." #. module: web #. openerp-web @@ -1495,7 +1500,7 @@ msgstr "Plik CSV:" #: code:addons/web/static/src/js/search.js:1743 #, python-format msgid "Advanced" -msgstr "" +msgstr "Zaawansowane" #. module: web #. openerp-web @@ -1508,7 +1513,7 @@ msgstr "Drzewo" #: code:addons/web/controllers/main.py:766 #, python-format msgid "Could not drop database !" -msgstr "" +msgstr "Nie mozna usunąć bazy !" #. module: web #. openerp-web @@ -1601,21 +1606,21 @@ msgstr "" #: code:addons/web/static/src/xml/base.xml:1654 #, python-format msgid "Import-Compatible Export" -msgstr "" +msgstr "Eksport kompatybilny z importem" #. module: web #. openerp-web #: code:addons/web/static/src/js/coresetup.js:601 #, python-format msgid "%d years ago" -msgstr "" +msgstr "%d lat temu" #. module: web #. openerp-web #: code:addons/web/static/src/js/view_list.js:1019 #, python-format msgid "Unknown m2m command %s" -msgstr "" +msgstr "Nieznana komenda m2m %s" #. module: web #. openerp-web @@ -1638,7 +1643,7 @@ msgstr "Nazwa nowej bazy danych:" #: code:addons/web/static/src/js/chrome.js:394 #, python-format msgid "Please enter your new password" -msgstr "" +msgstr "Wpisz swoje nowe hasło" #. module: web #. openerp-web @@ -1694,7 +1699,7 @@ msgstr "" #: code:addons/web/static/src/js/view_form.js:2345 #, python-format msgid "This resource is empty" -msgstr "" +msgstr "Ten zasób jest pusty" #. module: web #. openerp-web @@ -1730,7 +1735,7 @@ msgstr "Zapisz jako:" #: code:addons/web/static/src/js/search.js:929 #, python-format msgid "Filter on: %s" -msgstr "" +msgstr "Filtr na: %s" #. module: web #. openerp-web @@ -1745,14 +1750,14 @@ msgstr "Utwórz: " #: code:addons/web/static/src/xml/base.xml:534 #, python-format msgid "View Fields" -msgstr "" +msgstr "Pola widoku" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:330 #, python-format msgid "Confirm New Password:" -msgstr "" +msgstr "Potwierdź nowe hasło:" #. module: web #. openerp-web @@ -1827,7 +1832,7 @@ msgstr "Wysyłam..." #: code:addons/web/static/src/xml/base.xml:1828 #, python-format msgid "Name:" -msgstr "" +msgstr "Nazwa:" #. module: web #. openerp-web @@ -1841,7 +1846,7 @@ msgstr "O programie" #: code:addons/web/static/src/xml/base.xml:1406 #, python-format msgid "Search Again" -msgstr "" +msgstr "Szukaj ponownie" #. module: web #. openerp-web @@ -1873,13 +1878,15 @@ msgid "" "Grouping on field '%s' is not possible because that field does not appear in " "the list view." msgstr "" +"Grupowanie wg pola '%s' jest niemożliwe ponieważ to pole nie występuje w " +"widoku listy." #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:531 #, python-format msgid "Set Defaults" -msgstr "" +msgstr "Ustaw domyślne" #. module: web #. openerp-web @@ -1922,7 +1929,7 @@ msgstr "Typ:" #: code:addons/web/static/src/js/chrome.js:538 #, python-format msgid "Incorrect super-administrator password" -msgstr "" +msgstr "Niepoprawne hasło superadministratora" #. module: web #. openerp-web @@ -1937,14 +1944,14 @@ msgstr "Obiekt:" #: code:addons/web/static/src/js/chrome.js:326 #, python-format msgid "Loading" -msgstr "" +msgstr "Wczytywanie" #. module: web #. openerp-web #: code:addons/web/static/src/js/coresetup.js:600 #, python-format msgid "about a year ago" -msgstr "" +msgstr "około rok temu" #. module: web #. openerp-web @@ -1970,7 +1977,7 @@ msgstr "" #: code:addons/web/static/src/js/coresetup.js:595 #, python-format msgid "%d hours ago" -msgstr "" +msgstr "%d godzin temu" #. module: web #. openerp-web @@ -1985,7 +1992,7 @@ msgstr "Dodaj: " #: code:addons/web/static/src/xml/base.xml:1833 #, python-format msgid "Quick Add" -msgstr "" +msgstr "Szybkie dodawanie" #. module: web #. openerp-web @@ -2013,7 +2020,7 @@ msgstr "" #: code:addons/web/static/src/js/views.js:1163 #, python-format msgid "Uploading..." -msgstr "" +msgstr "Wysyłanie..." #. module: web #. openerp-web @@ -2028,7 +2035,7 @@ msgstr "Wczytaj dane demonstracyjne:" #: code:addons/web/static/src/js/dates.js:26 #, python-format msgid "'%s' is not a valid datetime" -msgstr "" +msgstr "'%s' nie jest poprawnym czasem" #. module: web #. openerp-web @@ -2080,14 +2087,14 @@ msgstr "jest prawdą" #: code:addons/web/static/src/js/view_form.js:3880 #, python-format msgid "Add an item" -msgstr "" +msgstr "Dodaj element" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:1578 #, python-format msgid "Save current filter" -msgstr "" +msgstr "Zapisz bieżący filtr" #. module: web #. openerp-web @@ -2115,14 +2122,14 @@ msgstr "Pobierz \"%s\"" #: code:addons/web/static/src/js/view_form.js:324 #, python-format msgid "New" -msgstr "" +msgstr "Nowe" #. module: web #. openerp-web #: code:addons/web/static/src/js/view_list.js:1746 #, python-format msgid "Can't convert value %s to context" -msgstr "" +msgstr "Nie mozna skonwertować wartości %s do kontekstu" #. module: web #. openerp-web @@ -2195,27 +2202,27 @@ msgstr "" #: code:addons/web/static/src/js/formats.js:297 #, python-format msgid "'%s' is not a correct date, datetime nor time" -msgstr "" +msgstr "'%s' nie jest poprawną datą lub czasem" #. module: web #: code:addons/web/controllers/main.py:1306 #, python-format msgid "No content found for field '%s' on '%s:%s'" -msgstr "" +msgstr "Nie znaleziono zawartości dla pola '%s' w '%s:%s'" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:304 #, python-format msgid "Database Management" -msgstr "" +msgstr "Zarządzanie Bazami Danych" #. module: web #. openerp-web #: code:addons/web/static/src/js/view_form.js:4971 #, python-format msgid "Image" -msgstr "" +msgstr "Obrazek" #. module: web #. openerp-web @@ -2229,7 +2236,7 @@ msgstr "Zarządzaj bazami" #: code:addons/web/static/src/js/pyeval.js:765 #, python-format msgid "Evaluation Error" -msgstr "" +msgstr "Błąd wyliczenia" #. module: web #. openerp-web @@ -2248,7 +2255,7 @@ msgstr "niedozwolona liczba całkowita" #: code:addons/web/static/src/xml/base.xml:1605 #, python-format msgid "or" -msgstr "" +msgstr "lub" #. module: web #. openerp-web @@ -2262,7 +2269,7 @@ msgstr "Nie" #: code:addons/web/static/src/js/formats.js:309 #, python-format msgid "'%s' is not convertible to date, datetime nor time" -msgstr "" +msgstr "'%s' nie jest konwertowalne do daty lub czasu" #. module: web #. openerp-web @@ -2280,21 +2287,21 @@ msgstr "Duplikuj" #: code:addons/web/static/src/xml/base.xml:1366 #, python-format msgid "Discard" -msgstr "" +msgstr "Odrzuć" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:1599 #, python-format msgid "Add a condition" -msgstr "" +msgstr "Dodaj warunek" #. module: web #. openerp-web #: code:addons/web/static/src/js/coresetup.js:620 #, python-format msgid "Still loading..." -msgstr "" +msgstr "Ciągle pobieram..." #. module: web #. openerp-web @@ -2308,7 +2315,7 @@ msgstr "Niepoprawna nazwa w polu %(fieldname)s: [%(value)s] is %(message)s" #: code:addons/web/static/src/js/view_form.js:3776 #, python-format msgid "The o2m record must be saved before an action can be used" -msgstr "" +msgstr "Rekord o2m musi być zapisany, aby akcja mogła zadziałać" #. module: web #. openerp-web @@ -2322,14 +2329,14 @@ msgstr "Zapisane" #: code:addons/web/static/src/xml/base.xml:1585 #, python-format msgid "Use by default" -msgstr "" +msgstr "Stosuj domyślnie" #. module: web #. openerp-web #: code:addons/web/static/src/js/view_form.js:4846 #, python-format msgid "The field is empty, there's nothing to save !" -msgstr "" +msgstr "Pole jest puste. Nie ma nic do zapisania !" #. module: web #. openerp-web @@ -2350,7 +2357,7 @@ msgstr "uruchomione z widoku szukania" #: code:addons/web/static/src/js/search.js:980 #, python-format msgid "Filter" -msgstr "" +msgstr "Filtr" #. module: web #. openerp-web @@ -2392,7 +2399,7 @@ msgstr "Edytuj obieg" #: code:addons/web/static/src/js/views.js:1172 #, python-format msgid "Do you really want to delete this attachment ?" -msgstr "" +msgstr "Na pewno chcesz usunąć ten załącznik?" #. module: web #. openerp-web @@ -2413,14 +2420,14 @@ msgstr "Pole:" #: code:addons/web/static/src/js/chrome.js:499 #, python-format msgid "The database %s has been dropped" -msgstr "" +msgstr "Baza danych %s została usunięta" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:456 #, python-format msgid "User's timezone" -msgstr "" +msgstr "Strefa czasowa użytkownika" #. module: web #. openerp-web @@ -2435,14 +2442,14 @@ msgstr "Błąd klienta" #: code:addons/web/static/src/js/views.js:996 #, python-format msgid "Print" -msgstr "" +msgstr "Drukuj" #. module: web #. openerp-web #: code:addons/web/static/src/xml/base.xml:1306 #, python-format msgid "Special:" -msgstr "" +msgstr "Specjalne:" #. module: web #: code:addons/web/controllers/main.py:850 @@ -2450,6 +2457,7 @@ msgstr "" msgid "" "The old password you provided is incorrect, your password was not changed." msgstr "" +"Poprzednie hasło wprowadziłeś niepoprawnie. Hasło nie zostało zmienione." #. module: web #. openerp-web @@ -2477,7 +2485,7 @@ msgstr "Zapisz i zamknij" #: code:addons/web/static/src/js/view_form.js:2818 #, python-format msgid "Search More..." -msgstr "" +msgstr "Szukaj dalej..." #. module: web #. openerp-web diff --git a/addons/web/i18n/pt.po b/addons/web/i18n/pt.po index c9cdd12c09b..00c9127c3f2 100644 --- a/addons/web/i18n/pt.po +++ b/addons/web/i18n/pt.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openerp-web\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2012-11-30 18:13+0000\n" -"PO-Revision-Date: 2012-12-07 11:24+0000\n" +"PO-Revision-Date: 2012-12-10 17:13+0000\n" "Last-Translator: Virgílio Oliveira \n" "Language-Team: Portuguese \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-08 05:21+0000\n" -"X-Generator: Launchpad (build 16341)\n" +"X-Launchpad-Export-Date: 2012-12-11 05:03+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: web #. openerp-web @@ -206,7 +206,7 @@ msgstr "" #: code:addons/web/static/src/js/view_form.js:1242 #, python-format msgid "Widget type '%s' is not implemented" -msgstr "" +msgstr "Elemento do tipo '%s' não está implementado" #. module: web #. openerp-web diff --git a/addons/web_calendar/i18n/hu.po b/addons/web_calendar/i18n/hu.po index 0ed9c71dec2..dc589f805f5 100644 --- a/addons/web_calendar/i18n/hu.po +++ b/addons/web_calendar/i18n/hu.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openerp-web\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2012-11-30 18:13+0000\n" -"PO-Revision-Date: 2012-11-27 22:10+0000\n" -"Last-Translator: Juhász Krisztián \n" +"PO-Revision-Date: 2012-12-10 11:45+0000\n" +"Last-Translator: krnkris \n" "Language-Team: Hungarian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-01 05:21+0000\n" -"X-Generator: Launchpad (build 16319)\n" +"X-Launchpad-Export-Date: 2012-12-11 05:03+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: web_calendar #. openerp-web @@ -43,7 +43,7 @@ msgstr "Mentés" #: code:addons/web_calendar/static/src/js/calendar.js:99 #, python-format msgid "Calendar view has a 'date_delay' type != float" -msgstr "" +msgstr "Naptár nézetnek van 'halasztás_dátum' típusa != lebegőpontos" #. module: web_calendar #. openerp-web @@ -214,7 +214,7 @@ msgstr "Naptár" #: code:addons/web_calendar/static/src/js/calendar.js:91 #, python-format msgid "Calendar view has not defined 'date_start' attribute." -msgstr "" +msgstr "Naptár nézetnek nincs definiálva 'induló_dátum' értéke" #~ msgid "Navigator" #~ msgstr "Navigátor" diff --git a/addons/web_calendar/i18n/pl.po b/addons/web_calendar/i18n/pl.po index b4d919e33bf..504daa3162d 100644 --- a/addons/web_calendar/i18n/pl.po +++ b/addons/web_calendar/i18n/pl.po @@ -8,35 +8,35 @@ msgstr "" "Project-Id-Version: openerp-web\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2012-11-30 18:13+0000\n" -"PO-Revision-Date: 2012-02-08 11:19+0000\n" -"Last-Translator: drygal \n" +"PO-Revision-Date: 2012-12-10 12:19+0000\n" +"Last-Translator: Grzegorz Grzelak (OpenGLOBE.pl) \n" "Language-Team: Polish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-01 05:21+0000\n" -"X-Generator: Launchpad (build 16319)\n" +"X-Launchpad-Export-Date: 2012-12-11 05:03+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: web_calendar #. openerp-web #: code:addons/web_calendar/static/src/js/calendar.js:151 #, python-format msgid "New event" -msgstr "" +msgstr "Nowe zdarzenie" #. module: web_calendar #. openerp-web #: code:addons/web_calendar/static/src/js/calendar.js:154 #, python-format msgid "Details" -msgstr "" +msgstr "Szczegóły" #. module: web_calendar #. openerp-web #: code:addons/web_calendar/static/src/js/calendar.js:152 #, python-format msgid "Save" -msgstr "" +msgstr "Zapisz" #. module: web_calendar #. openerp-web @@ -50,21 +50,21 @@ msgstr "" #: code:addons/web_calendar/static/src/js/calendar.js:147 #, python-format msgid "Today" -msgstr "" +msgstr "Dzisiaj" #. module: web_calendar #. openerp-web #: code:addons/web_calendar/static/src/js/calendar.js:149 #, python-format msgid "Week" -msgstr "" +msgstr "Tydzień" #. module: web_calendar #. openerp-web #: code:addons/web_calendar/static/src/js/calendar.js:161 #, python-format msgid "Full day" -msgstr "" +msgstr "Cały dzień" #. module: web_calendar #. openerp-web @@ -72,14 +72,14 @@ msgstr "" #: code:addons/web_calendar/static/src/js/calendar.js:172 #, python-format msgid "Description" -msgstr "" +msgstr "Opis" #. module: web_calendar #. openerp-web #: code:addons/web_calendar/static/src/js/calendar.js:158 #, python-format msgid "Event will be deleted permanently, are you sure?" -msgstr "" +msgstr "Zdarzenie zostanie ostatecznie usunięte" #. module: web_calendar #. openerp-web @@ -94,56 +94,56 @@ msgstr " " #: code:addons/web_calendar/static/src/js/calendar.js:171 #, python-format msgid "Date" -msgstr "" +msgstr "Data" #. module: web_calendar #. openerp-web #: code:addons/web_calendar/static/src/js/calendar.js:468 #, python-format msgid "Edit: " -msgstr "" +msgstr "Edytowanie: " #. module: web_calendar #. openerp-web #: code:addons/web_calendar/static/src/js/calendar.js:148 #, python-format msgid "Day" -msgstr "" +msgstr "Dzień" #. module: web_calendar #. openerp-web #: code:addons/web_calendar/static/src/js/calendar.js:155 #, python-format msgid "Edit" -msgstr "" +msgstr "Edytuj" #. module: web_calendar #. openerp-web #: code:addons/web_calendar/static/src/js/calendar.js:167 #, python-format msgid "Enabled" -msgstr "" +msgstr "Włączone" #. module: web_calendar #. openerp-web #: code:addons/web_calendar/static/src/js/calendar.js:164 #, python-format msgid "Do you want to edit the whole set of repeated events?" -msgstr "" +msgstr "Chcesz edytować cały zestaw powtarzalnych zdarzeń?" #. module: web_calendar #. openerp-web #: code:addons/web_calendar/static/src/js/calendar.js:80 #, python-format msgid "Filter" -msgstr "" +msgstr "Filtrowanie" #. module: web_calendar #. openerp-web #: code:addons/web_calendar/static/src/js/calendar.js:165 #, python-format msgid "Repeat event" -msgstr "" +msgstr "Powtórz zdarzenie" #. module: web_calendar #. openerp-web @@ -151,56 +151,56 @@ msgstr "" #: code:addons/web_calendar/static/src/js/calendar.js:178 #, python-format msgid "Agenda" -msgstr "" +msgstr "Plan spotkania" #. module: web_calendar #. openerp-web #: code:addons/web_calendar/static/src/js/calendar.js:160 #, python-format msgid "Time period" -msgstr "" +msgstr "Okres czasu" #. module: web_calendar #. openerp-web #: code:addons/web_calendar/static/src/js/calendar.js:156 #, python-format msgid "Delete" -msgstr "" +msgstr "Usuń" #. module: web_calendar #. openerp-web #: code:addons/web_calendar/static/src/js/calendar.js:150 #, python-format msgid "Month" -msgstr "" +msgstr "Miesiąc" #. module: web_calendar #. openerp-web #: code:addons/web_calendar/static/src/js/calendar.js:166 #, python-format msgid "Disabled" -msgstr "" +msgstr "Wyłączone" #. module: web_calendar #. openerp-web #: code:addons/web_calendar/static/src/js/calendar.js:433 #, python-format msgid "Create: " -msgstr "" +msgstr "Utwórz: " #. module: web_calendar #. openerp-web #: code:addons/web_calendar/static/src/js/calendar.js:175 #, python-format msgid "Year" -msgstr "" +msgstr "Rok" #. module: web_calendar #. openerp-web #: code:addons/web_calendar/static/src/js/calendar.js:153 #, python-format msgid "Cancel" -msgstr "" +msgstr "Anuluj" #. module: web_calendar #. openerp-web @@ -214,7 +214,7 @@ msgstr "Kalendarz" #: code:addons/web_calendar/static/src/js/calendar.js:91 #, python-format msgid "Calendar view has not defined 'date_start' attribute." -msgstr "" +msgstr "Widok kalendzara nie ma zdefiniowanego atrybutu 'date_start'." #~ msgid "Navigator" #~ msgstr "Nawigator" diff --git a/addons/web_diagram/i18n/hu.po b/addons/web_diagram/i18n/hu.po new file mode 100644 index 00000000000..3f9e2b75bed --- /dev/null +++ b/addons/web_diagram/i18n/hu.po @@ -0,0 +1,106 @@ +# Hungarian translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR , 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-11-24 01:23+0000\n" +"PO-Revision-Date: 2012-12-10 12:17+0000\n" +"Last-Translator: krnkris \n" +"Language-Team: Hungarian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-12-11 05:03+0000\n" +"X-Generator: Launchpad (build 16356)\n" + +#. module: web_diagram +#. openerp-web +#: code:addons/web_diagram/static/src/js/diagram.js:254 +#: code:addons/web_diagram/static/src/js/diagram.js:319 +#, python-format +msgid "Open: " +msgstr "Megnyitás: " + +#. module: web_diagram +#. openerp-web +#: code:addons/web_diagram/static/src/js/diagram.js:217 +#, python-format +msgid "" +"Deleting this node cannot be undone.\n" +"It will also delete all connected transitions.\n" +"\n" +"Are you sure ?" +msgstr "" +"Ennek a csomópontnak a törlését nem lehet visszaállítani.\n" +"Minden csatlakozott átmenetet is törölni fog.\n" +"\n" +"Biztos benne?" + +#. module: web_diagram +#. openerp-web +#: code:addons/web_diagram/static/src/xml/base_diagram.xml:13 +#, python-format +msgid "New Node" +msgstr "Új csomópont" + +#. module: web_diagram +#. openerp-web +#: code:addons/web_diagram/static/src/js/diagram.js:312 +#: code:addons/web_diagram/static/src/js/diagram.js:331 +#, python-format +msgid "Transition" +msgstr "Átmenet" + +#. module: web_diagram +#. openerp-web +#: code:addons/web_diagram/static/src/js/diagram.js:11 +#, python-format +msgid "Diagram" +msgstr "Diagram" + +#. module: web_diagram +#. openerp-web +#: code:addons/web_diagram/static/src/js/diagram.js:246 +#: code:addons/web_diagram/static/src/js/diagram.js:280 +#, python-format +msgid "Activity" +msgstr "Tevékenység" + +#. module: web_diagram +#. openerp-web +#: code:addons/web_diagram/static/src/js/diagram.js:422 +#, python-format +msgid "%d / %d" +msgstr "%d / %d" + +#. module: web_diagram +#. openerp-web +#: code:addons/web_diagram/static/src/js/diagram.js:285 +#: code:addons/web_diagram/static/src/js/diagram.js:337 +#, python-format +msgid "Create:" +msgstr "Létrehoz:" + +#. module: web_diagram +#. openerp-web +#: code:addons/web_diagram/static/src/js/diagram.js:187 +#, python-format +msgid "Are you sure?" +msgstr "Biztos benne?" + +#. module: web_diagram +#. openerp-web +#: code:addons/web_diagram/static/src/js/diagram.js:235 +#, python-format +msgid "" +"Deleting this transition cannot be undone.\n" +"\n" +"Are you sure ?" +msgstr "" +"Ennek az átmenetnek a törlését nem lehet visszaállítani.\n" +"\n" +"Biztos benne ?" diff --git a/addons/web_diagram/i18n/pl.po b/addons/web_diagram/i18n/pl.po index 741fa0fbc00..99ebb192053 100644 --- a/addons/web_diagram/i18n/pl.po +++ b/addons/web_diagram/i18n/pl.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openerp-web\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2012-11-24 01:23+0000\n" -"PO-Revision-Date: 2012-02-07 20:03+0000\n" +"PO-Revision-Date: 2012-12-10 12:10+0000\n" "Last-Translator: Grzegorz Grzelak (OpenGLOBE.pl) \n" "Language-Team: Polish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-11-25 06:41+0000\n" -"X-Generator: Launchpad (build 16293)\n" +"X-Launchpad-Export-Date: 2012-12-11 05:03+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: web_diagram #. openerp-web @@ -35,6 +35,10 @@ msgid "" "\n" "Are you sure ?" msgstr "" +"Usunięcie węzła nie może być cofnięte.\n" +"Zostaną usunięte również powiązanie z nim przejścia.\n" +"\n" +"Jesteś pewien ?" #. module: web_diagram #. openerp-web @@ -86,7 +90,7 @@ msgstr "Utwórz:" #: code:addons/web_diagram/static/src/js/diagram.js:187 #, python-format msgid "Are you sure?" -msgstr "" +msgstr "Jesteś pewien?" #. module: web_diagram #. openerp-web @@ -97,3 +101,6 @@ msgid "" "\n" "Are you sure ?" msgstr "" +"Usunięcie węzła nie może być cofnięte.\n" +"\n" +"Jesteś pewien ?" diff --git a/addons/web_graph/i18n/de.po b/addons/web_graph/i18n/de.po index 736cdc9c0c9..13aee967ff7 100644 --- a/addons/web_graph/i18n/de.po +++ b/addons/web_graph/i18n/de.po @@ -8,28 +8,28 @@ msgstr "" "Project-Id-Version: openerp-web\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2012-11-30 18:13+0000\n" -"PO-Revision-Date: 2011-12-16 15:28+0000\n" -"Last-Translator: Ferdinand @ Camptocamp \n" +"PO-Revision-Date: 2012-12-10 22:55+0000\n" +"Last-Translator: Maik Steinfeld \n" "Language-Team: German \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-01 05:21+0000\n" -"X-Generator: Launchpad (build 16319)\n" +"X-Launchpad-Export-Date: 2012-12-11 05:03+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:12 #, python-format msgid "Bars" -msgstr "" +msgstr "Balken" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:33 #, python-format msgid "Show Data" -msgstr "" +msgstr "Zeige Daten" #. module: web_graph #. openerp-web @@ -57,7 +57,7 @@ msgstr "" #: code:addons/web_graph/static/src/xml/web_graph.xml:11 #, python-format msgid "Pie" -msgstr "" +msgstr "Tortendiagramm" #. module: web_graph #. openerp-web @@ -78,14 +78,14 @@ msgstr "" #: code:addons/web_graph/static/src/xml/web_graph.xml:18 #, python-format msgid "Radar" -msgstr "" +msgstr "Radar" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:34 #, python-format msgid "Download as PNG" -msgstr "" +msgstr "Als PNG herunterladen" #. module: web_graph #. openerp-web @@ -99,7 +99,7 @@ msgstr "" #: code:addons/web_graph/static/src/xml/web_graph.xml:24 #, python-format msgid "Hidden" -msgstr "" +msgstr "Versteckt" #. module: web_graph #. openerp-web @@ -120,18 +120,18 @@ msgstr "" #: code:addons/web_graph/static/src/xml/web_graph.xml:20 #, python-format msgid "Legend" -msgstr "" +msgstr "Legende" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:32 #, python-format msgid "Switch Axis" -msgstr "" +msgstr "Achsen wechseln" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:15 #, python-format msgid "Areas" -msgstr "" +msgstr "Bereiche" diff --git a/addons/web_graph/i18n/hu.po b/addons/web_graph/i18n/hu.po index 3993b70cf69..9cb83d437f8 100644 --- a/addons/web_graph/i18n/hu.po +++ b/addons/web_graph/i18n/hu.po @@ -8,28 +8,28 @@ msgstr "" "Project-Id-Version: openerp-web\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2012-11-30 18:13+0000\n" -"PO-Revision-Date: 2012-07-19 06:27+0000\n" -"Last-Translator: FULL NAME \n" +"PO-Revision-Date: 2012-12-10 11:51+0000\n" +"Last-Translator: krnkris \n" "Language-Team: Hungarian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-01 05:21+0000\n" -"X-Generator: Launchpad (build 16319)\n" +"X-Launchpad-Export-Date: 2012-12-11 05:03+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:12 #, python-format msgid "Bars" -msgstr "" +msgstr "Sávok" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:33 #, python-format msgid "Show Data" -msgstr "" +msgstr "Adatokat mutatni" #. module: web_graph #. openerp-web @@ -43,95 +43,95 @@ msgstr "Grafikon" #: code:addons/web_graph/static/src/xml/web_graph.xml:25 #, python-format msgid "Inside" -msgstr "" +msgstr "Belül" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:3 #, python-format msgid "í" -msgstr "" +msgstr "Hegyes" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:11 #, python-format msgid "Pie" -msgstr "" +msgstr "Körcikk" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:28 #, python-format msgid "Actions" -msgstr "" +msgstr "Műveletek" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:7 #, python-format msgid "Graph Mode" -msgstr "" +msgstr "Grafikon üzemmód" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:18 #, python-format msgid "Radar" -msgstr "" +msgstr "Radar" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:34 #, python-format msgid "Download as PNG" -msgstr "" +msgstr "PNG-ben letölt" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:26 #, python-format msgid "Top" -msgstr "" +msgstr "Felső" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:24 #, python-format msgid "Hidden" -msgstr "" +msgstr "Rejtett" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:3 #, python-format msgid "Graph Options" -msgstr "" +msgstr "Grafikon beállítások" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:14 #, python-format msgid "Lines" -msgstr "" +msgstr "Sorok" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:20 #, python-format msgid "Legend" -msgstr "" +msgstr "Jelmagyarázat" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:32 #, python-format msgid "Switch Axis" -msgstr "" +msgstr "Tengelyket átkapcsolja" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:15 #, python-format msgid "Areas" -msgstr "" +msgstr "Területek" diff --git a/addons/web_graph/i18n/it.po b/addons/web_graph/i18n/it.po index 77a0fb7d83e..bdb83bb881e 100644 --- a/addons/web_graph/i18n/it.po +++ b/addons/web_graph/i18n/it.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-10 04:44+0000\n" -"X-Generator: Launchpad (build 16341)\n" +"X-Launchpad-Export-Date: 2012-12-11 05:03+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: web_graph #. openerp-web diff --git a/addons/web_graph/i18n/pl.po b/addons/web_graph/i18n/pl.po index 558839f3cbc..5af7e15eb9d 100644 --- a/addons/web_graph/i18n/pl.po +++ b/addons/web_graph/i18n/pl.po @@ -8,28 +8,28 @@ msgstr "" "Project-Id-Version: openerp-web\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2012-11-30 18:13+0000\n" -"PO-Revision-Date: 2012-02-28 11:09+0000\n" -"Last-Translator: FULL NAME \n" +"PO-Revision-Date: 2012-12-10 12:15+0000\n" +"Last-Translator: Grzegorz Grzelak (OpenGLOBE.pl) \n" "Language-Team: Polish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-01 05:21+0000\n" -"X-Generator: Launchpad (build 16319)\n" +"X-Launchpad-Export-Date: 2012-12-11 05:03+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:12 #, python-format msgid "Bars" -msgstr "" +msgstr "Słupki" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:33 #, python-format msgid "Show Data" -msgstr "" +msgstr "Pokaż dane" #. module: web_graph #. openerp-web @@ -43,7 +43,7 @@ msgstr "Wykres" #: code:addons/web_graph/static/src/xml/web_graph.xml:25 #, python-format msgid "Inside" -msgstr "" +msgstr "Wewnętrzne" #. module: web_graph #. openerp-web @@ -57,81 +57,81 @@ msgstr "" #: code:addons/web_graph/static/src/xml/web_graph.xml:11 #, python-format msgid "Pie" -msgstr "" +msgstr "Kołowy" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:28 #, python-format msgid "Actions" -msgstr "" +msgstr "Akcje" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:7 #, python-format msgid "Graph Mode" -msgstr "" +msgstr "Tryb wykresowy" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:18 #, python-format msgid "Radar" -msgstr "" +msgstr "Radar" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:34 #, python-format msgid "Download as PNG" -msgstr "" +msgstr "Pobierz jako PNG" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:26 #, python-format msgid "Top" -msgstr "" +msgstr "Na górze" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:24 #, python-format msgid "Hidden" -msgstr "" +msgstr "Ukryte" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:3 #, python-format msgid "Graph Options" -msgstr "" +msgstr "Opcje wykresu" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:14 #, python-format msgid "Lines" -msgstr "" +msgstr "Linie" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:20 #, python-format msgid "Legend" -msgstr "" +msgstr "Legenda" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:32 #, python-format msgid "Switch Axis" -msgstr "" +msgstr "Zamień osie" #. module: web_graph #. openerp-web #: code:addons/web_graph/static/src/xml/web_graph.xml:15 #, python-format msgid "Areas" -msgstr "" +msgstr "Obszary" diff --git a/addons/web_graph/i18n/pt.po b/addons/web_graph/i18n/pt.po index 4c2cfb26265..c0be279e588 100644 --- a/addons/web_graph/i18n/pt.po +++ b/addons/web_graph/i18n/pt.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openerp-web\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2012-11-30 18:13+0000\n" -"PO-Revision-Date: 2012-12-04 12:47+0000\n" -"Last-Translator: Andrei Talpa (multibase.pt) \n" +"PO-Revision-Date: 2012-12-10 17:17+0000\n" +"Last-Translator: Virgílio Oliveira \n" "Language-Team: Portuguese \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-05 05:43+0000\n" -"X-Generator: Launchpad (build 16335)\n" +"X-Launchpad-Export-Date: 2012-12-11 05:03+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: web_graph #. openerp-web @@ -106,7 +106,7 @@ msgstr "" #: code:addons/web_graph/static/src/xml/web_graph.xml:3 #, python-format msgid "Graph Options" -msgstr "" +msgstr "Opções do gráfico" #. module: web_graph #. openerp-web diff --git a/addons/web_kanban/i18n/hu.po b/addons/web_kanban/i18n/hu.po index ba4c12daba6..c4c89941688 100644 --- a/addons/web_kanban/i18n/hu.po +++ b/addons/web_kanban/i18n/hu.po @@ -8,28 +8,28 @@ msgstr "" "Project-Id-Version: openerp-web\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2012-11-30 18:13+0000\n" -"PO-Revision-Date: 2012-07-19 06:26+0000\n" -"Last-Translator: FULL NAME \n" +"PO-Revision-Date: 2012-12-10 11:58+0000\n" +"Last-Translator: krnkris \n" "Language-Team: Hungarian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-01 05:21+0000\n" -"X-Generator: Launchpad (build 16319)\n" +"X-Launchpad-Export-Date: 2012-12-11 05:03+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: web_kanban #. openerp-web #: code:addons/web_kanban/static/src/js/kanban.js:698 #, python-format msgid "Edit column" -msgstr "" +msgstr "Mező szerkesztése" #. module: web_kanban #. openerp-web #: code:addons/web_kanban/static/src/js/kanban.js:413 #, python-format msgid "An error has occured while moving the record to this group." -msgstr "" +msgstr "Hiba lépett fel a rekord ebbe a csoportba való mozgatásakor." #. module: web_kanban #. openerp-web @@ -50,35 +50,35 @@ msgstr "Nem definiált" #: code:addons/web_kanban/static/src/js/kanban.js:717 #, python-format msgid "Are you sure to remove this column ?" -msgstr "" +msgstr "Biztos el akarja távolítani ezt az oszlopot." #. module: web_kanban #. openerp-web #: code:addons/web_kanban/static/src/xml/web_kanban.xml:42 #, python-format msgid "Edit" -msgstr "" +msgstr "Szerkeszt" #. module: web_kanban #. openerp-web #: code:addons/web_kanban/static/src/js/kanban.js:188 #, python-format msgid "Add column" -msgstr "" +msgstr "Oszlop hozzáadása" #. module: web_kanban #. openerp-web #: code:addons/web_kanban/static/src/js/kanban.js:1093 #, python-format msgid "Create: " -msgstr "" +msgstr "Létrehoz: " #. module: web_kanban #. openerp-web #: code:addons/web_kanban/static/src/xml/web_kanban.xml:24 #, python-format msgid "Add a new column" -msgstr "" +msgstr "Új oszlop hozzáadása" #. module: web_kanban #. openerp-web @@ -86,21 +86,21 @@ msgstr "" #: code:addons/web_kanban/static/src/xml/web_kanban.xml:40 #, python-format msgid "Fold" -msgstr "" +msgstr "Hajtás" #. module: web_kanban #. openerp-web #: code:addons/web_kanban/static/src/xml/web_kanban.xml:90 #, python-format msgid "Add" -msgstr "" +msgstr "Hozzáad" #. module: web_kanban #. openerp-web #: code:addons/web_kanban/static/src/xml/web_kanban.xml:35 #, python-format msgid "Quick create" -msgstr "" +msgstr "Gyors létrehozás" #. module: web_kanban #. openerp-web @@ -114,28 +114,28 @@ msgstr "Biztosan törölni szeretné ezt a bejegyzést?" #: code:addons/web_kanban/static/src/js/kanban.js:688 #, python-format msgid "Unfold" -msgstr "" +msgstr "Kihajtás" #. module: web_kanban #. openerp-web #: code:addons/web_kanban/static/src/xml/web_kanban.xml:72 #, python-format msgid "Show more... (" -msgstr "" +msgstr "Mutasson többet... (" #. module: web_kanban #. openerp-web #: code:addons/web_kanban/static/src/xml/web_kanban.xml:91 #, python-format msgid "Cancel" -msgstr "" +msgstr "Mégsem" #. module: web_kanban #. openerp-web #: code:addons/web_kanban/static/src/xml/web_kanban.xml:72 #, python-format msgid "remaining)" -msgstr "" +msgstr "visszamaradt)" #. module: web_kanban #. openerp-web @@ -143,21 +143,21 @@ msgstr "" #: code:addons/web_kanban/static/src/xml/web_kanban.xml:90 #, python-format msgid "or" -msgstr "" +msgstr "vagy" #. module: web_kanban #. openerp-web #: code:addons/web_kanban/static/src/xml/web_kanban.xml:51 #, python-format msgid "99+" -msgstr "" +msgstr "99+" #. module: web_kanban #. openerp-web #: code:addons/web_kanban/static/src/xml/web_kanban.xml:43 #, python-format msgid "Delete" -msgstr "" +msgstr "Törlés" #~ msgid "Create" #~ msgstr "Létrehozás" diff --git a/addons/web_kanban/i18n/it.po b/addons/web_kanban/i18n/it.po index 5ec66ff2641..8b717bf1df0 100644 --- a/addons/web_kanban/i18n/it.po +++ b/addons/web_kanban/i18n/it.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-10 04:44+0000\n" -"X-Generator: Launchpad (build 16341)\n" +"X-Launchpad-Export-Date: 2012-12-11 05:03+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: web_kanban #. openerp-web diff --git a/addons/web_kanban/i18n/pl.po b/addons/web_kanban/i18n/pl.po index 9f2d33bcb2f..1400ef00f6f 100644 --- a/addons/web_kanban/i18n/pl.po +++ b/addons/web_kanban/i18n/pl.po @@ -8,28 +8,28 @@ msgstr "" "Project-Id-Version: openerp-web\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2012-11-30 18:13+0000\n" -"PO-Revision-Date: 2012-02-28 11:13+0000\n" -"Last-Translator: FULL NAME \n" +"PO-Revision-Date: 2012-12-10 12:15+0000\n" +"Last-Translator: Grzegorz Grzelak (OpenGLOBE.pl) \n" "Language-Team: Polish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-01 05:21+0000\n" -"X-Generator: Launchpad (build 16319)\n" +"X-Launchpad-Export-Date: 2012-12-11 05:03+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: web_kanban #. openerp-web #: code:addons/web_kanban/static/src/js/kanban.js:698 #, python-format msgid "Edit column" -msgstr "" +msgstr "Edytuj kolumnę" #. module: web_kanban #. openerp-web #: code:addons/web_kanban/static/src/js/kanban.js:413 #, python-format msgid "An error has occured while moving the record to this group." -msgstr "" +msgstr "Wystąpił błąd podczas przesuwania rekordu do tej grupy." #. module: web_kanban #. openerp-web @@ -50,35 +50,35 @@ msgstr "Niezdefiniowane" #: code:addons/web_kanban/static/src/js/kanban.js:717 #, python-format msgid "Are you sure to remove this column ?" -msgstr "" +msgstr "Jesteś pewien, że chcesz usunąć tę kolumnę?" #. module: web_kanban #. openerp-web #: code:addons/web_kanban/static/src/xml/web_kanban.xml:42 #, python-format msgid "Edit" -msgstr "" +msgstr "Edytuj" #. module: web_kanban #. openerp-web #: code:addons/web_kanban/static/src/js/kanban.js:188 #, python-format msgid "Add column" -msgstr "" +msgstr "Dodaj kolumnę" #. module: web_kanban #. openerp-web #: code:addons/web_kanban/static/src/js/kanban.js:1093 #, python-format msgid "Create: " -msgstr "" +msgstr "Utwórz: " #. module: web_kanban #. openerp-web #: code:addons/web_kanban/static/src/xml/web_kanban.xml:24 #, python-format msgid "Add a new column" -msgstr "" +msgstr "Dodaj nową kolumnę" #. module: web_kanban #. openerp-web @@ -86,21 +86,21 @@ msgstr "" #: code:addons/web_kanban/static/src/xml/web_kanban.xml:40 #, python-format msgid "Fold" -msgstr "" +msgstr "Zwiń" #. module: web_kanban #. openerp-web #: code:addons/web_kanban/static/src/xml/web_kanban.xml:90 #, python-format msgid "Add" -msgstr "" +msgstr "Dodaj" #. module: web_kanban #. openerp-web #: code:addons/web_kanban/static/src/xml/web_kanban.xml:35 #, python-format msgid "Quick create" -msgstr "" +msgstr "Szybkie tworzenie" #. module: web_kanban #. openerp-web @@ -114,7 +114,7 @@ msgstr "Jesteś pewien, że chcesz usunąć ten rekord?" #: code:addons/web_kanban/static/src/js/kanban.js:688 #, python-format msgid "Unfold" -msgstr "" +msgstr "Rozwiń" #. module: web_kanban #. openerp-web @@ -128,7 +128,7 @@ msgstr "Pokaż więcej...(" #: code:addons/web_kanban/static/src/xml/web_kanban.xml:91 #, python-format msgid "Cancel" -msgstr "" +msgstr "Anuluj" #. module: web_kanban #. openerp-web @@ -143,7 +143,7 @@ msgstr "pozostało)" #: code:addons/web_kanban/static/src/xml/web_kanban.xml:90 #, python-format msgid "or" -msgstr "" +msgstr "lub" #. module: web_kanban #. openerp-web @@ -157,7 +157,7 @@ msgstr "" #: code:addons/web_kanban/static/src/xml/web_kanban.xml:43 #, python-format msgid "Delete" -msgstr "" +msgstr "Usuń" #~ msgid "Create" #~ msgstr "Utwórz" diff --git a/addons/web_kanban/i18n/ro.po b/addons/web_kanban/i18n/ro.po index dff92f2002d..5d94ff2d563 100644 --- a/addons/web_kanban/i18n/ro.po +++ b/addons/web_kanban/i18n/ro.po @@ -8,28 +8,28 @@ msgstr "" "Project-Id-Version: openerp-web\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2012-11-30 18:13+0000\n" -"PO-Revision-Date: 2012-03-10 13:21+0000\n" -"Last-Translator: FULL NAME \n" +"PO-Revision-Date: 2012-12-10 05:10+0000\n" +"Last-Translator: Fekete Mihai \n" "Language-Team: Romanian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-01 05:21+0000\n" -"X-Generator: Launchpad (build 16319)\n" +"X-Launchpad-Export-Date: 2012-12-11 05:03+0000\n" +"X-Generator: Launchpad (build 16356)\n" #. module: web_kanban #. openerp-web #: code:addons/web_kanban/static/src/js/kanban.js:698 #, python-format msgid "Edit column" -msgstr "" +msgstr "Editează coloana" #. module: web_kanban #. openerp-web #: code:addons/web_kanban/static/src/js/kanban.js:413 #, python-format msgid "An error has occured while moving the record to this group." -msgstr "" +msgstr "Eroare in timp ce inregistra mutarea la acest grup." #. module: web_kanban #. openerp-web @@ -50,35 +50,35 @@ msgstr "Nedefinit(a)" #: code:addons/web_kanban/static/src/js/kanban.js:717 #, python-format msgid "Are you sure to remove this column ?" -msgstr "" +msgstr "Sunteţi sigur că vreti sa eliminati această coloană?" #. module: web_kanban #. openerp-web #: code:addons/web_kanban/static/src/xml/web_kanban.xml:42 #, python-format msgid "Edit" -msgstr "" +msgstr "Editaţi" #. module: web_kanban #. openerp-web #: code:addons/web_kanban/static/src/js/kanban.js:188 #, python-format msgid "Add column" -msgstr "" +msgstr "Adaugă coloană" #. module: web_kanban #. openerp-web #: code:addons/web_kanban/static/src/js/kanban.js:1093 #, python-format msgid "Create: " -msgstr "" +msgstr "Creati: " #. module: web_kanban #. openerp-web #: code:addons/web_kanban/static/src/xml/web_kanban.xml:24 #, python-format msgid "Add a new column" -msgstr "" +msgstr "Adaugă coloană nouă" #. module: web_kanban #. openerp-web @@ -86,21 +86,21 @@ msgstr "" #: code:addons/web_kanban/static/src/xml/web_kanban.xml:40 #, python-format msgid "Fold" -msgstr "" +msgstr "Pliază" #. module: web_kanban #. openerp-web #: code:addons/web_kanban/static/src/xml/web_kanban.xml:90 #, python-format msgid "Add" -msgstr "" +msgstr "Adaugă" #. module: web_kanban #. openerp-web #: code:addons/web_kanban/static/src/xml/web_kanban.xml:35 #, python-format msgid "Quick create" -msgstr "" +msgstr "Creaza rapid" #. module: web_kanban #. openerp-web @@ -114,7 +114,7 @@ msgstr "Sunteti sigur(a) ca doriti sa stergeti aceasta inregistrare?" #: code:addons/web_kanban/static/src/js/kanban.js:688 #, python-format msgid "Unfold" -msgstr "" +msgstr "Depliază" #. module: web_kanban #. openerp-web @@ -128,7 +128,7 @@ msgstr "Afiseaza mai mult... (" #: code:addons/web_kanban/static/src/xml/web_kanban.xml:91 #, python-format msgid "Cancel" -msgstr "" +msgstr "Renunţare" #. module: web_kanban #. openerp-web @@ -143,7 +143,7 @@ msgstr "ceea ce a ramas)" #: code:addons/web_kanban/static/src/xml/web_kanban.xml:90 #, python-format msgid "or" -msgstr "" +msgstr "sau" #. module: web_kanban #. openerp-web @@ -157,7 +157,7 @@ msgstr "" #: code:addons/web_kanban/static/src/xml/web_kanban.xml:43 #, python-format msgid "Delete" -msgstr "" +msgstr "Șterge" #~ msgid "Create" #~ msgstr "Creati" diff --git a/addons/web_linkedin/i18n/hr.po b/addons/web_linkedin/i18n/hr.po new file mode 100644 index 00000000000..cd36d145b3b --- /dev/null +++ b/addons/web_linkedin/i18n/hr.po @@ -0,0 +1,137 @@ +# Croatian translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-11-24 02:54+0000\n" +"PO-Revision-Date: 2012-12-10 07:03+0000\n" +"Last-Translator: Goran Kliska \n" +"Language-Team: Croatian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-12-11 04:49+0000\n" +"X-Generator: Launchpad (build 16356)\n" + +#. module: web_linkedin +#: view:sale.config.settings:0 +msgid "here:" +msgstr "ovdje:" + +#. module: web_linkedin +#: field:sale.config.settings,api_key:0 +msgid "API Key" +msgstr "API ključ" + +#. module: web_linkedin +#. openerp-web +#: code:addons/web_linkedin/static/src/js/linkedin.js:249 +#, python-format +msgid "No results found" +msgstr "Nema pronađenih rezultata" + +#. module: web_linkedin +#. openerp-web +#: code:addons/web_linkedin/static/src/js/linkedin.js:84 +#, python-format +msgid "Ok" +msgstr "Ok" + +#. module: web_linkedin +#: view:sale.config.settings:0 +msgid "Log into LinkedIn." +msgstr "Prijava na LinkedIn." + +#. module: web_linkedin +#. openerp-web +#: code:addons/web_linkedin/static/src/xml/linkedin.xml:13 +#, python-format +msgid "People" +msgstr "Osobe" + +#. module: web_linkedin +#: model:ir.model,name:web_linkedin.model_sale_config_settings +msgid "sale.config.settings" +msgstr "" + +#. module: web_linkedin +#: field:sale.config.settings,server_domain:0 +msgid "unknown" +msgstr "nepoznato" + +#. module: web_linkedin +#: view:sale.config.settings:0 +msgid "https://www.linkedin.com/secure/developer" +msgstr "https://www.linkedin.com/secure/developer" + +#. module: web_linkedin +#. openerp-web +#: code:addons/web_linkedin/static/src/xml/linkedin.xml:15 +#, python-format +msgid "Companies" +msgstr "Organizacije" + +#. module: web_linkedin +#: view:sale.config.settings:0 +msgid "API key" +msgstr "API ključ" + +#. module: web_linkedin +#: view:sale.config.settings:0 +msgid "Copy the" +msgstr "Kopiraj" + +#. module: web_linkedin +#. openerp-web +#: code:addons/web_linkedin/static/src/js/linkedin.js:181 +#, python-format +msgid "LinkedIn search" +msgstr "LinkedIn pretraga" + +#. module: web_linkedin +#. openerp-web +#: code:addons/web_linkedin/static/src/xml/linkedin.xml:31 +#, python-format +msgid "" +"LinkedIn access was not enabled on this server.\n" +" Please ask your administrator to configure it in Settings > " +"Configuration > Sales > Social Network Integration." +msgstr "" + +#. module: web_linkedin +#: view:sale.config.settings:0 +msgid "" +"To use the LinkedIn module with this database, an API Key is required. " +"Please follow this procedure:" +msgstr "" + +#. module: web_linkedin +#. openerp-web +#: code:addons/web_linkedin/static/src/js/linkedin.js:82 +#, python-format +msgid "LinkedIn is not enabled" +msgstr "LinkedIn nije omogućen" + +#. module: web_linkedin +#: view:sale.config.settings:0 +msgid "Add a new application and fill the form:" +msgstr "" + +#. module: web_linkedin +#: view:sale.config.settings:0 +msgid "Go to this URL:" +msgstr "" + +#. module: web_linkedin +#: view:sale.config.settings:0 +msgid "The programming tool is Javascript" +msgstr "" + +#. module: web_linkedin +#: view:sale.config.settings:0 +msgid "JavaScript API Domain:" +msgstr "" diff --git a/addons/web_shortcuts/i18n/hr.po b/addons/web_shortcuts/i18n/hr.po new file mode 100644 index 00000000000..ac3b78371b4 --- /dev/null +++ b/addons/web_shortcuts/i18n/hr.po @@ -0,0 +1,25 @@ +# Croatian translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-11-24 02:54+0000\n" +"PO-Revision-Date: 2012-12-10 06:51+0000\n" +"Last-Translator: Goran Kliska \n" +"Language-Team: Croatian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-12-11 04:49+0000\n" +"X-Generator: Launchpad (build 16356)\n" + +#. module: web_shortcuts +#. openerp-web +#: code:addons/web_shortcuts/static/src/xml/web_shortcuts.xml:21 +#, python-format +msgid "Add / Remove Shortcut..." +msgstr "Dodaj / ukloni prečac..." diff --git a/addons/web_shortcuts/i18n/ro.po b/addons/web_shortcuts/i18n/ro.po new file mode 100644 index 00000000000..3be484612fa --- /dev/null +++ b/addons/web_shortcuts/i18n/ro.po @@ -0,0 +1,25 @@ +# Romanian translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-11-24 02:54+0000\n" +"PO-Revision-Date: 2012-12-10 07:32+0000\n" +"Last-Translator: Fekete Mihai \n" +"Language-Team: Romanian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-12-11 04:49+0000\n" +"X-Generator: Launchpad (build 16356)\n" + +#. module: web_shortcuts +#. openerp-web +#: code:addons/web_shortcuts/static/src/xml/web_shortcuts.xml:21 +#, python-format +msgid "Add / Remove Shortcut..." +msgstr "Adauga / Elimina Shortcut-ul..." diff --git a/addons/web_view_editor/i18n/hu.po b/addons/web_view_editor/i18n/hu.po new file mode 100644 index 00000000000..70d3f9e2178 --- /dev/null +++ b/addons/web_view_editor/i18n/hu.po @@ -0,0 +1,184 @@ +# Hungarian translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR , 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-11-30 18:13+0000\n" +"PO-Revision-Date: 2012-12-10 12:13+0000\n" +"Last-Translator: krnkris \n" +"Language-Team: Hungarian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-12-11 05:03+0000\n" +"X-Generator: Launchpad (build 16356)\n" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:164 +#, python-format +msgid "The following fields are invalid :" +msgstr "Az alábbi mezők nem megfelelőek:" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:63 +#, python-format +msgid "Create" +msgstr "Létrehoz" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:986 +#, python-format +msgid "New Field" +msgstr "Új mező" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:386 +#, python-format +msgid "Do you really wants to create an inherited view here?" +msgstr "Biztos, hogy létre akar hozni ide egy származtatott nézetet?" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:396 +#, python-format +msgid "Preview" +msgstr "Előnézet" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:183 +#, python-format +msgid "Do you really want to remove this view?" +msgstr "Biztos, hogy el akarja távolítani ezt a nézetet?" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:90 +#, python-format +msgid "Save" +msgstr "Mentés" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:393 +#, python-format +msgid "Select an element" +msgstr "Válasszon ki egy elemet" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:828 +#: code:addons/web_view_editor/static/src/js/view_editor.js:954 +#, python-format +msgid "Update" +msgstr "Frissítés" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:263 +#, python-format +msgid "Please select view in list :" +msgstr "Kérem válasszon nézetet a listában:" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:37 +#, python-format +msgid "Manage Views (%s)" +msgstr "Nézetek kezelése (%s)" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:13 +#, python-format +msgid "Manage Views" +msgstr "Nézetek kezelése" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:825 +#: code:addons/web_view_editor/static/src/js/view_editor.js:951 +#, python-format +msgid "Properties" +msgstr "Beállítások" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:64 +#, python-format +msgid "Edit" +msgstr "Szerkeszt" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:14 +#, python-format +msgid "Could not find current view declaration" +msgstr "Nem található az aktuális nézet leírás" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:382 +#, python-format +msgid "Inherited View" +msgstr "Származtatott nézet" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:65 +#, python-format +msgid "Remove" +msgstr "Eltávolítás" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:516 +#, python-format +msgid "Do you really want to remove this node?" +msgstr "Biztos, hogy el akarja távolítani ezt a csomópontot?" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:390 +#, python-format +msgid "Can't Update View" +msgstr "Nézet nem frissíthető" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:379 +#, python-format +msgid "View Editor %d - %s" +msgstr "Nézet szerkesztő %d - %s" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:112 +#: code:addons/web_view_editor/static/src/js/view_editor.js:846 +#: code:addons/web_view_editor/static/src/js/view_editor.js:974 +#, python-format +msgid "Cancel" +msgstr "Mégsem" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:66 +#: code:addons/web_view_editor/static/src/js/view_editor.js:413 +#, python-format +msgid "Close" +msgstr "Bezárás" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:88 +#, python-format +msgid "Create a view (%s)" +msgstr "Nézet létrehozása (%s)" diff --git a/addons/web_view_editor/i18n/pl.po b/addons/web_view_editor/i18n/pl.po new file mode 100644 index 00000000000..19a7911acb4 --- /dev/null +++ b/addons/web_view_editor/i18n/pl.po @@ -0,0 +1,184 @@ +# Polish translation for openerp-web +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openerp-web package. +# FIRST AUTHOR , 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openerp-web\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-11-30 18:13+0000\n" +"PO-Revision-Date: 2012-12-10 12:04+0000\n" +"Last-Translator: Grzegorz Grzelak (OpenGLOBE.pl) \n" +"Language-Team: Polish \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-12-11 05:03+0000\n" +"X-Generator: Launchpad (build 16356)\n" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:164 +#, python-format +msgid "The following fields are invalid :" +msgstr "Następujące pola są niedozwolone :" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:63 +#, python-format +msgid "Create" +msgstr "Utwórz" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:986 +#, python-format +msgid "New Field" +msgstr "Nowe pole" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:386 +#, python-format +msgid "Do you really wants to create an inherited view here?" +msgstr "Na pewno chcesz utworzyć dziedziczony widok tutaj ?" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:396 +#, python-format +msgid "Preview" +msgstr "Podgląd" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:183 +#, python-format +msgid "Do you really want to remove this view?" +msgstr "Chcesz usunąć ten widok?" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:90 +#, python-format +msgid "Save" +msgstr "Zapisz" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:393 +#, python-format +msgid "Select an element" +msgstr "Wybierz element" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:828 +#: code:addons/web_view_editor/static/src/js/view_editor.js:954 +#, python-format +msgid "Update" +msgstr "Aktualizuj" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:263 +#, python-format +msgid "Please select view in list :" +msgstr "Wybierz widok z listy :" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:37 +#, python-format +msgid "Manage Views (%s)" +msgstr "Zarządzaj widokami (%s)" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:13 +#, python-format +msgid "Manage Views" +msgstr "Widoki" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:825 +#: code:addons/web_view_editor/static/src/js/view_editor.js:951 +#, python-format +msgid "Properties" +msgstr "Właściwości" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:64 +#, python-format +msgid "Edit" +msgstr "Edytuj" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:14 +#, python-format +msgid "Could not find current view declaration" +msgstr "Nie można znaleźć deklaracji bieżącego widoku" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:382 +#, python-format +msgid "Inherited View" +msgstr "Widok dziedziczony" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:65 +#, python-format +msgid "Remove" +msgstr "Usuń" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:516 +#, python-format +msgid "Do you really want to remove this node?" +msgstr "Na pewno chcesz usunąć ten węzeł ?" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:390 +#, python-format +msgid "Can't Update View" +msgstr "Nie można zmodyfikować widoku" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:379 +#, python-format +msgid "View Editor %d - %s" +msgstr "Edytor widoków %d - %s" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:112 +#: code:addons/web_view_editor/static/src/js/view_editor.js:846 +#: code:addons/web_view_editor/static/src/js/view_editor.js:974 +#, python-format +msgid "Cancel" +msgstr "Anuluj" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:66 +#: code:addons/web_view_editor/static/src/js/view_editor.js:413 +#, python-format +msgid "Close" +msgstr "Zamknij" + +#. module: web_view_editor +#. openerp-web +#: code:addons/web_view_editor/static/src/js/view_editor.js:88 +#, python-format +msgid "Create a view (%s)" +msgstr "Utwórz widok (%s)"