diff --git a/CHANGELOG b/CHANGELOG index 01257f2119..4d70bd57b7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Manage deletable and writable state * Support e-mail template * Send e-mail via the server * Support PYSON comparison of date and datetime diff --git a/tryton/gui/window/form.py b/tryton/gui/window/form.py index 9c27a0457a..f714e19640 100644 --- a/tryton/gui/window/form.py +++ b/tryton/gui/window/form.py @@ -302,7 +302,8 @@ class Form(SignalEvent, TabContent): self.menu_buttons[name].props.sensitive = False def sig_remove(self, widget=None): - if not common.MODELACCESS[self.model]['delete']: + if (not common.MODELACCESS[self.model]['delete'] + or not self.screen.deletable): return if self.screen.current_view.view_type == 'form': msg = _('Are you sure to remove this record?') @@ -379,7 +380,8 @@ class Form(SignalEvent, TabContent): # Called from button so we must save the tree state self.screen.save_tree_state() if not (common.MODELACCESS[self.model]['write'] - or common.MODELACCESS[self.model]['create']): + or common.MODELACCESS[self.model]['create'] + or self.screen.writable): return if self.screen.save_current(): self.message_info(_('Record saved.'), Gtk.MessageType.INFO) @@ -423,6 +425,7 @@ class Form(SignalEvent, TabContent): break self.screen.display(set_cursor=set_cursor) self.message_info() + self.set_buttons_sensitive() self.activate_save() self.screen.count_tab_domain() return True @@ -509,11 +512,18 @@ class Form(SignalEvent, TabContent): can_be_sensitive |= any( b.attrs.get('keyword', 'action') == action_type for b in screen.get_buttons()) + elif button_id == 'save': + can_be_sensitive &= not self.screen.readonly button.props.sensitive = (bool(signal_data[0]) and can_be_sensitive) button_switch = self.buttons['switch'] button_switch.props.sensitive = self.screen.number_of_views > 1 + menu_delete = self.menu_buttons['remove'] + menu_delete.props.sensitive = self.screen.deletable + menu_save = self.menu_buttons['save'] + menu_save.props.sensitive = not self.screen.readonly + msg = name + ' / ' + str(signal_data[1]) if signal_data[1] < signal_data[2]: msg += _(' of ') + str(signal_data[2]) diff --git a/tryton/gui/window/view_form/model/record.py b/tryton/gui/window/view_form/model/record.py index 415df1416e..dc3ed4c1bf 100644 --- a/tryton/gui/window/view_form/model/record.py +++ b/tryton/gui/window/view_form/model/record.py @@ -28,6 +28,8 @@ class Record(SignalEvent): self.state_attrs = {} self.modified_fields = {} self._timestamp = None + self._write = True + self._delete = True self.resources = None self.button_clicks = {} self.links_counts = {} @@ -73,7 +75,7 @@ class Record(SignalEvent): in ('many2one', 'one2one', 'reference'))) if 'rec_name' not in fnames: fnames.append('rec_name') - fnames.append('_timestamp') + fnames.extend(['_timestamp', '_write', '_delete']) record_context = self.get_context() if loading == 'eager': @@ -226,10 +228,17 @@ class Record(SignalEvent): deleted = property(get_deleted) def get_readonly(self): - return self.deleted or self.removed or self.exception + return (self.deleted + or self.removed + or self.exception + or not self._write) readonly = property(get_readonly) + @property + def deletable(self): + return self._delete + def fields_get(self): return self.group.fields @@ -436,6 +445,9 @@ class Record(SignalEvent): if not self._timestamp: self._timestamp = value continue + if fieldname in {'_write', '_delete'}: + setattr(self, fieldname, value) + continue if fieldname not in self.group.fields: if fieldname == 'rec_name': self.value['rec_name'] = value diff --git a/tryton/gui/window/view_form/screen/screen.py b/tryton/gui/window/view_form/screen/screen.py index 28bc1de3c1..28d8eb4d00 100644 --- a/tryton/gui/window/view_form/screen/screen.py +++ b/tryton/gui/window/view_form/screen/screen.py @@ -142,6 +142,19 @@ class Screen(SignalEvent): def __repr__(self): return '' % (self.model_name, id(self)) + @property + def readonly(self): + return (self.__readonly + or any(r.readonly for r in self.selected_records)) + + @readonly.setter + def readonly(self, value): + self.__readonly = value + + @property + def deletable(self): + return all(r.deletable for r in self.selected_records) + def search_active(self, active=True): if active and not self.parent: self.screen_container.set_screen(self) @@ -413,7 +426,7 @@ class Screen(SignalEvent): def new_group(self, context=None): context = context if context is not None else self.context self.group = Group(self.model_name, {}, domain=self.domain, - context=context, readonly=self.readonly) + context=context, readonly=self.__readonly) def _group_cleared(self, group, signal): for view in self.views: @@ -1104,7 +1117,7 @@ class Screen(SignalEvent): @property def selected_records(self): - return self.current_view.selected_records + return self.current_view.selected_records if self.current_view else [] def clear(self): self.current_record = None @@ -1196,7 +1209,9 @@ class Screen(SignalEvent): if access['create']: self.new() elif action == 'delete': - if access['delete']: + if (access['delete'] + and (self.current_record.deletable + if self.current_record else True)): self.remove(delete=not self.parent, force_remove=not self.parent) elif action == 'remove': 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 fb392dc125..7a7c4368c8 100644 --- a/tryton/gui/window/view_form/view/form_gtk/one2many.py +++ b/tryton/gui/window/view_form/view/form_gtk/one2many.py @@ -268,6 +268,7 @@ class One2Many(Widget): if isinstance(self._position, int): first = self._position <= 1 last = self._position >= self._length + deletable = self.screen.deletable self.but_new.set_sensitive(bool( not self._readonly @@ -277,6 +278,7 @@ class One2Many(Widget): self.but_del.set_sensitive(bool( not self._readonly and self.attrs.get('delete', True) + and deletable and self._position and access['delete'])) self.but_undel.set_sensitive(bool( @@ -427,11 +429,13 @@ class One2Many(Widget): def _sig_remove(self, widget, remove=False): access = common.MODELACCESS[self.screen.model_name] + writable = not self.screen.readonly + deletable = self.screen.deletable if remove: - if not access['write'] or not access['read']: + if not access['write'] or not writable or not access['read']: return else: - if not access['delete']: + if not access['delete'] or not deletable: return self.screen.remove(remove=remove) diff --git a/tryton/gui/window/win_form.py b/tryton/gui/window/win_form.py index a3b3d797aa..f5575db616 100644 --- a/tryton/gui/window/win_form.py +++ b/tryton/gui/window/win_form.py @@ -322,6 +322,9 @@ class WinForm(NoModal, InfoBar): def _sig_label(self, screen, signal_data): name = '_' access = common.MODELACCESS[screen.model_name] + deletable = True + if screen.current_record: + deletable = screen.current_record.deletable readonly = screen.group.readonly if signal_data[0] >= 1: name = str(signal_data[0]) @@ -335,7 +338,7 @@ class WinForm(NoModal, InfoBar): self.but_pre.set_sensitive(True) else: self.but_pre.set_sensitive(False) - if access['delete'] and not readonly: + if access['delete'] and not readonly and deletable: self.but_del.set_sensitive(True) self.but_undel.set_sensitive(True) else: