From 6be32b621623df6e66168bae5437661c0e1be7b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Tue, 24 Apr 2012 12:19:47 +0200 Subject: [PATCH 001/104] Increase version number --- tryton/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tryton/version.py b/tryton/version.py index df205c95e7..82ae4e3646 100644 --- a/tryton/version.py +++ b/tryton/version.py @@ -1,6 +1,6 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. PACKAGE = "tryton" -VERSION = "2.4.0" +VERSION = "2.4.1" LICENSE = "GPL-3" WEBSITE = "http://www.tryton.org/" From 9ab579e74fbafa85fcac272f14359531f78c7601 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 27 Apr 2012 13:36:02 +0200 Subject: [PATCH 002/104] Check if GTK locale directory exists before copying it (transplanted from 7cebed85fb4e0fed16e31aec1c7dededd6bd5195) --- setup.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index 2bac21704e..e93598de9a 100644 --- a/setup.py +++ b/setup.py @@ -247,8 +247,9 @@ if os.name == 'nt': for lang in all_languages(): if os.path.isdir(os.path.join(dist_dir, 'share', 'locale', lang)): shutil.rmtree(os.path.join(dist_dir, 'share', 'locale', lang)) - shutil.copytree(os.path.join(gtk_dir, 'share', 'locale', lang), - os.path.join(dist_dir, 'share', 'locale', lang)) + if os.path.isdir(os.path.join(gtk_dir, 'share', 'locale', lang)): + shutil.copytree(os.path.join(gtk_dir, 'share', 'locale', lang), + os.path.join(dist_dir, 'share', 'locale', lang)) if os.path.isdir(os.path.join(dist_dir, 'share', 'themes', 'MS-Windows')): @@ -361,8 +362,9 @@ elif sys.platform == 'darwin': lang)): shutil.rmtree(os.path.join(resources_dir, 'share', 'locale', lang)) - shutil.copytree(os.path.join(gtk_dir, 'share', 'locale', lang), - os.path.join(resources_dir, 'share', 'locale', lang)) + if os.path.isdir(os.path.join(gtk_dir, 'share', 'locale', lang)): + shutil.copytree(os.path.join(gtk_dir, 'share', 'locale', lang), + os.path.join(resources_dir, 'share', 'locale', lang)) # fix pathes within shared libraries for library in chain( From 40ef53347080df95a043b50f25d23e6a8ef56521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 27 Apr 2012 13:36:22 +0200 Subject: [PATCH 003/104] Add missing Tryton icon to wizard dialog (transplanted from 60449e5bfc90aaecb868fe91151a6eca945ae41d) --- tryton/gui/window/wizard.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tryton/gui/window/wizard.py b/tryton/gui/window/wizard.py index 6b0c7de8ef..d57dc4d79b 100644 --- a/tryton/gui/window/wizard.py +++ b/tryton/gui/window/wizard.py @@ -13,7 +13,7 @@ from tryton.gui import Main from tryton.exceptions import TrytonServerError, TrytonServerUnavailable from tryton.gui.window.nomodal import NoModal from tryton.common.button import Button -from tryton.common import RPCExecute, RPCException +from tryton.common import RPCExecute, RPCException, TRYTON_ICON _ = gettext.gettext @@ -293,6 +293,7 @@ class WizardDialog(Wizard, NoModal): self.dia = gtk.Dialog(self.name, self.parent, gtk.DIALOG_DESTROY_WITH_PARENT) self.dia.set_position(gtk.WIN_POS_CENTER_ON_PARENT) + self.dia.set_icon(TRYTON_ICON) if hasattr(self.dia, 'set_deletable'): self.dia.set_deletable(False) self.dia.connect('close', self.close) From 732ed37303cdf7f792caca0e82344d3bba0a6f21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 27 Apr 2012 13:37:07 +0200 Subject: [PATCH 004/104] Stop wizard on TrytonServerError if there is no screen issue2569 review347001 (transplanted from f3efd9da7bb8145108e72d82d1d3afc41414fe45) --- tryton/gui/window/wizard.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tryton/gui/window/wizard.py b/tryton/gui/window/wizard.py index d57dc4d79b..b18a2e5666 100644 --- a/tryton/gui/window/wizard.py +++ b/tryton/gui/window/wizard.py @@ -82,8 +82,9 @@ class Wizard(object): result = RPCExecute('wizard', self.action, 'execute', self.session_id, data, self.state, context=ctx) except RPCException, rpc_exception: - if not isinstance(rpc_exception.exception, - TrytonServerError): + if (not isinstance(rpc_exception.exception, + TrytonServerError) + or not self.screen): self.state = self.end_state break From 3c324001a90cdb518a43790e9a116449b617bc74 Mon Sep 17 00:00:00 2001 From: Mathias Behrle Date: Sat, 28 Apr 2012 11:45:30 +0200 Subject: [PATCH 005/104] Adding missing argument for string conversion. issue2573 (transplanted from 1aa6aa450c48ba800a6a978a9f985dabd876b43e) --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index e93598de9a..589880ebf1 100644 --- a/setup.py +++ b/setup.py @@ -62,7 +62,7 @@ data_files = [ for lang in languages: data_files += [ ('share/locale/%s/LC_MESSAGES' % lang, - glob.glob('share/locale/%s/LC_MESSAGES/*.mo')), + glob.glob('share/locale/%s/LC_MESSAGES/*.mo' % lang)), ] if os.name == 'nt': From ec8a7f3e5122e832a6c370ed14cf68c6e8d376dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 5 May 2012 09:30:41 +0200 Subject: [PATCH 006/104] Improve hack of glitch in position of widgets in notebook By calling display when switching tabs, it loose the value of focused widget because value is reset before the focus-out is proceed. issue2571 review353001 (transplanted from 9c863c7e1626ca96677d04de19ab59957f5d7253) --- tryton/gui/window/view_form/view/form.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tryton/gui/window/view_form/view/form.py b/tryton/gui/window/view_form/view/form.py index 3ed74f4b18..5f5e69b584 100644 --- a/tryton/gui/window/view_form/view/form.py +++ b/tryton/gui/window/view_form/view/form.py @@ -30,6 +30,7 @@ class ViewForm(ParserView): for notebook in notebooks: def switch(notebook, page, page_num): if page_num not in display_done.setdefault(notebook, []): + notebook.grab_focus() display_done[notebook].append(page_num) self.display() notebook.connect('switch-page', switch) From 0fbc23f54a44d7080cbd933f8e40b90dd85a952d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 12 May 2012 16:48:17 +0200 Subject: [PATCH 007/104] Fix domain_inversion for comparison of datetime with None review359002 (transplanted from 49609073c798e7b019a5d2dc28dcebb627ad10dd) --- tryton/common/domain_inversion.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/tryton/common/domain_inversion.py b/tryton/common/domain_inversion.py index 5b7a125077..9c71e0d66f 100644 --- a/tryton/common/domain_inversion.py +++ b/tryton/common/domain_inversion.py @@ -3,6 +3,7 @@ import operator import types +import datetime def in_(a, b): @@ -56,7 +57,18 @@ def eval_leaf(part, context, boolop=operator.and_): # We should consider that other domain inversion will set a correct # value to this field return True - return OPERATORS[operand](context[field], value) + context_field = context[field] + if isinstance(context_field, datetime.date) and not value: + if isinstance(context_field, datetime.datetime): + value = datetime.datetime.min + else: + value = datetime.date.min + if isinstance(value, datetime.date) and not context_field: + if isinstance(value, datetime.datetime): + context_field = datetime.datetime.min + else: + context_field = datetime.date.min + return OPERATORS[operand](context_field, value) def inverse_leaf(domain): @@ -422,6 +434,15 @@ def test_evaldomain(): assert eval_domain(domain, {'x': 6}) assert not eval_domain(domain, {'x': 4}) + domain = [['x', '>', None]] + assert eval_domain(domain, {'x': datetime.date.today()}) + assert eval_domain(domain, {'x': datetime.datetime.now()}) + + domain = [['x', '<', datetime.date.today()]] + assert eval_domain(domain, {'x': None}) + domain = [['x', '<', datetime.datetime.now()]] + assert eval_domain(domain, {'x': None}) + domain = [['x', 'in', [3, 5]]] assert eval_domain(domain, {'x': 3}) assert not eval_domain(domain, {'x': 4}) From fc0fc33900c52ee8399384fe211007adedbf094f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 12 May 2012 17:26:43 +0200 Subject: [PATCH 008/104] Set None as value for empty reference field issue2599 (transplanted from 11a133823558e92f8428d6405a5ea5bbbbfe2b8c) --- tryton/gui/window/view_form/view/form_gtk/reference.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tryton/gui/window/view_form/view/form_gtk/reference.py b/tryton/gui/window/view_form/view/form_gtk/reference.py index a610486c5d..651982afc6 100644 --- a/tryton/gui/window/view_form/view/form_gtk/reference.py +++ b/tryton/gui/window/view_form/view/form_gtk/reference.py @@ -116,7 +116,10 @@ class Reference(Many2One): def set_value(self, record, field): if not self.get_model(): value = self.wid_text.get_text() - return field.set_client(record, ('', value)) + if not value: + field.set_client(record, None) + else: + return field.set_client(record, ('', value)) return super(Reference, self).set_value(record, field) def set_text(self, value): From 9ed60d92adc9175ae8ae1a8cd92247a6478932bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 14 May 2012 00:09:17 +0200 Subject: [PATCH 009/104] Activate form buttons only when they are sensitive issue2601 (transplanted from 4d6c095c984165c19ae61a45a7a82d4c706e4693) --- tryton/gui/window/form.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tryton/gui/window/form.py b/tryton/gui/window/form.py index fe4012f979..e28025ed15 100644 --- a/tryton/gui/window/form.py +++ b/tryton/gui/window/form.py @@ -395,19 +395,24 @@ class Form(SignalEvent, TabContent): return True def sig_action(self, widget): - self.buttons['action'].props.active = True + if self.buttons['action'].props.sensitive: + self.buttons['action'].props.active = True def sig_print(self, widget): - self.buttons['print'].props.active = True + if self.buttons['print'].props.sensitive: + self.buttons['print'].props.active = True def sig_print_open(self, widget): - self.buttons['open'].props.active = True + if self.buttons['open'].props.sensitive: + self.buttons['open'].props.active = True def sig_print_email(self, widget): - self.buttons['email'].props.active = True + if self.buttons['email'].props.sensitive: + self.buttons['email'].props.active = True def sig_relate(self, widget): - self.buttons['relate'].props.active = True + if self.buttons['relate'].props.sensitive: + self.buttons['relate'].props.active = True def sig_search(self, widget): search_container = self.screen.screen_container From c22b7eb52a67a8437a0f814d955a3b6ed7e0dbd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 19 May 2012 15:37:35 +0200 Subject: [PATCH 010/104] Use one record pool per group Having a global pool has some side effect between tabs. So it is better to get read optimization only inside one tab by using a pool on the main group. issue2582 review370002 (transplanted from c025b45e7c70bc262ddab7bb680b4260725da966) --- tryton/gui/window/view_form/model/group.py | 6 ++++++ tryton/gui/window/view_form/model/record.py | 23 ++++++++++++--------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/tryton/gui/window/view_form/model/group.py b/tryton/gui/window/view_form/model/group.py index dae254d2c2..089882af9b 100644 --- a/tryton/gui/window/view_form/model/group.py +++ b/tryton/gui/window/view_form/model/group.py @@ -1,5 +1,10 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. +try: + from weakref import WeakSet +except ImportError: + from weakrefset import WeakSet + from record import Record from field import Field, O2MField from tryton.signal_event import SignalEvent @@ -36,6 +41,7 @@ class Group(SignalEvent, list): self.__id2record = {} self.__field_childs = None self.exclude_field = None + self.pool = WeakSet() @property def domain(self): diff --git a/tryton/gui/window/view_form/model/record.py b/tryton/gui/window/view_form/model/record.py index 874615ed12..cf3458ce5a 100644 --- a/tryton/gui/window/view_form/model/record.py +++ b/tryton/gui/window/view_form/model/record.py @@ -1,10 +1,5 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. -try: - from weakref import WeakSet -except ImportError: - from weakrefset import WeakSet -from collections import defaultdict import tryton.rpc as rpc from tryton.signal_event import SignalEvent import tryton.common as common @@ -13,8 +8,6 @@ import field as fields from functools import reduce from tryton.common import RPCExecute, RPCException -POOL = defaultdict(WeakSet) - class Record(SignalEvent): @@ -42,7 +35,7 @@ class Record(SignalEvent): self.autocompletion = {} self.exception = False self.destroyed = False - POOL[model_name].add(self) + self.pool.add(self) def __getitem__(self, name): if name not in self._loaded and self.id >= 0: @@ -74,7 +67,7 @@ class Record(SignalEvent): n += 1 record_context = self.context_get() if loading == 'eager' and len(id2record) < 80: - for record in POOL[self.model_name]: + for record in self.pool: if (name not in record._loaded and record.id >= 0 and record.id not in id2record @@ -131,6 +124,14 @@ class Record(SignalEvent): def modified(self): return bool(self.modified_fields) + @property + def pool(self): + group = self.group + while (group.parent is not None + and group.parent.model_name == self.model_name): + group = group.parent + return group.pool + @property def parent(self): return self.group.parent @@ -576,6 +577,8 @@ class Record(SignalEvent): return self.attachment_count def destroy(self): + # Get reference to pool before unref group + pool = self.pool for v in self.value.itervalues(): if hasattr(v, 'destroy'): v.destroy() @@ -584,4 +587,4 @@ class Record(SignalEvent): self.value = None self.next = None self.destroyed = True - POOL[self.model_name].remove(self) + pool.remove(self) From f2160ea20ce6bcc85e848fc0147c2546688bc26a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 19 May 2012 15:40:27 +0200 Subject: [PATCH 011/104] Retreive current record after saving issue371001 (transplanted from 8a902d5ad123085bd9c2dda0203483295ee0a386) --- tryton/gui/window/view_form/view/form.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tryton/gui/window/view_form/view/form.py b/tryton/gui/window/view_form/view/form.py index 5f5e69b584..af75da275d 100644 --- a/tryton/gui/window/view_form/view/form.py +++ b/tryton/gui/window/view_form/view/form.py @@ -166,8 +166,8 @@ class ViewForm(ParserView): focus_widget.grab_focus() def button_clicked(self, widget): - record = self.screen.current_record record_id = self.screen.save_current() + record = self.screen.current_record attrs = widget.attrs if record_id: if not attrs.get('confirm', False) or \ From 7acf30e7557416a4e9286783b7e00e5c35f762df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 21 May 2012 12:40:10 +0200 Subject: [PATCH 012/104] Set record modified when setting list on O2M issue2607 review375001 (transplanted from 209e5395d4076e182cf454c0ab868e02869c361e) --- tryton/gui/window/view_form/model/field.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tryton/gui/window/view_form/model/field.py b/tryton/gui/window/view_form/model/field.py index b918d360d0..af88c6bffa 100644 --- a/tryton/gui/window/view_form/model/field.py +++ b/tryton/gui/window/view_form/model/field.py @@ -664,7 +664,10 @@ class O2MField(CharField): def set_on_change(self, record, value): self._set_default_value(record) if isinstance(value, (list, tuple)): - return self.set(record, value, modified=False) + self.set(record, value, modified=False) + record.modified_fields.setdefault(self.name) + record.signal('record-modified') + return True if value and (value.get('add') or value.get('update')): context = self.context_get(record) From cdbe6850f4a91d4fefcca34a219a221dcbd6bdb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 8 Jun 2012 19:22:05 +0200 Subject: [PATCH 013/104] Manage error when saving record from Many2One The save of the current record must be done at the same level as the validation. issue2614 review393001 (transplanted from d284eb9d3405d49c09eb5e59d2bac4ce9b4b27bc) --- tryton/gui/window/view_form/view/form_gtk/many2one.py | 11 ++++------- tryton/gui/window/view_form/view/list_gtk/parser.py | 9 +++------ tryton/gui/window/win_form.py | 7 ++++++- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/tryton/gui/window/view_form/view/form_gtk/many2one.py b/tryton/gui/window/view_form/view/form_gtk/many2one.py index a8448d2439..82b647175a 100644 --- a/tryton/gui/window/view_form/view/form_gtk/many2one.py +++ b/tryton/gui/window/view_form/view/form_gtk/many2one.py @@ -177,13 +177,13 @@ class Many2One(WidgetInterface): screen = self.get_screen() def callback(result): - if result and screen.save_current(): + if result: self.field.set_client(self.record, self.value_from_id(screen.current_record.id, screen.current_record.rec_name())) self.display(self.record, self.field) self.focus_out = True - WinForm(screen, callback, new=True) + WinForm(screen, callback, new=True, save_current=True) def sig_edit(self, widget): if not self.focus_out or not self.field: @@ -198,18 +198,15 @@ class Many2One(WidgetInterface): screen.load([self.id_from_value(self.field.get(self.record))]) def callback(result): - if result and screen.save_current(): + if result: self.field.set_client(self.record, self.value_from_id(screen.current_record.id, screen.current_record.rec_name()), force_change=True) - elif result: - screen.display() - return WinForm(screen, callback) self.focus_out = True self.display(self.record, self.field) self.changed = True - WinForm(screen, callback) + WinForm(screen, callback, save_current=True) return elif model and not self._readonly: domain = self.field.domain_get(self.record) diff --git a/tryton/gui/window/view_form/view/list_gtk/parser.py b/tryton/gui/window/view_form/view/list_gtk/parser.py index 335db528c3..b67cd6f30b 100644 --- a/tryton/gui/window/view_form/view/list_gtk/parser.py +++ b/tryton/gui/window/view_form/view/list_gtk/parser.py @@ -627,20 +627,17 @@ class M2O(Char): mode=['form']) def open_callback(result): - if result and screen.save_current(): + if result: value = (screen.current_record.id, screen.current_record.rec_name()) field.set_client(record, value, force_change=True) - elif result: - screen.display() - return WinForm(screen, open_callback) if callback: callback() if obj_id: screen.load([obj_id]) - WinForm(screen, open_callback) + WinForm(screen, open_callback, save_current=True) else: - WinForm(screen, open_callback, new=True) + WinForm(screen, open_callback, new=True, save_current=True) def search_remote(self, record, relation, ids=None, domain=None, context=None, callback=None): diff --git a/tryton/gui/window/win_form.py b/tryton/gui/window/win_form.py index 9b27b928b9..f6536583d7 100644 --- a/tryton/gui/window/win_form.py +++ b/tryton/gui/window/win_form.py @@ -16,12 +16,14 @@ class WinForm(NoModal): "Form window" def __init__(self, screen, callback, view_type='form', - new=False, many=False, domain=None, context=None): + new=False, many=False, domain=None, context=None, + save_current=False): NoModal.__init__(self) self.screen = screen self.callback = callback self.domain = domain self.context = context + self.save_current = save_current self.prev_view = self.screen.current_view self.screen.screen_container.alternate_view = True switch_new = False @@ -366,6 +368,9 @@ class WinForm(NoModal): and self.screen.current_record is not None): validate = self.screen.current_record.validate( self.screen.current_view.get_fields()) + if validate and self.save_current: + if not self.screen.save_current(): + validate = False if not validate: self.screen.set_cursor() self.screen.display() From 514f085711928dda1bfa6b694847b0c6aa08a988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 8 Jun 2012 21:21:33 +0200 Subject: [PATCH 014/104] Update context before it could be overriden by field name (transplanted from 62f249f7fd8511a3b7dcd4c008dadb163375c182) --- tryton/gui/window/view_form/model/record.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tryton/gui/window/view_form/model/record.py b/tryton/gui/window/view_form/model/record.py index cf3458ce5a..4bbc56bb81 100644 --- a/tryton/gui/window/view_form/model/record.py +++ b/tryton/gui/window/view_form/model/record.py @@ -448,10 +448,10 @@ class Record(SignalEvent): self._check_load() ctx = rpc.CONTEXT.copy() ctx['context'] = ctx.copy() + ctx['context'].update(self.context_get()) for name, field in self.group.fields.items(): ctx[name] = field.get_eval(self, check_load=check_load) - ctx['context'].update(self.context_get()) ctx['active_id'] = self.id ctx['id'] = self.id # Force local id ctx['_user'] = rpc._USER From 1bb9d7d370c2bb63e1f073a10c2e6e7efe2342ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 20 Jun 2012 10:54:07 +0200 Subject: [PATCH 015/104] Don't fail on unknow font family issue2617 review399004 (transplanted from 03432c9a3c3db53087a8e83c5ca0da58069ab62a) --- tryton/gui/window/view_form/view/form_gtk/richtextbox.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tryton/gui/window/view_form/view/form_gtk/richtextbox.py b/tryton/gui/window/view_form/view/form_gtk/richtextbox.py index 85c68d5e47..8325f6a5dd 100644 --- a/tryton/gui/window/view_form/view/form_gtk/richtextbox.py +++ b/tryton/gui/window/view_form/view/form_gtk/richtextbox.py @@ -145,8 +145,10 @@ class RichTextBox(TextBox): } self.tools['size'].child.set_active(self.sizes.index( self.current_font_prop['size'])) - self.tools['font_family'].child.set_active(self.families.index( - self.current_font_prop['font_family'])) + font_family = self.current_font_prop['font_family'] + if font_family in self.families: + self.tools['font_family'].child.set_active(self.families.index( + font_family)) self.start_tags = {} self.end_tags = {} # Connect events From 38a0046efb07064d7758f8a9155e0a4c4b106d15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 22 Jun 2012 17:51:38 +0200 Subject: [PATCH 016/104] Don't reload records when sorting if there are unsaved records issue2664 review403001 (transplanted from 696e37e43e038166ccf84050272397070e54251a) --- tryton/gui/window/view_form/view/list_gtk/parser.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tryton/gui/window/view_form/view/list_gtk/parser.py b/tryton/gui/window/view_form/view/list_gtk/parser.py index b67cd6f30b..a423780cbb 100644 --- a/tryton/gui/window/view_form/view/list_gtk/parser.py +++ b/tryton/gui/window/view_form/view/list_gtk/parser.py @@ -64,13 +64,11 @@ def sort_model(column, treeview, screen): store = treeview.get_model() unsaved_records = [x for x in store.group if x.id < 0] search_string = screen.screen_container.get_text() or None - if screen.search_count == len(store): + if screen.search_count == len(store) or unsaved_records: ids = screen.search_filter(search_string=search_string, only_ids=True) store.sort(ids) else: screen.search_filter(search_string=search_string) - for record in unsaved_records: - store.group.append(record) def realized(func): From 1880b0ec6334ad065ffd6598026b74808058f55f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 22 Jun 2012 17:53:35 +0200 Subject: [PATCH 017/104] Fix name of fields loaded from predefined export issue2648 review404001 (transplanted from 481bf839727a0a1afff30fb963e46afa368b4306) --- tryton/gui/window/win_export.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tryton/gui/window/win_export.py b/tryton/gui/window/win_export.py index 1ea435db93..949d8d1ed9 100644 --- a/tryton/gui/window/win_export.py +++ b/tryton/gui/window/win_export.py @@ -242,7 +242,8 @@ class WinExport(object): child = self.model1.iter_children(iter) if self.model1.get_value(child, 0) is None: prefix_field = self.model1.get_value(iter, 1) - name, model = self.fields[prefix_field] + _, model = self.fields[prefix_field] + name = self.fields_data[prefix_field]['string'] self.model_populate(self._get_fields(model), iter, prefix_field + '/', name + '/') self.model1.remove(child) From 1702d2f4f180557912bacd9a9caf6ad130d29964 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 22 Jun 2012 17:56:23 +0200 Subject: [PATCH 018/104] Reset rec_name when setting just id of M2O or Reference issue2641 review406001 (transplanted from 6e702bc1759212191c72dfb480378c652b08886b) --- tryton/gui/window/view_form/model/field.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tryton/gui/window/view_form/model/field.py b/tryton/gui/window/view_form/model/field.py index af88c6bffa..85e06d194a 100644 --- a/tryton/gui/window/view_form/model/field.py +++ b/tryton/gui/window/view_form/model/field.py @@ -389,7 +389,9 @@ class M2OField(CharField): def set_client(self, record, value, force_change=False): if isinstance(value, (tuple, list)): value, rec_name = value - record.value[self.name + '.rec_name'] = rec_name + else: + rec_name = '' + record.value[self.name + '.rec_name'] = rec_name super(M2OField, self).set_client(record, value, force_change=force_change) @@ -806,7 +808,9 @@ class ReferenceField(CharField): ref_model, ref_id = value if isinstance(ref_id, (tuple, list)): ref_id, rec_name = ref_id - record.value[self.name + '.rec_name'] = rec_name + else: + rec_name = '' + record.value[self.name + '.rec_name'] = rec_name value = (ref_model, ref_id) super(ReferenceField, self).set_client(record, value, force_change=force_change) From 6f1cd98f759495de2081134b48d1b4d057b4a6aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 22 Jun 2012 17:58:23 +0200 Subject: [PATCH 019/104] Manage focus out event when adding in O2M issue2640 review404002 (transplanted from ac56de2924712b7b0e36e7a788e199b237fe7b7d) --- tryton/gui/window/view_form/view/form_gtk/one2many.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tryton/gui/window/view_form/view/form_gtk/one2many.py b/tryton/gui/window/view_form/view/form_gtk/one2many.py index 3a4db78586..bf3ca67cd4 100644 --- a/tryton/gui/window/view_form/view/form_gtk/one2many.py +++ b/tryton/gui/window/view_form/view/form_gtk/one2many.py @@ -32,6 +32,7 @@ class One2Many(WidgetInterface): tooltips = common.Tooltips() + self.focus_out = True if attrs.get('add_remove'): self.wid_text = gtk.Entry() @@ -299,6 +300,8 @@ class One2Many(WidgetInterface): self._sig_add() def _sig_add(self, *args): + if not self.focus_out: + return self.view.set_value() domain = self.field.domain_get(self.record) context = self.field.context_get(self.record) @@ -306,6 +309,7 @@ class One2Many(WidgetInterface): domain.extend(self.record.expr_eval(self.attrs.get('add_remove'))) removed_ids = self.field.get_removed_ids(self.record) + self.focus_out = False try: if self.wid_text.get_text(): dom = [('rec_name', 'ilike', @@ -316,9 +320,11 @@ class One2Many(WidgetInterface): ids = RPCExecute('model', self.attrs['relation'], 'search', dom, 0, CONFIG['client.limit'], None, context=context) except RPCException: + self.focus_out = True return False def callback(result): + self.focus_out = True if result: ids = [x[0] for x in result] self.screen.load(ids, modified=True) From 1fee6f3058f77d740f259737ad7d3df7ae9371a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 28 Jun 2012 15:11:17 +0200 Subject: [PATCH 020/104] set_value of M2O must simulate a focus-out in case it did not yet occur issue2624 review408001 (transplanted from a07d90df4d9e6b98778260ec684b6f1317a4cb8b) --- tryton/gui/window/view_form/view/form_gtk/many2one.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tryton/gui/window/view_form/view/form_gtk/many2one.py b/tryton/gui/window/view_form/view/form_gtk/many2one.py index 82b647175a..1b70515282 100644 --- a/tryton/gui/window/view_form/view/form_gtk/many2one.py +++ b/tryton/gui/window/view_form/view/form_gtk/many2one.py @@ -106,7 +106,7 @@ class Many2One(WidgetInterface): def id_from_value(value): return value - def sig_activate(self, widget, event=None, key_press=False): + def sig_activate(self, widget=None, event=None, key_press=False): if not self.focus_out or not self.field: return self.changed = False @@ -115,7 +115,7 @@ class Many2One(WidgetInterface): self.focus_out = False if model and not self.has_target(value): - if not key_press and not event: + if not key_press and not event and widget: widget.emit_stop_by_name('activate') if (not self._readonly and (self.wid_text.get_text() @@ -276,7 +276,8 @@ class Many2One(WidgetInterface): return False def set_value(self, record, field): - pass # No update of the model, the model is updated in real time ! + # Simulate a focus-out + self.sig_activate() def set_text(self, value): if not value: From 9b1441447c0df9e571caffe4d20390b876ebbc37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Tue, 10 Jul 2012 13:09:27 +0200 Subject: [PATCH 021/104] Delay the save of record and popup in treeview The save and popup must be delayed because if it take too much time, the rpc call will process events before the main event is finished. issue2621 review412001 (transplanted from f2b19cdf0cf13c91d6b7f261a9a7838872f0e589) --- .../view_form/view/form_gtk/many2one.py | 4 +- tryton/gui/window/view_form/view/list.py | 84 +++++++++++-------- 2 files changed, 51 insertions(+), 37 deletions(-) diff --git a/tryton/gui/window/view_form/view/form_gtk/many2one.py b/tryton/gui/window/view_form/view/form_gtk/many2one.py index 1b70515282..906b597018 100644 --- a/tryton/gui/window/view_form/view/form_gtk/many2one.py +++ b/tryton/gui/window/view_form/view/form_gtk/many2one.py @@ -308,5 +308,7 @@ class Many2One(WidgetInterface): def _populate_popup(self, widget, menu): value = self.field.get(self.record) if self.has_target(value): - populate(menu, self.get_model(), self.id_from_value(value)) + # Delay filling of popup as it can take time + gobject.idle_add(populate, menu, self.get_model(), + self.id_from_value(value)) return True diff --git a/tryton/gui/window/view_form/view/list.py b/tryton/gui/window/view_form/view/list.py index 6233da2051..285bd6c00a 100644 --- a/tryton/gui/window/view_form/view/list.py +++ b/tryton/gui/window/view_form/view/list.py @@ -549,38 +549,41 @@ class ViewList(ParserView): return False group = model.group record = group[path[0][0]] - menu = gtk.Menu() - copy_item = gtk.ImageMenuItem('gtk-copy') - copy_item.connect('activate', lambda x: self.on_copy()) - menu.append(copy_item) - # Don't activate actions if parent is modified - parent = record.parent if record else None - while parent: - if parent.modified: - record = None - break - parent = parent.parent - populate(menu, group.model_name, record) - for col in self.widget_tree.get_columns(): - if not col.get_visible() or not col.name: - continue - field = group.fields[col.name] - model = None - if field.attrs['type'] == 'many2one': - model = field.attrs['relation'] - record_id = field.get(record) - elif field.attrs['type'] == 'reference': - value = field.get(record) - if value: - model, record_id = value.split(',') - record_id = int(record_id) - if not model: - continue - label = field.attrs['string'] - populate(menu, model, record_id, title=label) - menu.show_all() menu.popup(None, None, None, event.button, event.time) + + def pop(menu, group, record): + copy_item = gtk.ImageMenuItem('gtk-copy') + copy_item.connect('activate', lambda x: self.on_copy()) + menu.append(copy_item) + # Don't activate actions if parent is modified + parent = record.parent if record else None + while parent: + if parent.modified: + record = None + break + parent = parent.parent + populate(menu, group.model_name, record) + for col in self.widget_tree.get_columns(): + if not col.get_visible() or not col.name: + continue + field = group.fields[col.name] + model = None + if field.attrs['type'] == 'many2one': + model = field.attrs['relation'] + record_id = field.get(record) + elif field.attrs['type'] == 'reference': + value = field.get(record) + if value: + model, record_id = value.split(',') + record_id = int(record_id) + if not model: + continue + label = field.attrs['string'] + populate(menu, model, record_id, title=label) + menu.show_all() + # Delay filling of popup as it can take time + gobject.idle_add(pop, menu, group, record) elif event.button == 2: event.button = 1 event.state |= gtk.gdk.MOD1_MASK @@ -707,12 +710,21 @@ class ViewList(ParserView): and self.widget_tree.editable \ and not self.screen.parent \ and previous_record != self.screen.current_record: - if previous_record and \ - not (previous_record.validate(self.get_fields()) - and previous_record.save()): - self.screen.current_record = previous_record - self.set_cursor() - return True + if previous_record: + def go_previous(): + self.screen.current_record = previous_record + self.set_cursor() + + def save(): + if not previous_record.destroyed: + if not previous_record.save(): + go_previous() + + if not previous_record.validate(self.get_fields()): + go_previous() + return True + # Delay the save to let GTK process the current event + gobject.idle_add(save) self.update_children() def set_value(self): From f55021fd57747bc2fb35ceda484a06f73587c638 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 19 Jul 2012 09:45:58 +0200 Subject: [PATCH 022/104] get_toplevel_window should return only gtk.WINDOW_TOPLEVEL issue2707 review410003 (transplanted from a10ed833b61119c2c169356a493023f892e2526b) --- tryton/common/common.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tryton/common/common.py b/tryton/common/common.py index 815f2ee685..0f4dd0c5d4 100644 --- a/tryton/common/common.py +++ b/tryton/common/common.py @@ -255,7 +255,8 @@ def request_server(server_widget): def get_toplevel_window(): windows = [x for x in gtk.window_list_toplevels() - if x.window and x.props.visible] + if x.window and x.props.visible + and x.get_window_type() == gtk.WINDOW_TOPLEVEL] trans2windows = dict((x.get_transient_for(), x) for x in windows) for window in set(windows) - set(trans2windows.iterkeys()): return window From a6a099be17de176e353a45f2d37bffef8d262398 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 25 Jul 2012 20:31:56 +0200 Subject: [PATCH 023/104] Fix test on legend graph attribute review421001 (transplanted from 883480d210a7b3361afc69df26e766c62a4e76dc) --- tryton/gui/window/view_form/view/graph_gtk/graph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tryton/gui/window/view_form/view/graph_gtk/graph.py b/tryton/gui/window/view_form/view/graph_gtk/graph.py index c90173ef85..77c464c9ff 100644 --- a/tryton/gui/window/view_form/view/graph_gtk/graph.py +++ b/tryton/gui/window/view_form/view/graph_gtk/graph.py @@ -276,7 +276,7 @@ class Graph(gtk.DrawingArea): cr.restore() def drawLegend(self, cr, width, height): - if not self.attrs.get('legend', True): + if not int(self.attrs.get('legend', 1)): return padding = 4 From c50ddfe8bc2d387a02ac4716050dbf9183f68208 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bonicoli Date: Tue, 14 Aug 2012 17:52:52 +0200 Subject: [PATCH 024/104] Use repr for faultCode in Fault.__repr__ issue2742 review467002 (transplanted from 56c1da6e515b54c3148c772ccc950fd051286de3) --- tryton/jsonrpc.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tryton/jsonrpc.py b/tryton/jsonrpc.py index 0b1daa1701..59d7eccb0b 100644 --- a/tryton/jsonrpc.py +++ b/tryton/jsonrpc.py @@ -32,6 +32,11 @@ class Fault(xmlrpclib.Fault): super(Fault, self).__init__(faultCode, faultString, **extra) self.args = faultString + def __repr__(self): + return ( + "" % + (repr(self.faultCode), repr(self.faultString)) + ) class ProtocolError(xmlrpclib.ProtocolError): pass From f7e77afa19f72973c67aefb4cc43ee205dcae2a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 16 Aug 2012 14:57:00 +0200 Subject: [PATCH 025/104] Don't use Screen.save_current in button_clicked Screen.save_current force the reload of the record before the call to the Button RPC. So this could generate a race condition between the button call and the reload of the record. issue2739 review460001 (transplanted from dade8f50910b320f00618418a409f22f169b147a) --- tryton/gui/window/view_form/view/form.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tryton/gui/window/view_form/view/form.py b/tryton/gui/window/view_form/view/form.py index af75da275d..887c7af20d 100644 --- a/tryton/gui/window/view_form/view/form.py +++ b/tryton/gui/window/view_form/view/form.py @@ -166,10 +166,12 @@ class ViewForm(ParserView): focus_widget.grab_focus() def button_clicked(self, widget): - record_id = self.screen.save_current() record = self.screen.current_record attrs = widget.attrs - if record_id: + fields = self.get_fields() + if record.validate(fields): + # Don't reload as it will be done after the RPC call + record.save(force_reload=False) if not attrs.get('confirm', False) or \ common.sur(attrs['confirm']): button_type = attrs.get('type', 'object') @@ -177,7 +179,7 @@ class ViewForm(ParserView): if button_type == 'object': try: RPCExecute('model', self.screen.model_name, - attrs['name'], [record_id], context=context) + attrs['name'], [record.id], context=context) except RPCException: pass elif button_type == 'action': @@ -191,8 +193,8 @@ class ViewForm(ParserView): if action_id: Action.execute(action_id, { 'model': self.screen.model_name, - 'id': record_id, - 'ids': [record_id], + 'id': record.id, + 'ids': [record.id], }, context=context) else: raise Exception('Unallowed button type') From a7a8816146d08137f171fceded620aeef3b0b080 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 16 Aug 2012 16:00:21 +0200 Subject: [PATCH 026/104] Fix wrong sorting to get lazy first review461001 (transplanted from 32492bf6244d7f1e93570e3ad8136488e1b981e4) --- tryton/gui/window/view_form/view/form.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tryton/gui/window/view_form/view/form.py b/tryton/gui/window/view_form/view/form.py index 887c7af20d..ee09ff6163 100644 --- a/tryton/gui/window/view_form/view/form.py +++ b/tryton/gui/window/view_form/view/form.py @@ -122,7 +122,7 @@ class ViewForm(ParserView): # Get first the lazy one to reduce number of requests fields = [(name, field.attrs.get('loading', 'eager')) for name, field in record.group.fields.iteritems()] - fields.sort(key=operator.itemgetter(1)) + fields.sort(key=operator.itemgetter(1), reverse=True) for field, _ in fields: record[field].get(record, check_load=False) for name, widgets in self.widgets.iteritems(): From 2dd3e1c5f358953083a0d1bb434494889a7dbeb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 25 Aug 2012 15:42:53 +0200 Subject: [PATCH 027/104] Fix missing on_change_with initialisation for non-grouped calls issue2759 review481004 (transplanted from 38340c974fc513f3e3800fa8c1a6db35e3c1d327) --- tryton/gui/window/view_form/model/record.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tryton/gui/window/view_form/model/record.py b/tryton/gui/window/view_form/model/record.py index 4bbc56bb81..bf54c2f257 100644 --- a/tryton/gui/window/view_form/model/record.py +++ b/tryton/gui/window/view_form/model/record.py @@ -534,6 +534,8 @@ class Record(SignalEvent): for fieldname, value in result.items(): self.group.fields[fieldname].set_on_change(self, value) for fieldname in later: + on_change_with = self.group.fields[fieldname].attrs.get( + 'on_change_with') values = self._get_on_change_args(on_change_with) try: result = RPCExecute('model', self.model_name, From 8768ef541667a199efd9696eb4f308d98a6ad1b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 1 Sep 2012 18:50:12 +0200 Subject: [PATCH 028/104] Prepare release 2.4.1 --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 3ec662b48c..de75c733f2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Version 2.4.1 - 2012-09-01 +* Bug fixes (see mercurial logs for details) + Version 2.4.0 - 2012-04-23 * Bug fixes (see mercurial logs for details) * Remove workflow From 8626276ec4c2069bf43032349957bef40ac3d7f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 1 Sep 2012 18:50:13 +0200 Subject: [PATCH 029/104] Added tag 2.4.1 for changeset 03f86ced937d --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 0bfa039388..2661a07fe0 100644 --- a/.hgtags +++ b/.hgtags @@ -6,3 +6,4 @@ de84c1443d4bad908eabb99c381578b988c2117f 1.4.0 1876747b3f963ba6011b4bcf6968e681624c6e24 2.0.0 5364f8ca6cd2cccfb5f7f39c69bb20bae2b5f495 2.2.0 fe15da23f7333e0e90d2693aadddf6bed53ed602 2.4.0 +03f86ced937d03bada5193526bf78655c0c489a5 2.4.1 From 48e3a11d57c357a0bbe1dc589e8630c97b68c253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 1 Sep 2012 18:52:28 +0200 Subject: [PATCH 030/104] Increase version number --- tryton/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tryton/version.py b/tryton/version.py index 82ae4e3646..aa797078eb 100644 --- a/tryton/version.py +++ b/tryton/version.py @@ -1,6 +1,6 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. PACKAGE = "tryton" -VERSION = "2.4.1" +VERSION = "2.4.2" LICENSE = "GPL-3" WEBSITE = "http://www.tryton.org/" From 6650a4c01162b646ab2658e0811f95bf2fe959fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 10 Sep 2012 13:24:02 +0200 Subject: [PATCH 031/104] Replace Window.get_window_type by the property Window.get_window_type is only available since PyGTK 2.22 issue2743 review501008 (transplanted from b035296eecf53b535d420355c002ee7cc3e7c2f3) --- tryton/common/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tryton/common/common.py b/tryton/common/common.py index 0f4dd0c5d4..fd3c32fa61 100644 --- a/tryton/common/common.py +++ b/tryton/common/common.py @@ -256,7 +256,7 @@ def request_server(server_widget): def get_toplevel_window(): windows = [x for x in gtk.window_list_toplevels() if x.window and x.props.visible - and x.get_window_type() == gtk.WINDOW_TOPLEVEL] + and x.props.type == gtk.WINDOW_TOPLEVEL] trans2windows = dict((x.get_transient_for(), x) for x in windows) for window in set(windows) - set(trans2windows.iterkeys()): return window From f387e4d02fde8641f4076a531258c12ae595d9fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Tue, 18 Sep 2012 11:18:49 +0200 Subject: [PATCH 032/104] Fix closing tag in richtext box review515002 (transplanted from 92a7806efe2da14abb429f86517176be9dbe7873) --- .../view_form/view/form_gtk/richtextbox.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tryton/gui/window/view_form/view/form_gtk/richtextbox.py b/tryton/gui/window/view_form/view/form_gtk/richtextbox.py index 8325f6a5dd..6ccd9460dc 100644 --- a/tryton/gui/window/view_form/view/form_gtk/richtextbox.py +++ b/tryton/gui/window/view_form/view/form_gtk/richtextbox.py @@ -31,16 +31,16 @@ def formalize_text_markup(text): occurrence = 0 i += 1 if stack: - span_close = '<\\\span>' * stack[-1][2] + span_close = '' * stack[-1][2] data = re.search('^' + span_close, text[i:]) if data and stack[-1][2] > 1: span = stack.pop() - text = re.sub(span_close, text[:i] + '<\\span>', text[i:], 1) + text = re.sub(span_close, text[:i] + '', text[i:], 1) text = text[:span[0]] + re.sub('> tag lines = text.split('\n') align = "left" @@ -53,12 +53,12 @@ def formalize_text_markup(text): else: break actual_align = "

" % align - if re.search("(<\\\p>|<\\p>)", lines[i]): + if re.search("

", lines[i]): align = 'left' - lines[i] = re.sub('(<\\\p>|<\\p>)', '', lines[i]) + lines[i] = re.sub('

', '', lines[i]) # If not exists any line add paragraph if not re.search('^$', lines[i]): - lines[i] = actual_align + lines[i] + "<\\p>" + lines[i] = actual_align + lines[i] + "

" return '\n'.join(lines) @@ -258,7 +258,7 @@ class RichTextBox(TextBox): locks[tag] = True imark = text_buffer.get_iter_at_mark(end_mark) if imark.ends_tag(self.text_tags[tag]) and locks[tag]: - text_buffer.insert(imark, "<\\%s>" % tag[0]) + text_buffer.insert(imark, "" % tag[0]) locks[tag] = False for tag in ('left', 'right', 'center', 'fill'): imark = text_buffer.get_iter_at_mark(begin_mark) @@ -267,7 +267,7 @@ class RichTextBox(TextBox): locks[tag] = True imark = text_buffer.get_iter_at_mark(end_mark) if imark.ends_tag(self.text_tags[tag]) and locks[tag]: - text_buffer.insert(imark, "<\\p>") + text_buffer.insert(imark, "

") locks[tag] = False for tag in ('font_family', 'size', 'foreground', 'background'): for name, prop in self.font_props[tag].items(): @@ -280,7 +280,7 @@ class RichTextBox(TextBox): imark = text_buffer.get_iter_at_mark(end_mark) if imark.ends_tag(prop) and locks['%s %s' % (tag, name)]: locks['%s %s' % (tag, name)] = False - text_buffer.insert(imark, "<\\span>") + text_buffer.insert(imark, "") imark = text_buffer.get_iter_at_mark(end_mark) if not imark.forward_to_tag_toggle(None): break @@ -311,7 +311,7 @@ class RichTextBox(TextBox): text_buffer.delete(text_buffer.get_iter_at_offset( data_open.start()), text_buffer.get_iter_at_offset( data_open.end())) - close_re = '<\\\%s>' % tag + close_re = '' % tag data_close = re.search(close_re, text) if data_close: text = re.sub(close_re, '', text, 1) From 0082c5307cad7d94873a8fdc5e77ab09810a8282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=C3=89vrard?= Date: Fri, 21 Sep 2012 15:46:10 +0200 Subject: [PATCH 033/104] call WinSearch callback even when WinForm do not yield result review529003 issue2782 (transplanted from 28fce397c4d151ba200d805422c84aabf6befc82) --- tryton/gui/window/win_search.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tryton/gui/window/win_search.py b/tryton/gui/window/win_search.py index 2de567f5ba..68ad5e9d55 100644 --- a/tryton/gui/window/win_search.py +++ b/tryton/gui/window/win_search.py @@ -116,6 +116,8 @@ class WinSearch(NoModal): record = screen.current_record res = [(record.id, record.value.get('rec_name', ''))] self.callback(res) + else: + self.callback(None) self.destroy() WinForm(screen, callback, new=True) return From 4a5ae5d2e3c677a9388457b9d1897f89c238e785 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 27 Sep 2012 11:36:34 +0200 Subject: [PATCH 034/104] Allow to display time(0, 0) (transplanted from 11bfe50ebecf65ae3f44a4331f42d47898d34e71) --- tryton/gui/window/view_form/model/field.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tryton/gui/window/view_form/model/field.py b/tryton/gui/window/view_form/model/field.py index 85e06d194a..d7627375c4 100644 --- a/tryton/gui/window/view_form/model/field.py +++ b/tryton/gui/window/view_form/model/field.py @@ -263,7 +263,7 @@ class TimeField(CharField): def get_client(self, record): value = super(TimeField, self).get_client(record) - if value: + if value is not None: return value.strftime(HM_FORMAT) return '' From 737a98a7650dd62ab52a40feeb5394d6c01f4fd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 1 Oct 2012 16:07:19 +0200 Subject: [PATCH 035/104] Don't store interger as current record Use inline if/else instead of and issue2701 (transplanted from 33f9f70a701fb253463297e173345e8d08cc58f3) --- tryton/gui/window/view_form/screen/screen.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tryton/gui/window/view_form/screen/screen.py b/tryton/gui/window/view_form/screen/screen.py index 22d764f8e7..790aad9515 100644 --- a/tryton/gui/window/view_form/screen/screen.py +++ b/tryton/gui/window/view_form/screen/screen.py @@ -707,7 +707,7 @@ class Screen(SignalEvent): break self.current_record = record else: - self.current_record = len(self.group) and self.group[0] + self.current_record = self.group[0] if len(self.group) else None self.set_cursor(reset_view=False) view.display() @@ -739,7 +739,7 @@ class Screen(SignalEvent): record = parent self.current_record = record else: - self.current_record = len(self.group) and self.group[-1] + self.current_record = self.group[-1] if len(self.group) else None self.set_cursor(reset_view=False) view.display() From ada2e694fc44060200619d0048c27f541c6bd766 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 5 Oct 2012 18:31:00 +0200 Subject: [PATCH 036/104] Restore Concurrency exception with RPCProgress issue2789 review549003 (transplanted from 532d1c16e16b84f2bcf3f0f8299356c509cd85a8) --- tryton/common/common.py | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/tryton/common/common.py b/tryton/common/common.py index fd3c32fa61..c1f3d5a3da 100644 --- a/tryton/common/common.py +++ b/tryton/common/common.py @@ -952,6 +952,8 @@ PLOCK = Lock() def process_exception(exception, *args, **kwargs): + rpc_execute = kwargs.get('rpc_execute', rpc.execute) + if isinstance(exception, TrytonError): if exception.faultCode == 'BadFingerprint': warning( @@ -977,14 +979,15 @@ def process_exception(exception, *args, **kwargs): 'always': (res == 'always'), }, rpc.CONTEXT) try: - rpc.execute(*args2) + rpc_execute(*args2) except TrytonServerError, exception: process_exception(exception, *args2) if args: try: - return rpc.execute(*args) + return rpc_execute(*args) except TrytonServerError, exception: - return process_exception(exception, *args) + return process_exception(exception, *args, + rpc_execute=rpc_execute) return True return False elif exception.faultCode == 'UserError': @@ -997,9 +1000,10 @@ def process_exception(exception, *args, **kwargs): if '_timestamp' in args[5]: del args[5]['_timestamp'] try: - return rpc.execute(*args) + return rpc_execute(*args) except TrytonServerError, exception: - return process_exception(exception, *args) + return process_exception(exception, *args, + rpc_execute=rpc_execute) return False else: message(_('Concurrency Exception'), msg_type=gtk.MESSAGE_ERROR) @@ -1027,9 +1031,10 @@ def process_exception(exception, *args, **kwargs): continue if args: try: - return rpc.execute(*args) + return rpc_execute(*args) except TrytonServerError, exception: - return process_exception(exception, *args) + return process_exception(exception, *args, + rpc_execute=rpc_execute) return True finally: PLOCK.release() @@ -1256,12 +1261,14 @@ class RPCProgress(object): gtk.main_iteration() if self.exception: if process_exception_p: - if process_exception(self.exception): - self.res = None - self.error = False - self.exception = None - return self.run(process_exception_p) - raise RPCException(self.exception) + def rpc_execute(*args): + return RPCProgress('execute', + args).run(process_exception_p, main_iteration_p) + result = process_exception(self.exception, *self.args, + rpc_execute=rpc_execute) + if result is False: + raise RPCException(self.exception) + return result else: raise self.exception return self.res From e7735c88bdf560a656ae367308b410cb7a0f8b1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 6 Oct 2012 15:18:44 +0200 Subject: [PATCH 037/104] Don't register search_box if not on a page issue2770 (transplanted from cda1bcf226dc18140e6fa6f9e884b9be2ea29c7a) --- tryton/gui/window/view_form/view/screen_container.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tryton/gui/window/view_form/view/screen_container.py b/tryton/gui/window/view_form/view/screen_container.py index 802abc19ef..b40130b1a1 100644 --- a/tryton/gui/window/view_form/view/screen_container.py +++ b/tryton/gui/window/view_form/view/screen_container.py @@ -331,10 +331,10 @@ class ScreenContainer(object): from tryton.gui.main import Main page = Main.get_main().get_page() if button.get_active(): - if self.search_window not in page.dialogs: + if page and self.search_window not in page.dialogs: page.dialogs.append(self.search_window) self.search_window.show() else: self.search_window.hide() - if self.search_window in page.dialogs: + if page and self.search_window in page.dialogs: page.dialogs.remove(self.search_window) From c4aff8dcf295ab98f03c2a49e42384065b3c27c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 6 Oct 2012 17:11:18 +0200 Subject: [PATCH 038/104] Use the same pattern for move_into than _move to prevent concurrency (transplanted from f57702fc015c87566146ee11623d8481ff2388c8) --- tryton/gui/window/view_form/view/list.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tryton/gui/window/view_form/view/list.py b/tryton/gui/window/view_form/view/list.py index 285bd6c00a..11a09f56b1 100644 --- a/tryton/gui/window/view_form/view/list.py +++ b/tryton/gui/window/view_form/view/list.py @@ -126,6 +126,10 @@ class AdaptModelGroup(gtk.GenericTreeModel): group = parent.children_group(self.children_field, check_load=True) if group is not record.group: record.group.remove(record, remove=True, force_remove=True) + # Don't remove record from previous group + # as the new parent will change the parent + # This prevents concurrency conflict + record.group.record_removed.remove(record) group.add(record) record.modified_fields.setdefault(record.parent_name or 'id') group.move(record, 0) From 8087ee8275f887788601695116761cce5b156cfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 8 Oct 2012 21:05:24 +0200 Subject: [PATCH 039/104] Remove row_has_child_toggled in removed Calling get_iter force a reload of the records and break the clear of the group. issue2710 review552002 (transplanted from 35b8da5ce48822cd279e01d2f53ff1f064d5c269) --- tryton/gui/window/view_form/view/list.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tryton/gui/window/view_form/view/list.py b/tryton/gui/window/view_form/view/list.py index 11a09f56b1..4341382e6b 100644 --- a/tryton/gui/window/view_form/view/list.py +++ b/tryton/gui/window/view_form/view/list.py @@ -74,14 +74,6 @@ class AdaptModelGroup(gtk.GenericTreeModel): or record.group.child_name == self.children_field)): path = self.on_get_path(record) self.row_deleted(path) - if (record.parent and - record.group != self.group and - len(record.children_group(self.children_field)) <= 1): - path = self.on_get_path(record.parent) - iter_ = self.get_iter(path) - self.__removed = record # XXX check for thread - self.row_has_child_toggled(path, iter_) - self.__removed = None def append(self, model): self.group.add(model) From 5a51b0abbc0bb9790652a521cb8f08e09036cee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 8 Oct 2012 21:07:31 +0200 Subject: [PATCH 040/104] Delete the children before the parent records review553002 (transplanted from 735011602235036618d293eabc3991f06c602a4b) --- tryton/gui/window/view_form/model/record.py | 9 +++++++++ tryton/gui/window/view_form/screen/screen.py | 2 ++ 2 files changed, 11 insertions(+) diff --git a/tryton/gui/window/view_form/model/record.py b/tryton/gui/window/view_form/model/record.py index bf54c2f257..a75757bdda 100644 --- a/tryton/gui/window/view_form/model/record.py +++ b/tryton/gui/window/view_form/model/record.py @@ -140,6 +140,15 @@ class Record(SignalEvent): def parent_name(self): return self.group.parent_name + @property + def depth(self): + parent = self.parent + i = 0 + while parent: + i += 1 + parent = parent.parent + return i + def set_modified(self, value): if value: self.signal('record-modified') diff --git a/tryton/gui/window/view_form/screen/screen.py b/tryton/gui/window/view_form/screen/screen.py index 790aad9515..0585979a4a 100644 --- a/tryton/gui/window/view_form/screen/screen.py +++ b/tryton/gui/window/view_form/screen/screen.py @@ -542,6 +542,8 @@ class Screen(SignalEvent): if not records: return if delete: + # Must delete children records before parent + records.sort(key=lambda r: r.depth, reverse=True) if not self.group.delete(records): return False From 0f75b8138fb3dd70511b2de0fa75b480bdb6b5b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Tue, 9 Oct 2012 17:18:35 +0200 Subject: [PATCH 041/104] Fix next/previous in form view when there is no current record issue2788 review548003 (transplanted from bd5d0d004c19cd4fe33f3263b065d3fdba630130) --- tryton/gui/window/view_form/screen/screen.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tryton/gui/window/view_form/screen/screen.py b/tryton/gui/window/view_form/screen/screen.py index 0585979a4a..08ab95ebe3 100644 --- a/tryton/gui/window/view_form/screen/screen.py +++ b/tryton/gui/window/view_form/screen/screen.py @@ -682,7 +682,9 @@ class Screen(SignalEvent): store = view.store iter_ = store.get_iter(end) self.current_record = store.get_value(iter_, 0) - elif view.view_type == 'form' and self.current_record.group: + elif (view.view_type == 'form' + and self.current_record + and self.current_record.group): group = self.current_record.group record = self.current_record while group: @@ -724,7 +726,9 @@ class Screen(SignalEvent): store = view.store iter_ = store.get_iter(start) self.current_record = store.get_value(iter_, 0) - elif view.view_type == 'form' and self.current_record.group: + elif (view.view_type == 'form' + and self.current_record + and self.current_record.group): group = self.current_record.group record = self.current_record idx = group.index(record) - 1 From 1e452b1eea6568c5401443825eb9dd07d6401740 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 10 Oct 2012 17:49:02 +0200 Subject: [PATCH 042/104] Prevent closing non-sensitive tabs review555002 (transplanted from 5f8ba9d5cb9c84c4d118f0de07acc16abbec512a) --- tryton/gui/main.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tryton/gui/main.py b/tryton/gui/main.py index 0281966bd3..d68cc44218 100644 --- a/tryton/gui/main.py +++ b/tryton/gui/main.py @@ -1189,6 +1189,8 @@ class Main(object): def _sig_remove_book(self, widget, page_widget): for page in self.pages: if page.widget == page_widget: + if not page.widget.props.sensitive: + return page_num = self.notebook.page_num(page.widget) self.notebook.set_current_page(page_num) res = page.sig_close() From f4dbcd33da284d47dd519328a87521e183efeddf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 17 Oct 2012 21:19:22 +0200 Subject: [PATCH 043/104] Fix calls to sig_db_dump/restore issue2814 (transplanted from 2105a3798a43a68f2813a0258597c20d653c87a0) --- tryton/gui/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tryton/gui/main.py b/tryton/gui/main.py index d68cc44218..fcdef8c463 100644 --- a/tryton/gui/main.py +++ b/tryton/gui/main.py @@ -1311,7 +1311,7 @@ class Main(object): self.refresh_ssl() common.message(_("Database dropped successfully!")) - def sig_db_restore(self, widget): + def sig_db_restore(self, widget=None): if not self.sig_logout(widget): return False filename = common.file_selection(_('Open Backup File to Restore...'), @@ -1353,7 +1353,7 @@ class Main(object): else: common.message(_('Database restore failed!')) - def sig_db_dump(self, widget): + def sig_db_dump(self, widget=None): if not self.sig_logout(widget): return False dialog = DBBackupDrop(function='backup') From a5d70fc4ceda2dfdf64ec92d495cc37f1b41fce7 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bonicoli Date: Sat, 20 Oct 2012 13:46:07 +0200 Subject: [PATCH 044/104] avoid markup on unknown strings issue2843 review576002 (transplanted from ee1eeb93e98c8a82678b6cf37fa44fbc1b41fd55) --- tryton/gui/window/win_form.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tryton/gui/window/win_form.py b/tryton/gui/window/win_form.py index f6536583d7..4c91fca280 100644 --- a/tryton/gui/window/win_form.py +++ b/tryton/gui/window/win_form.py @@ -77,9 +77,8 @@ class WinForm(NoModal): self.win.set_title(self.screen.current_view.title) title = gtk.Label() - title.set_use_markup(True) - title.modify_font(pango.FontDescription("12")) - title.set_label('' + self.screen.current_view.title + '') + title.modify_font(pango.FontDescription("bold 12")) + title.set_label(self.screen.current_view.title) title.set_padding(20, 3) title.set_alignment(0.0, 0.5) title.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse("#000000")) From 56d036c56abfe72589efd977efc4fc91fd2e0d7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 5 Nov 2012 17:30:19 +0100 Subject: [PATCH 045/104] Prepare release 2.4.2 --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index de75c733f2..817e33203c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Version 2.4.2 - 2012-11-05 +* Bug fixes (see mercurial logs for details) + Version 2.4.1 - 2012-09-01 * Bug fixes (see mercurial logs for details) From 4d1cd77d16e914996c773417ef0b166fab046257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 5 Nov 2012 17:30:20 +0100 Subject: [PATCH 046/104] Added tag 2.4.2 for changeset 733b3f702200 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 2661a07fe0..a780ed8685 100644 --- a/.hgtags +++ b/.hgtags @@ -7,3 +7,4 @@ de84c1443d4bad908eabb99c381578b988c2117f 1.4.0 5364f8ca6cd2cccfb5f7f39c69bb20bae2b5f495 2.2.0 fe15da23f7333e0e90d2693aadddf6bed53ed602 2.4.0 03f86ced937d03bada5193526bf78655c0c489a5 2.4.1 +733b3f7022001aad274cd502d7e34fc9231af8b6 2.4.2 From a51cfcdb272fa6ff4c0400fc65a03b69a8cc9c92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 5 Nov 2012 17:31:08 +0100 Subject: [PATCH 047/104] Increase version number --- tryton/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tryton/version.py b/tryton/version.py index aa797078eb..607d4d92da 100644 --- a/tryton/version.py +++ b/tryton/version.py @@ -1,6 +1,6 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. PACKAGE = "tryton" -VERSION = "2.4.2" +VERSION = "2.4.3" LICENSE = "GPL-3" WEBSITE = "http://www.tryton.org/" From 58bac50f467dd2106b3ef1a0f159409f3b713fa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Tue, 30 Oct 2012 13:04:27 +0100 Subject: [PATCH 048/104] Fix domain parser to not always strip default operator review585002 (transplanted from 548cb6aeab1f9cb6aa456a7221e79c53de6f6cbe) --- tryton/common/domain_parser.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tryton/common/domain_parser.py b/tryton/common/domain_parser.py index 48338a85f4..ba02d80b37 100644 --- a/tryton/common/domain_parser.py +++ b/tryton/common/domain_parser.py @@ -715,7 +715,9 @@ class DomainParser(object): operator = '!' value = value.replace('%%', '%') def_operator = default_operator(field) - if def_operator in operator: + if (def_operator == operator.strip() + or (def_operator in operator + and 'not' in operator)): operator = operator.rstrip(def_operator ).replace('not', '!').strip() if operator.endswith('in'): @@ -924,6 +926,10 @@ def test_string(): 'string': '(Sur)Name', 'type': 'char', }, + 'date': { + 'string': 'Date', + 'type': 'date', + }, }) assert dom.string([('name', '=', 'Doe')]) == 'Name: =Doe' assert dom.string([('name', 'ilike', '%Doe%')]) == 'Name: Doe' @@ -951,6 +957,8 @@ def test_string(): 'Name: Doe (Name: John or Name: Jane)' assert dom.string([]) == '' assert dom.string([('surname', 'ilike', '%Doe%')]) == '"(Sur)Name": Doe' + assert dom.string([('date', '>=', datetime.date(2012, 10, 24))]) == \ + 'Date: >=10/24/2012' def test_group(): From 3af52d79947a30bcfbff945075a5b573ee7dcf75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 2 Nov 2012 16:19:00 +0100 Subject: [PATCH 049/104] Fix domain parser when value is a list review591002 (transplanted from 61d834ed6a8c49c89c29b85b3134ae28112cb9ee) --- tryton/common/domain_parser.py | 45 +++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/tryton/common/domain_parser.py b/tryton/common/domain_parser.py index ba02d80b37..11e50daec3 100644 --- a/tryton/common/domain_parser.py +++ b/tryton/common/domain_parser.py @@ -541,9 +541,15 @@ def complete_value(field, value): yield True def complete_selection(): + test_value = value + if isinstance(value, list): + test_value = value[-1] for svalue, test in field['selection']: - if test.lower().startswith(value.lower()): - yield svalue + if test.lower().startswith(test_value.lower()): + if test_value == value: + yield svalue + else: + yield value[:-1] + [svalue] def complete_datetime(): yield datetime.date.today() @@ -566,6 +572,23 @@ def complete_value(field, value): return completes.get(field['type'], lambda: [])() +def test_complete_selection(): + field = { + 'type': 'selection', + 'selection': [ + ('male', 'Male'), + ('female', 'Female'), + ], + } + for value, result in ( + ('m', ['male']), + ('test', []), + ('', ['male', 'female']), + (['male', 'f'], [['male', 'female']]), + ): + assert list(complete_value(field, value)) == result + + def parenthesize(tokens): "Nest tokens according to parenthesis" for token in tokens: @@ -912,7 +935,10 @@ class DomainParser(object): (field['name'], '<', rvalue), ]) continue - value = convert_value(field, value) + if isinstance(value, list): + value = [convert_value(field, v) for v in value] + else: + value = convert_value(field, value) yield field['name'], operator, value @@ -1045,6 +1071,15 @@ def test_parse_clause(): 'name': 'integer', 'type': 'integer', }, + 'selection': { + 'string': 'Selection', + 'name': 'selection', + 'type': 'selection', + 'selection': [ + ('male', 'Male'), + ('female', 'Female'), + ], + }, }) assert rlist(dom.parse_clause([('John',)])) == [ ('rec_name', 'ilike', '%John%')] @@ -1058,6 +1093,10 @@ def test_parse_clause(): assert rlist(dom.parse_clause([('Name', '!', ['John', 'Jane'])])) == [ ('name', 'not in', ['John', 'Jane']), ] + assert rlist(dom.parse_clause([('Selection', None, ['Male', 'Female'])])) \ + == [ + ('selection', 'in', ['male', 'female']) + ] assert rlist(dom.parse_clause([('Integer', None, '3..5')])) == [[ ('integer', '>=', 3), ('integer', '<', 5), From 82dd460c543c4b7b21c07f30e8e5ca0995969a18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 2 Nov 2012 16:20:04 +0100 Subject: [PATCH 050/104] Fix Char.translate_widget_set when value is None review587004 (transplanted from 4b45f57716e5a9576db6c02bc16a51e77daf6aef) --- tryton/gui/window/view_form/view/form_gtk/char.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tryton/gui/window/view_form/view/form_gtk/char.py b/tryton/gui/window/view_form/view/form_gtk/char.py index c999f2228a..b0e112a236 100644 --- a/tryton/gui/window/view_form/view/form_gtk/char.py +++ b/tryton/gui/window/view_form/view/form_gtk/char.py @@ -56,7 +56,7 @@ class Char(WidgetInterface, TranslateMixin): @staticmethod def translate_widget_set(widget, value): - widget.set_text(value) + widget.set_text(value or '') @staticmethod def translate_widget_get(widget): From 0501cf57e5fa7a295862ae49dccbf6a15e583ff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 15 Nov 2012 18:50:16 +0100 Subject: [PATCH 051/104] Record.get_eval must return only loaded values and validate only fields Domain inversion should not have fields that are not loaded otherwise the inversion could be wrong. issue2865 review583003 (transplanted from 4d2bd7127a51862ab1293e8cdc4cf9f8bf217945) --- tryton/common/domain_inversion.py | 4 ++-- tryton/gui/window/view_form/model/record.py | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/tryton/common/domain_inversion.py b/tryton/common/domain_inversion.py index 9c71e0d66f..db120c680f 100644 --- a/tryton/common/domain_inversion.py +++ b/tryton/common/domain_inversion.py @@ -53,11 +53,11 @@ def eval_leaf(part, context, boolop=operator.and_): # In the case where the leaf concerns a m2o then having a value in the # evaluation context is deemed suffisant return bool(context.get(field.split('.')[0])) - if operand == '=' and not context[field] and boolop == operator.and_: + if operand == '=' and not context.get(field) and boolop == operator.and_: # We should consider that other domain inversion will set a correct # value to this field return True - context_field = context[field] + context_field = context.get(field) if isinstance(context_field, datetime.date) and not value: if isinstance(context_field, datetime.datetime): value = datetime.datetime.min diff --git a/tryton/gui/window/view_form/model/record.py b/tryton/gui/window/view_form/model/record.py index a75757bdda..3ef1c257d5 100644 --- a/tryton/gui/window/view_form/model/record.py +++ b/tryton/gui/window/view_form/model/record.py @@ -249,6 +249,8 @@ class Record(SignalEvent): self._check_load() value = {} for name, field in self.group.fields.iteritems(): + if name not in self._loaded and self.id >= 0: + continue value[name] = field.get_eval(self, check_load=check_load) value['id'] = self.id return value @@ -448,7 +450,7 @@ class Record(SignalEvent): else: for field in fields: self[field] - self.validate([]) + self.validate(fields or []) def expr_eval(self, expr, check_load=False): if not isinstance(expr, basestring): From 2b82b59650bdecb7038512c3009cb5c980ac2ea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 15 Nov 2012 19:49:00 +0100 Subject: [PATCH 052/104] Don't untimezone fallback date for converting datetime (transplanted from aa2764ccc856dd2c589caf1f02e957c32211c79a) --- tryton/common/domain_parser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tryton/common/domain_parser.py b/tryton/common/domain_parser.py index 11e50daec3..d8873a0134 100644 --- a/tryton/common/domain_parser.py +++ b/tryton/common/domain_parser.py @@ -214,8 +214,8 @@ def convert_value(field, value): date_format() + ' ' + HM_FORMAT)[:6])) except ValueError: try: - return untimezoned_date(datetime.datetime(*time.strptime(value, - date_format())[:6])) + return datetime.datetime(*time.strptime(value, + date_format())[:6]) except ValueError: return except TypeError: From 11db41ec52fb59a540a85fa844013e3fc6f806ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 16 Nov 2012 11:03:43 +0100 Subject: [PATCH 053/104] Reset rec_name for on_change_with calls issue2888 review594006 (transplanted from ea3353be76ad7370364874c1d08c767b15b49e8e) --- tryton/gui/window/view_form/model/record.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tryton/gui/window/view_form/model/record.py b/tryton/gui/window/view_form/model/record.py index 3ef1c257d5..95e033a80a 100644 --- a/tryton/gui/window/view_form/model/record.py +++ b/tryton/gui/window/view_form/model/record.py @@ -536,6 +536,11 @@ class Record(SignalEvent): continue fieldnames.add(fieldname) values.update(self._get_on_change_args(on_change_with)) + if isinstance(self.group.fields[fieldname], (fields.M2OField, + fields.ReferenceField)): + field_rec_name = fieldname + '.rec_name' + if field_rec_name in self.value: + del self.value[field_rec_name] if fieldnames: try: result = RPCExecute('model', self.model_name, 'on_change_with', From 38ace29ddb5e16274469b09f2466b62869d385da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 9 Dec 2012 00:11:55 +0100 Subject: [PATCH 054/104] Add missing parenthesis on test in O2M.set_on_change issue2917 (transplanted from b76788d09e04a0a80f5fccb26d5a505bdeb35c7d) --- tryton/gui/window/view_form/model/field.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tryton/gui/window/view_form/model/field.py b/tryton/gui/window/view_form/model/field.py index d7627375c4..8ceb3fb45d 100644 --- a/tryton/gui/window/view_form/model/field.py +++ b/tryton/gui/window/view_form/model/field.py @@ -697,7 +697,7 @@ class O2MField(CharField): record.value[self.name].remove(record2, signal=False, force_remove=True) - if value and value.get('add') or value.get('update', []): + if value and (value.get('add') or value.get('update', [])): record.value[self.name].add_fields(fields, signal=False) for vals in value.get('add', []): new_record = record.value[self.name].new(default=False) From fc142bdb4ce6ede004ff49123ef9acee14b7d59d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 13 Dec 2012 19:06:01 +0100 Subject: [PATCH 055/104] Copy also tryton locale on win32 as directory is clean before issue2921 review597006 (transplanted from e5cdaf0a7ba1fd96593138d905bebe50de6c5663) --- setup.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/setup.py b/setup.py index 589880ebf1..81319e72e2 100644 --- a/setup.py +++ b/setup.py @@ -250,6 +250,11 @@ if os.name == 'nt': if os.path.isdir(os.path.join(gtk_dir, 'share', 'locale', lang)): shutil.copytree(os.path.join(gtk_dir, 'share', 'locale', lang), os.path.join(dist_dir, 'share', 'locale', lang)) + if os.path.isdir(os.path.join(os.path.dirname(__file__), + 'share', 'locale', lang)): + shutil.copytree(os.path.join(os.path.dirname(__file__), + 'share', 'locale', lang), + os.path.join(dist_dir, 'share', 'locale', lang)) if os.path.isdir(os.path.join(dist_dir, 'share', 'themes', 'MS-Windows')): @@ -365,6 +370,11 @@ elif sys.platform == 'darwin': if os.path.isdir(os.path.join(gtk_dir, 'share', 'locale', lang)): shutil.copytree(os.path.join(gtk_dir, 'share', 'locale', lang), os.path.join(resources_dir, 'share', 'locale', lang)) + if os.path.isdir(os.path.join(os.path.dirname(__file__), + 'share', 'locale', lang)): + shutil.copytree(os.path.join(os.path.dirname(__file__), + 'share', 'locale', lang), + os.path.join(resources_dir, 'share', 'locale', lang)) # fix pathes within shared libraries for library in chain( From 982a34deb3c0fbfaf840678da4b2e1960f01e94b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 23 Dec 2012 16:47:59 +0100 Subject: [PATCH 056/104] Prepare release 2.4.3 --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 817e33203c..250b9617d4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Version 2.4.3 - 2012-12-23 +* Bug fixes (see mercurial logs for details) + Version 2.4.2 - 2012-11-05 * Bug fixes (see mercurial logs for details) From 29c4341d7ce0f90b609f0630199530b4f7e81def Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 23 Dec 2012 16:48:00 +0100 Subject: [PATCH 057/104] Added tag 2.4.3 for changeset 0e4ac607e0a0 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index a780ed8685..89cf904ebf 100644 --- a/.hgtags +++ b/.hgtags @@ -8,3 +8,4 @@ de84c1443d4bad908eabb99c381578b988c2117f 1.4.0 fe15da23f7333e0e90d2693aadddf6bed53ed602 2.4.0 03f86ced937d03bada5193526bf78655c0c489a5 2.4.1 733b3f7022001aad274cd502d7e34fc9231af8b6 2.4.2 +0e4ac607e0a048972a6b5c5f90651a25fae25c6e 2.4.3 From f8ce2af8525b0574add39f45b8488b76ba4b692c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 23 Dec 2012 16:48:32 +0100 Subject: [PATCH 058/104] Increase version number --- tryton/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tryton/version.py b/tryton/version.py index 607d4d92da..6d44fae66d 100644 --- a/tryton/version.py +++ b/tryton/version.py @@ -1,6 +1,6 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. PACKAGE = "tryton" -VERSION = "2.4.3" +VERSION = "2.4.4" LICENSE = "GPL-3" WEBSITE = "http://www.tryton.org/" From f1d82ed859fa642d4b0f66bfa615dc32c3967f94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 23 Dec 2012 22:57:12 +0100 Subject: [PATCH 059/104] Disable main_iteration on RPC query from field RPC query from field could happen during a GTK event. review619002 (transplanted from db149f280d6053d50dd7dd230ce744d9ae397acd) --- tryton/gui/window/view_form/model/field.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tryton/gui/window/view_form/model/field.py b/tryton/gui/window/view_form/model/field.py index 8ceb3fb45d..dcd4a51313 100644 --- a/tryton/gui/window/view_form/model/field.py +++ b/tryton/gui/window/view_form/model/field.py @@ -410,7 +410,7 @@ class M2OField(CharField): if not rec_name and value >= 0: try: result = RPCExecute('model', self.attrs['relation'], 'read', - value, ['rec_name']) + value, ['rec_name'], main_iteration=False) except RPCException: return False rec_name = result['rec_name'] or '' @@ -637,7 +637,8 @@ class O2MField(CharField): if field_names: try: fields.update(RPCExecute('model', self.attrs['relation'], - 'fields_get', field_names, context=context)) + 'fields_get', field_names, + main_iteration=False, context=context)) except RPCException: return False @@ -680,7 +681,8 @@ class O2MField(CharField): field_names.append(fieldname) try: fields = RPCExecute('model', self.attrs['relation'], - 'fields_get', field_names, context=context) + 'fields_get', field_names, main_iteration=False, + context=context) except RPCException: return False @@ -835,7 +837,7 @@ class ReferenceField(CharField): if not rec_name and ref_id >= 0: try: result = RPCExecute('model', ref_model, 'read', ref_id, - ['rec_name']) + ['rec_name'], main_iteration=False) except RPCException: return rec_name = result['rec_name'] @@ -893,7 +895,8 @@ class BinaryField(CharField): context = record.context_get() try: values = RPCExecute('model', record.model_name, 'read', - record.id, [self.name], context=context) + record.id, [self.name], main_iteration=False, + context=context) except RPCException: return '' _, filename = tempfile.mkstemp(prefix='tryton_') From 3e34450de85862ce4300dfaa730a4b36a16f9499 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=C3=89vrard?= Date: Thu, 10 Jan 2013 11:37:17 +0100 Subject: [PATCH 060/104] Add missing _default for TimeField issue2940 review631003 (transplanted from 034e965007685e470598698fec3a235d2df3bcb3) --- tryton/gui/window/view_form/model/field.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tryton/gui/window/view_form/model/field.py b/tryton/gui/window/view_form/model/field.py index dcd4a51313..eb25933dd0 100644 --- a/tryton/gui/window/view_form/model/field.py +++ b/tryton/gui/window/view_form/model/field.py @@ -252,6 +252,8 @@ class DateField(CharField): class TimeField(CharField): + _default = None + def set_client(self, record, value, force_change=False): if not isinstance(value, datetime.time): try: From e2210cd9b5f55d6d0a8d3fbbe1b243e991f7e2cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 19 Jan 2013 17:13:44 +0100 Subject: [PATCH 061/104] Fix list popup when parent modified issue2949 review635004 (transplanted from 97de060117ce1f39dfedb41efc02ef496d7e1979) --- tryton/gui/window/view_form/view/list.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tryton/gui/window/view_form/view/list.py b/tryton/gui/window/view_form/view/list.py index 4341382e6b..ddb86e212d 100644 --- a/tryton/gui/window/view_form/view/list.py +++ b/tryton/gui/window/view_form/view/list.py @@ -556,10 +556,10 @@ class ViewList(ParserView): parent = record.parent if record else None while parent: if parent.modified: - record = None break parent = parent.parent - populate(menu, group.model_name, record) + else: + populate(menu, group.model_name, record) for col in self.widget_tree.get_columns(): if not col.get_visible() or not col.name: continue From 751e4d6cc16e77927e32e694433444cc993bd511 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 26 Jan 2013 15:07:23 +0100 Subject: [PATCH 062/104] Validate non-loaded O2M record when new and pass missing softvalidation issue2968 review637003 (transplanted from f753403e8ebc0455c691361f2c5e9a36be771006) --- tryton/gui/window/view_form/model/field.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tryton/gui/window/view_form/model/field.py b/tryton/gui/window/view_form/model/field.py index eb25933dd0..1f1aa33e4b 100644 --- a/tryton/gui/window/view_form/model/field.py +++ b/tryton/gui/window/view_form/model/field.py @@ -725,9 +725,9 @@ class O2MField(CharField): return True res = True for record2 in record.value.get(self.name, []): - if not record2.loaded: + if not record2.loaded and record2.id >= 0: continue - if not record2.validate(): + if not record2.validate(softvalidation=softvalidation): if not record2.modified: record.value[self.name].remove(record2) else: From 736f17b1d5a5b4adfda26b110cf0df04e2ceee61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Tue, 12 Feb 2013 17:59:08 +0100 Subject: [PATCH 063/104] Prepare release 2.4.4 --- CHANGELOG | 3 +++ COPYRIGHT | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 250b9617d4..80e703c066 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Version 2.4.4 - 2013-02-12 +* Bug fixes (see mercurial logs for details) + Version 2.4.3 - 2012-12-23 * Bug fixes (see mercurial logs for details) diff --git a/COPYRIGHT b/COPYRIGHT index 55b3570380..3f60d5d9be 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -1,7 +1,7 @@ -Copyright (C) 2010-2012 Nicolas Évrard. -Copyright (C) 2007-2012 Cédric Krier. +Copyright (C) 2010-2013 Nicolas Évrard. +Copyright (C) 2007-2013 Cédric Krier. Copyright (C) 2007-2012 Bertrand Chenal. -Copyright (C) 2008-2012 B2CK SPRL. +Copyright (C) 2008-2013 B2CK SPRL. Copyright (C) 2008-2011 Udo Spallek. Copyright (C) 2008-2011 virtual things - Preisler & Spallek GbR. Copyright (C) 2011-2012 Rodrigo Hübner. From 82404c9737e1391f93d2950d33eda2a3a7c245f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Tue, 12 Feb 2013 17:59:09 +0100 Subject: [PATCH 064/104] Added tag 2.4.4 for changeset 4207622ad8b1 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 89cf904ebf..9cb4d39d70 100644 --- a/.hgtags +++ b/.hgtags @@ -9,3 +9,4 @@ fe15da23f7333e0e90d2693aadddf6bed53ed602 2.4.0 03f86ced937d03bada5193526bf78655c0c489a5 2.4.1 733b3f7022001aad274cd502d7e34fc9231af8b6 2.4.2 0e4ac607e0a048972a6b5c5f90651a25fae25c6e 2.4.3 +4207622ad8b1c6885e3e36265906035b950d7b89 2.4.4 From d04815b6e0141fcdbf594db37d2138b28f11eacb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Tue, 12 Feb 2013 17:59:42 +0100 Subject: [PATCH 065/104] Increase version number --- tryton/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tryton/version.py b/tryton/version.py index 6d44fae66d..30c6cedbba 100644 --- a/tryton/version.py +++ b/tryton/version.py @@ -1,6 +1,6 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. PACKAGE = "tryton" -VERSION = "2.4.4" +VERSION = "2.4.5" LICENSE = "GPL-3" WEBSITE = "http://www.tryton.org/" From bc371869bb2b63ccdfcd5fce51bf0775decd0e04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=C3=89vrard?= Date: Mon, 11 Feb 2013 09:38:40 +0100 Subject: [PATCH 066/104] Correctly parse the URL review621003 (transplanted from f690b98c1a382fd36a43ba90c0272adaf6cba370) --- tryton/gui/main.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tryton/gui/main.py b/tryton/gui/main.py index fcdef8c463..fcf35adb1e 100644 --- a/tryton/gui/main.py +++ b/tryton/gui/main.py @@ -5,7 +5,7 @@ import os import sys import socket import gettext -from urlparse import urlparse +from urlparse import urlparse, parse_qsl import urllib import gobject import gtk @@ -1403,14 +1403,14 @@ class Main(object): Main.get_main().refresh_ssl() def _open_url(self, url): - url = urllib.unquote(url) urlp = urlparse(url) if not urlp.scheme == 'tryton': return urlp = urlparse('http' + url[6:]) - hostname, port = (urlp.netloc.split(':', 1) - + [CONFIG.defaults['login.port']])[:2] - database, path = (urlp.path[1:].split('/', 1) + [None])[:2] + hostname, port = map(urllib.unquote, + (urlp.netloc.split(':', 1) + [CONFIG.defaults['login.port']])[:2]) + database, path = map(urllib.unquote, + (urlp.path[1:].split('/', 1) + [None])[:2]) if (not path or hostname != rpc._HOST or int(port) != rpc._PORT or @@ -1420,8 +1420,8 @@ class Main(object): params = {} if urlp.params: try: - params = dict(param.split('=', 1) - for param in urlp.params.split('&')) + params.update(dict(parse_qsl(urlp.params, + strict_parsing=True))) except ValueError: return From 86e5fdbfb4728ee2d0a51917c7ec42fac4ba7097 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 4 Mar 2013 22:21:52 +0100 Subject: [PATCH 067/104] Disable main iteration for all RPC request from model/ We can not know if the request is fired during the process of a GTK event. review686002 (transplanted from db672c4e45901a05ce23a669648daa25794c3e9b) --- tryton/gui/window/view_form/model/group.py | 4 +-- tryton/gui/window/view_form/model/record.py | 27 +++++++++++++-------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/tryton/gui/window/view_form/model/group.py b/tryton/gui/window/view_form/model/group.py index 089882af9b..ab0a8b06a1 100644 --- a/tryton/gui/window/view_form/model/group.py +++ b/tryton/gui/window/view_form/model/group.py @@ -187,7 +187,7 @@ class Group(SignalEvent, list): for fnct in self.on_write: try: res += RPCExecute('model', self.model_name, fnct, ids, - context=self.context) + main_iteration=False, context=self.context) except RPCException: return [] return list({}.fromkeys(res)) @@ -376,7 +376,7 @@ class Group(SignalEvent, list): ctx.update(self.context) try: values = RPCExecute('model', self.model_name, 'default_get', - to_add.keys(), context=ctx) + to_add.keys(), main_iteration=False, context=ctx) except RPCException: return False for name in to_add: diff --git a/tryton/gui/window/view_form/model/record.py b/tryton/gui/window/view_form/model/record.py index 95e033a80a..0ab86cd8d6 100644 --- a/tryton/gui/window/view_form/model/record.py +++ b/tryton/gui/window/view_form/model/record.py @@ -281,6 +281,7 @@ class Record(SignalEvent): value = self.get(get_readonly=True) try: res = RPCExecute('model', self.model_name, 'create', value, + main_iteration=False, context=self.context_get()) except RPCException: return False @@ -297,7 +298,8 @@ class Record(SignalEvent): context['_timestamp'] = self.get_timestamp() try: if not RPCExecute('model', self.model_name, 'write', - [self.id], value, context=context): + [self.id], value, main_iteration=False, + context=context): return False except RPCException: return False @@ -332,7 +334,7 @@ class Record(SignalEvent): reload_ids = list(reload_ids) try: RPCExecute('model', record.model_name, 'delete', list(record_ids), - context=ctx) + main_iteration=False, context=ctx) except RPCException: return False if reload_ids: @@ -343,7 +345,8 @@ class Record(SignalEvent): if len(self.group.fields): try: vals = RPCExecute('model', self.model_name, 'default_get', - self.group.fields.keys(), context=context) + self.group.fields.keys(), main_iteration=False, + context=context) except RPCException: return if (self.parent @@ -360,7 +363,8 @@ class Record(SignalEvent): def rec_name(self): try: return RPCExecute('model', self.model_name, 'read', self.id, - ['rec_name'], context=self.context_get())['rec_name'] + ['rec_name'], main_iteration=False, + context=self.context_get())['rec_name'] except RPCException: return '' @@ -490,8 +494,9 @@ class Record(SignalEvent): attr = PYSONDecoder().decode(attr) args = self._get_on_change_args(attr) try: - res = RPCExecute('model', self.model_name, - 'on_change_' + fieldname, args, context=self.context_get()) + res = RPCExecute('model', self.model_name, 'on_change_' + + fieldname, args, main_iteration=False, + context=self.context_get()) except RPCException: return later = {} @@ -544,7 +549,8 @@ class Record(SignalEvent): if fieldnames: try: result = RPCExecute('model', self.model_name, 'on_change_with', - list(fieldnames), values, context=self.context_get()) + list(fieldnames), values, main_iteration=False, + context=self.context_get()) except RPCException: return for fieldname, value in result.items(): @@ -556,7 +562,7 @@ class Record(SignalEvent): try: result = RPCExecute('model', self.model_name, 'on_change_with_' + fieldname, values, - context=self.context_get()) + main_iteration=False, context=self.context_get()) except RPCException: return self.group.fields[fieldname].set_on_change(self, result) @@ -574,7 +580,8 @@ class Record(SignalEvent): args = self._get_on_change_args(autocomplete) try: res = RPCExecute('model', self.model_name, 'autocomplete_' + - fieldname, args, context=self.context_get()) + fieldname, args, main_iteration=False, + context=self.context_get()) except RPCException: # ensure res is a list res = [] @@ -589,7 +596,7 @@ class Record(SignalEvent): 'search_count', [ ('resource', '=', '%s,%s' % (self.model_name, self.id)), - ]) + ], main_iteration=False) except RPCException: return 0 return self.attachment_count From 8611b523952a7eddb843c699bb10658c97143970 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 27 Mar 2013 22:50:08 +0100 Subject: [PATCH 068/104] Fix group loading when using '*' Prevent to load unnecessary records due to wrong loaded matching. issue3069 review744003 (transplanted from 0371410177db3ab0a3721486c1f83444ba549fa8) --- tryton/gui/window/view_form/model/record.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tryton/gui/window/view_form/model/record.py b/tryton/gui/window/view_form/model/record.py index 0ab86cd8d6..b0fd90091b 100644 --- a/tryton/gui/window/view_form/model/record.py +++ b/tryton/gui/window/view_form/model/record.py @@ -48,6 +48,11 @@ class Record(SignalEvent): (field.attrs.get('loading', 'eager') for field in self.group.fields.itervalues()), 'eager') + # Set a valid name for next loaded check + for fname, field in self.group.fields.iteritems(): + if field.attrs.get('loading', 'eager') == loading: + name = fname + break else: loading = self.group.fields[name].attrs.get('loading', 'eager') if self in self.group and loading == 'eager': From ad5090570c293e1455280f99c6d583bcbd3bd8ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 27 Mar 2013 22:52:48 +0100 Subject: [PATCH 069/104] Add missing initialization of bars in Bar issue3073 review730002 (transplanted from 4ce2f0028aa8ac17114588eacf668de236f9d25a) --- tryton/gui/window/view_form/view/graph_gtk/bar.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tryton/gui/window/view_form/view/graph_gtk/bar.py b/tryton/gui/window/view_form/view/graph_gtk/bar.py index 493b65c594..6833a21cde 100644 --- a/tryton/gui/window/view_form/view/graph_gtk/bar.py +++ b/tryton/gui/window/view_form/view/graph_gtk/bar.py @@ -12,6 +12,10 @@ import tryton.rpc as rpc class Bar(Graph): + def __init__(self, *args, **kwargs): + super(Bar, self).__init__(*args, **kwargs) + self.bars = [] + def drawGraph(self, cr, width, height): def drawBar(bar): From dd5937abcb0dac187bb96adbe3767fbe1eedca08 Mon Sep 17 00:00:00 2001 From: Ralf Peschke Date: Fri, 29 Mar 2013 08:28:27 +0100 Subject: [PATCH 070/104] Prevent o2m-records to be deleted on client-side When I delete some records of an one2many-field in an on-change-Event, the deleted records are sent to the client and the client hides them in the list. Pressing the save button the record loads again including the deleted o2m-records. The reason is the force_remove-flag. This will delete the record on the client-side and therefore this change will not be reflected on the server side. issue3091 review744002 (transplanted from e40190b28b312f3dd63bceb167698a885826ea0e) --- tryton/gui/window/view_form/model/field.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tryton/gui/window/view_form/model/field.py b/tryton/gui/window/view_form/model/field.py index 1f1aa33e4b..c71faa3aa1 100644 --- a/tryton/gui/window/view_form/model/field.py +++ b/tryton/gui/window/view_form/model/field.py @@ -699,7 +699,7 @@ class O2MField(CharField): to_remove.append(record2) for record2 in to_remove: record.value[self.name].remove(record2, signal=False, - force_remove=True) + force_remove=False) if value and (value.get('add') or value.get('update', [])): record.value[self.name].add_fields(fields, signal=False) From 09a01dcb2fec1743970beb5baff1d629d56ac723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 2 May 2013 09:30:28 +0200 Subject: [PATCH 071/104] Prepare release 2.4.5 --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 80e703c066..436841cafa 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Version 2.4.5 - 2013-05-02 +* Bug fixes (see mercurial logs for details) + Version 2.4.4 - 2013-02-12 * Bug fixes (see mercurial logs for details) From 7c52804b343c1c644bcb5adc6872935149ac64e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 2 May 2013 09:30:29 +0200 Subject: [PATCH 072/104] Added tag 2.4.5 for changeset 50e628545fa3 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 9cb4d39d70..90f047e74d 100644 --- a/.hgtags +++ b/.hgtags @@ -10,3 +10,4 @@ fe15da23f7333e0e90d2693aadddf6bed53ed602 2.4.0 733b3f7022001aad274cd502d7e34fc9231af8b6 2.4.2 0e4ac607e0a048972a6b5c5f90651a25fae25c6e 2.4.3 4207622ad8b1c6885e3e36265906035b950d7b89 2.4.4 +50e628545fa3f690fc5cad5e572e95f8bfd21b75 2.4.5 From 93452f95b82ca3ea515b80ed41ed99b93dcb4f40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 2 May 2013 09:30:49 +0200 Subject: [PATCH 073/104] Increase version number --- tryton/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tryton/version.py b/tryton/version.py index 30c6cedbba..809ded80da 100644 --- a/tryton/version.py +++ b/tryton/version.py @@ -1,6 +1,6 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. PACKAGE = "tryton" -VERSION = "2.4.5" +VERSION = "2.4.6" LICENSE = "GPL-3" WEBSITE = "http://www.tryton.org/" From 57646c7232d8e7805bcda098db13240a4b10ee3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 24 May 2013 12:40:34 +0200 Subject: [PATCH 074/104] Use jsonrpc object_hook to load json from URL (transplanted from ead6182dfa926891e9a429363c23466de561b5f0) --- tryton/gui/main.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/tryton/gui/main.py b/tryton/gui/main.py index fcf35adb1e..26a8e42e8e 100644 --- a/tryton/gui/main.py +++ b/tryton/gui/main.py @@ -19,6 +19,7 @@ from tryton.common import RPCExecute, RPCException from tryton.config import CONFIG, TRYTON_ICON, get_config_dir import tryton.common as common from tryton.pyson import PYSONDecoder +from tryton.jsonrpc import object_hook from tryton.action import Action from tryton.exceptions import TrytonServerError, TrytonError, \ TrytonServerUnavailable @@ -1436,9 +1437,12 @@ class Main(object): limit = json.loads(params.get('limit', 'null')) auto_refresh = json.loads(params.get('auto_refresh', 'false')) name = json.loads(params.get('window_name', 'false')) - search_value = json.loads(params.get('search_value', '{}')) - domain = json.loads(params.get('domain', '[]')) - context = json.loads(params.get('context', '{}')) + search_value = json.loads(params.get('search_value', '{}'), + object_hook=object_hook) + domain = json.loads(params.get('domain', '[]'), + object_hook=object_hook) + context = json.loads(params.get('context', '{}'), + object_hook=object_hook) except ValueError: return if path: @@ -1459,13 +1463,15 @@ class Main(object): if not wizard: return try: - data = json.loads(params.get('data', '{}')) + data = json.loads(params.get('data', '{}'), + object_hook=object_hook) direct_print = json.loads(params.get('direct_print', 'false')) email_print = json.loads(params.get('email_print', 'false')) email = json.loads(params.get('email', 'null')) name = json.loads(params.get('name', 'false')) window = json.loads(params.get('window', 'false')) - context = json.loads(params.get('context', '{}')) + context = json.loads(params.get('context', '{}'), + object_hook=object_hook) except ValueError: return try: @@ -1480,11 +1486,12 @@ class Main(object): if not report: return try: - data = json.loads(params.get('data')) + data = json.loads(params.get('data'), object_hook=object_hook) direct_print = json.loads(params.get('direct_print', 'false')) email_print = json.loads(params.get('email_print', 'false')) email = json.loads(params.get('email', 'null')) - context = json.loads(params.get('context', '{}')) + context = json.loads(params.get('context', '{}'), + object_hook=object_hook) except ValueError: return try: From 2c55b91e32acb58e3fc1cf00c51e520044cb182d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 9 Jun 2013 22:29:28 +0200 Subject: [PATCH 075/104] Prepare release 2.4.6 --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 436841cafa..9e595fd001 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Version 2.4.6 - 2013-06-09 +* Bug fixes (see mercurial logs for details) + Version 2.4.5 - 2013-05-02 * Bug fixes (see mercurial logs for details) From a96f682a0acca6bca195c5b645c35e66a1419c9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 9 Jun 2013 22:29:29 +0200 Subject: [PATCH 076/104] Added tag 2.4.6 for changeset 74255ad128ab --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 90f047e74d..304dd8e886 100644 --- a/.hgtags +++ b/.hgtags @@ -11,3 +11,4 @@ fe15da23f7333e0e90d2693aadddf6bed53ed602 2.4.0 0e4ac607e0a048972a6b5c5f90651a25fae25c6e 2.4.3 4207622ad8b1c6885e3e36265906035b950d7b89 2.4.4 50e628545fa3f690fc5cad5e572e95f8bfd21b75 2.4.5 +74255ad128ab5ce3792823e18982477192b5c6d9 2.4.6 From cff4c46d863d96808c8b1ae020eae0e8a20ee895 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 9 Jun 2013 22:29:53 +0200 Subject: [PATCH 077/104] Increase version number --- tryton/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tryton/version.py b/tryton/version.py index 809ded80da..bd2fb07df7 100644 --- a/tryton/version.py +++ b/tryton/version.py @@ -1,6 +1,6 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. PACKAGE = "tryton" -VERSION = "2.4.6" +VERSION = "2.4.7" LICENSE = "GPL-3" WEBSITE = "http://www.tryton.org/" From a25a5296f55e42a1d6b21b4c8b3d3c3253a5ee6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 7 Jun 2013 19:28:16 +0200 Subject: [PATCH 078/104] del editabletree_entry on remove-widget instead of quit_cell issue3245 review918002 (transplanted from bc89814018e620da9d507688c1ba7047addcc39b) --- tryton/gui/window/view_form/view/list_gtk/editabletree.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tryton/gui/window/view_form/view/list_gtk/editabletree.py b/tryton/gui/window/view_form/view/list_gtk/editabletree.py index 3a0b2ba47a..8ce628fb34 100644 --- a/tryton/gui/window/view_form/view/list_gtk/editabletree.py +++ b/tryton/gui/window/view_form/view/list_gtk/editabletree.py @@ -24,8 +24,6 @@ class EditableTreeView(gtk.TreeView): def on_quit_cell(self, current_record, fieldname, value, callback=None): field = current_record[fieldname] - if hasattr(field, 'editabletree_entry'): - del field.editabletree_entry cell = self.cells[fieldname] # The value has not changed and is valid ... do nothing. @@ -233,6 +231,11 @@ class EditableTreeView(gtk.TreeView): # store in the record the entry widget to get the value in # set_value field.editabletree_entry = entry + + def remove_widget(cell): + if hasattr(field, 'editabletree_entry'): + del field.editabletree_entry + entry.connect('remove-widget', remove_widget) record.modified_fields.setdefault(column.name) return False From 472f9cc0a5ab405107a36e7aba93b71faa1df6ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 22 Jul 2013 11:42:05 +0200 Subject: [PATCH 079/104] Prepare release 2.4.7 --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 9e595fd001..b4dd532482 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Version 2.4.7 - 2013-07-22 +* Bug fixes (see mercurial logs for details) + Version 2.4.6 - 2013-06-09 * Bug fixes (see mercurial logs for details) From df0412a22f77177b490e33179eca80a14ef5dfe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 22 Jul 2013 11:42:05 +0200 Subject: [PATCH 080/104] Added tag 2.4.7 for changeset b7f74f142071 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 304dd8e886..ffd2ee20b2 100644 --- a/.hgtags +++ b/.hgtags @@ -12,3 +12,4 @@ fe15da23f7333e0e90d2693aadddf6bed53ed602 2.4.0 4207622ad8b1c6885e3e36265906035b950d7b89 2.4.4 50e628545fa3f690fc5cad5e572e95f8bfd21b75 2.4.5 74255ad128ab5ce3792823e18982477192b5c6d9 2.4.6 +b7f74f142071f979d7f9747e9138427fc6e1d639 2.4.7 From cff26ed06cf36df281b571256900263afea5df3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 22 Jul 2013 11:42:35 +0200 Subject: [PATCH 081/104] Increase version number --- tryton/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tryton/version.py b/tryton/version.py index bd2fb07df7..538a5a1ace 100644 --- a/tryton/version.py +++ b/tryton/version.py @@ -1,6 +1,6 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. PACKAGE = "tryton" -VERSION = "2.4.7" +VERSION = "2.4.8" LICENSE = "GPL-3" WEBSITE = "http://www.tryton.org/" From b5092645af73b2ca7ee91f591768b2724af02652 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 22 Jul 2013 14:43:55 +0200 Subject: [PATCH 082/104] Fix domain composition between screen and attribute domains domains can not be summed because they can be of 'OR' type review985002 (transplanted from ba60f315fc3e9346c438edd10cdcc2470abd1317) --- tryton/gui/window/view_form/model/field.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tryton/gui/window/view_form/model/field.py b/tryton/gui/window/view_form/model/field.py index c71faa3aa1..32424b62bb 100644 --- a/tryton/gui/window/view_form/model/field.py +++ b/tryton/gui/window/view_form/model/field.py @@ -58,13 +58,13 @@ class CharField(object): def domain_get(self, record): screen_domain, attr_domain = self.domains_get(record) - return localize_domain(screen_domain) + attr_domain + return [localize_domain(screen_domain), attr_domain] def validation_domains(self, record): screen_domain, attr_domain = self.domains_get(record) if attr_domain: - return (screen_domain, screen_domain + - unlocalize_domain(attr_domain, self.name)) + return (screen_domain, [screen_domain, + unlocalize_domain(attr_domain, self.name)]) else: return screen_domain, screen_domain @@ -442,8 +442,8 @@ class M2OField(CharField): def domain_get(self, record): screen_domain, attr_domain = self.domains_get(record) - return (localize_domain(inverse_leaf(screen_domain), self.name) - + attr_domain) + return [localize_domain(inverse_leaf(screen_domain), self.name), + attr_domain] def get_state_attrs(self, record): result = super(M2OField, self).get_state_attrs(record) @@ -750,7 +750,7 @@ class O2MField(CharField): def domain_get(self, record): screen_domain, attr_domain = self.domains_get(record) - return localize_domain(inverse_leaf(screen_domain)) + attr_domain + return [localize_domain(inverse_leaf(screen_domain)), attr_domain] class M2MField(O2MField): From ac720ef80d0a2b96a205dcd1f8bb1cc685d7e72d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 1 Aug 2013 15:59:57 +0200 Subject: [PATCH 083/104] Fix fallback value of get_preferences (transplanted from b70b126c0b22c7db6ad9aeb30a752b47eaeeeba2) --- tryton/gui/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tryton/gui/main.py b/tryton/gui/main.py index 26a8e42e8e..9d90cfecee 100644 --- a/tryton/gui/main.py +++ b/tryton/gui/main.py @@ -928,7 +928,7 @@ class Main(object): prefs = RPCExecute('model', 'res.user', 'get_preferences', False) except RPCException: - prefs = None + prefs = {} common.ICONFACTORY.load_icons() if prefs and 'language_direction' in prefs: translate.set_language_direction(prefs['language_direction']) From d1deab26bf319a9231bb22e18a51de0e45a5cbb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 4 Sep 2013 10:41:21 +0200 Subject: [PATCH 084/104] Don't use field digits to format float/Decimal in domain_parser The conversion of a domain into string should keep the same significant digit, which means to not use the field digits definition but significant digit of the value. issue3266 review1041002 (transplanted from add046625334e0237bc6fc9d684427e6de1b1903) --- tryton/common/domain_parser.py | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/tryton/common/domain_parser.py b/tryton/common/domain_parser.py index d8873a0134..1ba888933d 100644 --- a/tryton/common/domain_parser.py +++ b/tryton/common/domain_parser.py @@ -378,10 +378,10 @@ def format_value(field, value): and not isinstance(value, (float, Decimal))): return '' try: - digit = int(field.get('digits', (16, 2))[1]) - except ValueError: - digit = 2 - return locale.format('%.' + str(digit) + 'f', value or 0.0, True) + digit = len(str(value).split('.')[1]) + except IndexError: + digit = 0 + return locale.format('%.*f', (digit, value or 0), True) def format_selection(): selections = dict(field['selection']) @@ -447,13 +447,14 @@ def test_format_integer(): def test_format_float(): field = { 'type': 'float', - 'digits': (16, 2), } for value, result in ( - (1, '1.00'), - (1.5, '1.50'), - (0, '0.00'), - (0.0, '0.00'), + (1, '1'), + (1.5, '1.5'), + (1.50, '1.5'), + (150.79, '150.79'), + (0, '0'), + (0.0, '0.0'), (False, ''), (None, ''), ): @@ -463,13 +464,14 @@ def test_format_float(): def test_format_numeric(): field = { 'type': 'numeric', - 'digits': (16, 2), } for value, result in ( - (Decimal(1), '1.00'), - (Decimal('1.5'), '1.50'), - (Decimal(0), '0.00'), - (Decimal('0.0'), '0.00'), + (Decimal(1), '1'), + (Decimal('1.5'), '1.5'), + (Decimal('1.50'), '1.50'), + (Decimal('150.79'), '150.79'), + (Decimal(0), '0'), + (Decimal('0.0'), '0.0'), (False, ''), (None, ''), ): From c29651f993622f4da0ffe2d611624041b60505f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 10 Oct 2013 23:23:06 +0200 Subject: [PATCH 085/104] Prepare release 2.4.8 --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index b4dd532482..b9cd986ac8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Version 2.4.8 - 2013-10-10 +* Bug fixes (see mercurial logs for details) + Version 2.4.7 - 2013-07-22 * Bug fixes (see mercurial logs for details) From dc53f988d2e6e3f86651cdc84278b4ff3352242b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 10 Oct 2013 23:23:07 +0200 Subject: [PATCH 086/104] Added tag 2.4.8 for changeset 3c42199fdfdc --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index ffd2ee20b2..f3ca0ef43b 100644 --- a/.hgtags +++ b/.hgtags @@ -13,3 +13,4 @@ fe15da23f7333e0e90d2693aadddf6bed53ed602 2.4.0 50e628545fa3f690fc5cad5e572e95f8bfd21b75 2.4.5 74255ad128ab5ce3792823e18982477192b5c6d9 2.4.6 b7f74f142071f979d7f9747e9138427fc6e1d639 2.4.7 +3c42199fdfdc35a1ec7c8b1e46a52cacf1c3d474 2.4.8 From c1013f281dabda46f983a3cadc8b1053130bd432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 10 Oct 2013 23:23:31 +0200 Subject: [PATCH 087/104] Increase version number --- tryton/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tryton/version.py b/tryton/version.py index 538a5a1ace..dece4337a4 100644 --- a/tryton/version.py +++ b/tryton/version.py @@ -1,6 +1,6 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. PACKAGE = "tryton" -VERSION = "2.4.8" +VERSION = "2.4.9" LICENSE = "GPL-3" WEBSITE = "http://www.tryton.org/" From ec8d0415632559f7c5b452c7cbe82814294bdd22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 28 Sep 2013 14:56:27 +0200 Subject: [PATCH 088/104] Fix Reference widget when no model is selected, value is just text issue3352 review1090002 (transplanted from 5b7b2c1ba521565cb9e052c33f980a28589a47de) --- tryton/gui/window/view_form/view/form_gtk/reference.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tryton/gui/window/view_form/view/form_gtk/reference.py b/tryton/gui/window/view_form/view/form_gtk/reference.py index 651982afc6..b96a17d836 100644 --- a/tryton/gui/window/view_form/view/form_gtk/reference.py +++ b/tryton/gui/window/view_form/view/form_gtk/reference.py @@ -111,7 +111,12 @@ class Reference(Many2One): return self.wid_text.set_text('') self.wid_text.set_position(0) - self.field.set_client(self.record, (self.get_model(), (-1, ''))) + model = self.get_model() + if model: + value = (model, (-1, '')) + else: + value = ('', '') + self.field.set_client(self.record, value) def set_value(self, record, field): if not self.get_model(): From 99e59d9e3ea835dbf10e067d6c29dea7ff12856e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 2 Oct 2013 21:16:47 +0200 Subject: [PATCH 089/104] Don't extend domain but wrap into a new list review1106002 (transplanted from e4a2d43ad02e5c5d3ddd4c22a3c2a0890afff0aa) --- tryton/gui/window/view_form/view/form_gtk/one2many.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tryton/gui/window/view_form/view/form_gtk/one2many.py b/tryton/gui/window/view_form/view/form_gtk/one2many.py index bf3ca67cd4..24c136e34e 100644 --- a/tryton/gui/window/view_form/view/form_gtk/one2many.py +++ b/tryton/gui/window/view_form/view/form_gtk/one2many.py @@ -305,8 +305,7 @@ class One2Many(WidgetInterface): self.view.set_value() domain = self.field.domain_get(self.record) context = self.field.context_get(self.record) - domain = domain[:] - domain.extend(self.record.expr_eval(self.attrs.get('add_remove'))) + domain = [domain, self.record.expr_eval(self.attrs.get('add_remove'))] removed_ids = self.field.get_removed_ids(self.record) self.focus_out = False From fda5b0132ed4b4697e8e43488a3c7208d9bcb651 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 2 Oct 2013 21:17:30 +0200 Subject: [PATCH 090/104] Fix move_into when record is not saved review1107002 (transplanted from d4d4ec2782007eab231325592e4885da76ce1381) --- tryton/gui/window/view_form/view/list.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tryton/gui/window/view_form/view/list.py b/tryton/gui/window/view_form/view/list.py index ddb86e212d..f22d481f13 100644 --- a/tryton/gui/window/view_form/view/list.py +++ b/tryton/gui/window/view_form/view/list.py @@ -121,7 +121,8 @@ class AdaptModelGroup(gtk.GenericTreeModel): # Don't remove record from previous group # as the new parent will change the parent # This prevents concurrency conflict - record.group.record_removed.remove(record) + if record in record.group.record_removed: + record.group.record_removed.remove(record) group.add(record) record.modified_fields.setdefault(record.parent_name or 'id') group.move(record, 0) From bac5f4075624e95729a56c2a62dead4d1b3162bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 9 Oct 2013 18:19:26 +0200 Subject: [PATCH 091/104] Fix restore predefined export with more than 2 levels depth issue3402 review1124003 (transplanted from 30b291e0bf4f17a0219b4572cee67e8fcc07acc7) --- tryton/gui/window/win_export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tryton/gui/window/win_export.py b/tryton/gui/window/win_export.py index 949d8d1ed9..63748784d8 100644 --- a/tryton/gui/window/win_export.py +++ b/tryton/gui/window/win_export.py @@ -352,7 +352,7 @@ class WinExport(object): self.on_row_expanded(self.view1, iter, self.model1.get_path(iter)) iter = self.model1.iter_children(iter) - prefix = parent + '/' + prefix += parent + '/' break else: iter = self.model1.iter_next(iter) From bb89e16c54b47438c2bf02b71e97be80bb4c54f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 3 Nov 2013 18:41:31 +0100 Subject: [PATCH 092/104] Sanitize report file extension issue3446 (transplanted from 357d0a4d9cb8b3f0f4192cb87996a5720bf1cb37) --- CHANGELOG | 2 ++ tryton/action/main.py | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index b9cd986ac8..37c05d7bc6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,5 @@ +* Sanitize report file extension + Version 2.4.8 - 2013-10-10 * Bug fixes (see mercurial logs for details) diff --git a/tryton/action/main.py b/tryton/action/main.py index 88f1698c2f..d733ea1aaf 100644 --- a/tryton/action/main.py +++ b/tryton/action/main.py @@ -42,7 +42,8 @@ class Action(object): dtemp = tempfile.mkdtemp(prefix='tryton_') fp_name = os.path.join(dtemp, name.replace(os.sep, '_').replace(os.altsep or os.sep, '_') \ - + os.extsep + type) + + os.extsep + + type.replace(os.sep, '_').replace(os.altsep or os.sep, '_')) with open(fp_name, 'wb') as file_d: file_d.write(data) if email_print: From 6253bbb72fe7fc73ba957bfc4a5ebd9be65cc0c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 3 Nov 2013 18:51:52 +0100 Subject: [PATCH 093/104] Prepare release 2.4.9 --- CHANGELOG | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 37c05d7bc6..04e4fce54a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,5 @@ +Version 2.4.9 - 2013-11-03 +* Bug fixes (see mercurial logs for details) * Sanitize report file extension Version 2.4.8 - 2013-10-10 From 5813f7c3a26ad3b1a70e71189db0cd466165a01f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 3 Nov 2013 18:51:53 +0100 Subject: [PATCH 094/104] Added tag 2.4.9 for changeset 675708fca92f --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index f3ca0ef43b..f7333626a6 100644 --- a/.hgtags +++ b/.hgtags @@ -14,3 +14,4 @@ fe15da23f7333e0e90d2693aadddf6bed53ed602 2.4.0 74255ad128ab5ce3792823e18982477192b5c6d9 2.4.6 b7f74f142071f979d7f9747e9138427fc6e1d639 2.4.7 3c42199fdfdc35a1ec7c8b1e46a52cacf1c3d474 2.4.8 +675708fca92f27625e0619e9a5444e8abbc289d6 2.4.9 From 98796205ebbeb333f6f57726a776cb16ac4dbe5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 3 Nov 2013 18:52:17 +0100 Subject: [PATCH 095/104] Increase version number --- tryton/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tryton/version.py b/tryton/version.py index dece4337a4..49b334890a 100644 --- a/tryton/version.py +++ b/tryton/version.py @@ -1,6 +1,6 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. PACKAGE = "tryton" -VERSION = "2.4.9" +VERSION = "2.4.10" LICENSE = "GPL-3" WEBSITE = "http://www.tryton.org/" From be45c1c050399ca43128c7293a5ecf4a6bf7b99c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 18 Oct 2013 22:25:45 +0200 Subject: [PATCH 096/104] Quote user and password in the roundup XML-RPC URL review1143002 (transplanted from 8d9757fd30187f3bb35287b10020dd04e320f8d6) --- tryton/common/common.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tryton/common/common.py b/tryton/common/common.py index c1f3d5a3da..3b0cc6dd24 100644 --- a/tryton/common/common.py +++ b/tryton/common/common.py @@ -10,6 +10,7 @@ import os import subprocess import re import logging +from functools import partial from tryton.config import CONFIG from tryton.config import TRYTON_ICON, PIXMAPS_DIR import time @@ -897,9 +898,10 @@ def send_bugtracker(title, msg): protocol = 'http' if ssl or hasattr(socket, 'ssl'): protocol = 'https' + quote = partial(urllib.quote, safe="!$&'()*+,;=:") server = xmlrpclib.Server( ('%s://%s:%s@' + CONFIG['roundup.xmlrpc']) - % (protocol, user, password), allow_none=True) + % (protocol, quote(user), quote(password)), allow_none=True) if hashlib: msg_md5 = hashlib.md5(msg + '\n' + title).hexdigest() else: From fcb547c1ead5ee4225d35816d37af4b63867f77c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 3 Nov 2013 20:42:34 +0100 Subject: [PATCH 097/104] Don't set image on button if it is still the same It fixes also a strange behavior of GTK that doesn't display any images if the icon is changed before it is realized. issue3191 review1451002 (transplanted from 4bc44691dc89dfd7802c9d84bbb466f98038bb75) --- tryton/common/button.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tryton/common/button.py b/tryton/common/button.py index 2adae35ba6..5a695e8cc3 100644 --- a/tryton/common/button.py +++ b/tryton/common/button.py @@ -17,6 +17,11 @@ class Button(gtk.Button): self._set_icon(attrs.get('icon')) def _set_icon(self, stock): + image = self.get_image() + if not image and not stock: + return + elif image and image.get_stock()[0] == stock: + return if not stock: self.set_image(gtk.Image()) return From f4f748ff045afc9b485c06c5366385b52b648554 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=C3=89vrard?= Date: Fri, 15 Nov 2013 15:03:00 +0100 Subject: [PATCH 098/104] Use get_eval for check_required computation issue3473 review1561002 (transplanted from 4786286136344caf3e4bda27c23ac1ab911d9380) --- tryton/gui/window/view_form/model/field.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tryton/gui/window/view_form/model/field.py b/tryton/gui/window/view_form/model/field.py index 32424b62bb..21eb409798 100644 --- a/tryton/gui/window/view_form/model/field.py +++ b/tryton/gui/window/view_form/model/field.py @@ -80,7 +80,7 @@ class CharField(object): def check_required(self, record): state_attrs = self.get_state_attrs(record) if bool(int(state_attrs.get('required') or 0)): - if (not self.get(record) + if (not self.get_eval(record) and not bool(int(state_attrs.get('readonly') or 0))): return False return True From 3d29bf47a25981c7c85fc5dbce216009befe71ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 4 Dec 2013 18:03:19 +0100 Subject: [PATCH 099/104] Prepare release 2.4.10 --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 04e4fce54a..5e97561920 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +Version 2.4.10 - 2013-12-04 +* Bug fixes (see mercurial logs for details) + Version 2.4.9 - 2013-11-03 * Bug fixes (see mercurial logs for details) * Sanitize report file extension From de4b7143eb26b568aa6e329e2d95ed0b1452b21d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 4 Dec 2013 18:03:20 +0100 Subject: [PATCH 100/104] Added tag 2.4.10 for changeset ccc098c7a309 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index f7333626a6..c92a76ecdf 100644 --- a/.hgtags +++ b/.hgtags @@ -15,3 +15,4 @@ fe15da23f7333e0e90d2693aadddf6bed53ed602 2.4.0 b7f74f142071f979d7f9747e9138427fc6e1d639 2.4.7 3c42199fdfdc35a1ec7c8b1e46a52cacf1c3d474 2.4.8 675708fca92f27625e0619e9a5444e8abbc289d6 2.4.9 +ccc098c7a309d87a968a5a43148627f311078260 2.4.10 From 5490d54f9a6e5d963f929fce0e98562de347d52c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 4 Dec 2013 18:04:11 +0100 Subject: [PATCH 101/104] Increase version number --- tryton/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tryton/version.py b/tryton/version.py index 49b334890a..7b365ce19e 100644 --- a/tryton/version.py +++ b/tryton/version.py @@ -1,6 +1,6 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. PACKAGE = "tryton" -VERSION = "2.4.10" +VERSION = "2.4.11" LICENSE = "GPL-3" WEBSITE = "http://www.tryton.org/" From 03a3d7097c39c1e02ba89c7f1c95a62a8f602835 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 26 Jan 2014 22:17:03 +0100 Subject: [PATCH 102/104] Encode gettext value to match with string value issue3587 review2991002 (transplanted from 80a479e15e31f5a97eaf26ddbd3f9a89303c595e) --- tryton/common/domain_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tryton/common/domain_parser.py b/tryton/common/domain_parser.py index 1ba888933d..a83c594850 100644 --- a/tryton/common/domain_parser.py +++ b/tryton/common/domain_parser.py @@ -178,7 +178,7 @@ def convert_value(field, value): def convert_boolean(): if isinstance(value, basestring): - return any(test.lower().startswith(value.lower()) + return any(test.decode('utf-8').lower().startswith(value.lower()) for test in (_('y'), _('yes'), _('true'), _('t'), '1')) else: return bool(value) From 47a89aa7b45d3b6697ca254efdd572c3a42111ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 6 Feb 2014 22:36:44 +0100 Subject: [PATCH 103/104] Fix find button in search window It should use the text filled in the search entry. And it should not close the window if nothing is found. review3101003 (transplanted from 8b22f7366f7caeb7ecbe86c28b8b4f8f3bf32627) --- tryton/gui/window/win_search.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tryton/gui/window/win_search.py b/tryton/gui/window/win_search.py index 68ad5e9d55..53cc9f0ffe 100644 --- a/tryton/gui/window/win_search.py +++ b/tryton/gui/window/win_search.py @@ -103,10 +103,8 @@ class WinSearch(NoModal): if response_id == gtk.RESPONSE_OK: res = self.screen.sel_ids_get() elif response_id == gtk.RESPONSE_APPLY: - if not self.screen.search_filter(): - res = self.screen.sel_ids_get() - else: - return + self.screen.search_filter(self.screen.screen_container.get_text()) + return elif response_id == gtk.RESPONSE_ACCEPT: screen = Screen(self.model_name, domain=self.domain, context=self.context, mode=['form']) From 8217924234cba5669bd6fb0c28531ed990375af1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 9 Feb 2014 23:54:56 +0100 Subject: [PATCH 104/104] Don't set missing default value to False False is not always a good valid value for the field, so it is better to let the field dealing with default value. issue3643 review3281002 (transplanted from 9a16381241f48ca880e7544edeef54c255c99255) --- tryton/gui/window/view_form/model/group.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tryton/gui/window/view_form/model/group.py b/tryton/gui/window/view_form/model/group.py index ab0a8b06a1..45dbefe58d 100644 --- a/tryton/gui/window/view_form/model/group.py +++ b/tryton/gui/window/view_form/model/group.py @@ -379,9 +379,6 @@ class Group(SignalEvent, list): to_add.keys(), main_iteration=False, context=ctx) except RPCException: return False - for name in to_add: - if name not in values: - values[name] = False for record in new: record.set_default(values, signal=signal)