def __init__(self, store, model, visual_mode, ui_form_name, parent): self._parent = parent if ui_form_name: self.db_form = DatabaseForm(ui_form_name) else: self.db_form = None super(_PersonEditorTemplate, self).__init__(store, model, visual_mode=visual_mode)
class _IndividualDocuments(BaseEditorSlave): model_type = Individual gladefile = 'IndividualDocuments' proxy_widgets = ('cpf', 'rg_expedition_date', 'rg_expedition_local', 'rg_number', 'state_registry', 'city_registry') def __init__(self, store, model, visual_mode=None, ui_form_name=None): self.db_form = DatabaseForm(ui_form_name) if ui_form_name else None BaseEditorSlave.__init__(self, store, model, visual_mode=visual_mode) def _setup_widgets(self): self.document_l10n = api.get_l10n_field('person_document') self.cpf_lbl.set_label(self.document_l10n.label + ':') self.cpf.set_mask(self.document_l10n.entry_mask) def _setup_form_fields(self): if not self.db_form: return # Do not update the widget if the model already has a responsible if self.model.responsible: return self.db_form.update_widget(self.cpf, other=self.cpf_lbl) def setup_proxies(self): self._setup_widgets() self._setup_form_fields() self.proxy = self.add_proxy(self.model, _IndividualDocuments.proxy_widgets) def on_cpf__validate(self, widget, value): # This will allow the user to use an empty value to this field if self.cpf.is_empty(): return if not self.document_l10n.validate(value): return ValidationError(_('%s is not valid.') % ( self.document_l10n.label,)) if self.model.check_cpf_exists(value): return ValidationError(_('A person with this %s already exists') % ( self.document_l10n.label,))
class _IndividualDocuments(BaseEditorSlave): model_type = Individual gladefile = 'IndividualDocuments' proxy_widgets = ('cpf', 'rg_expedition_date', 'rg_expedition_local', 'rg_number', 'state_registry', 'city_registry') def __init__(self, store, model, visual_mode=None, ui_form_name=None): self.db_form = DatabaseForm(ui_form_name) if ui_form_name else None BaseEditorSlave.__init__(self, store, model, visual_mode=visual_mode) def _setup_widgets(self): self.document_l10n = api.get_l10n_field('person_document') self.cpf_lbl.set_label(self.document_l10n.label + ':') self.cpf.set_mask(self.document_l10n.entry_mask) def _setup_form_fields(self): if not self.db_form: return # Do not update the widget if the model already has a responsible if self.model.responsible: return self.db_form.update_widget(self.cpf, other=self.cpf_lbl) def setup_proxies(self): self._setup_widgets() self._setup_form_fields() self.proxy = self.add_proxy(self.model, _IndividualDocuments.proxy_widgets) def on_cpf__validate(self, widget, value): # This will allow the user to use an empty value to this field if self.cpf.is_empty(): return if not self.document_l10n.validate(value): return ValidationError( _('%s is not valid.') % (self.document_l10n.label, )) if self.model.check_cpf_exists(value): return ValidationError( _('A person with this %s already exists') % (self.document_l10n.label, ))
def __init__(self, store, model=None, visual_mode=False): from stoqlib.gui.slaves.sellableslave import CategoryPriceSlave is_new = not model self._sellable = None self._demo_mode = sysparam.get_bool('DEMO_MODE') self._requires_weighing_text = ( "<b>%s</b>" % api.escape(_("This unit type requires weighing"))) if self.ui_form_name: self.db_form = DatabaseForm(self.ui_form_name) else: self.db_form = None BaseEditor.__init__(self, store, model, visual_mode) self.enable_window_controls() if self._demo_mode: self._add_demo_warning() # Code suggestion. We need to do this before disabling sensitivity, # otherwise, the sellable will not be updated. if not self.code.read(): self._update_default_sellable_code() edit_code_product = sysparam.get_bool('EDIT_CODE_PRODUCT') self.code.set_sensitive(not edit_code_product and not self.visual_mode) self.description.grab_focus() self.table.set_focus_chain([ self.code, self.barcode, self.default_sale_cfop, self.description, self.cost_hbox, self.price_hbox, self.category_combo, self.tax_hbox, self.unit_combo, ]) self._print_labels_btn = self.add_button('print_labels', gtk.STOCK_PRINT) self._print_labels_btn.connect('clicked', self.on_print_labels_clicked, 'print_labels') label = self._print_labels_btn.get_children()[0] label = label.get_children()[0].get_children()[1] label.set_label(_(u'Print labels')) self.setup_widgets() if not is_new and not self.visual_mode: # Although a sellable can be both removed/closed, we show only one, # to avoid having *lots* of buttons. If it's closed, provide a way # to reopen it, else, show a delete button if it can be removed # or a close button if it can be closed if self._sellable.is_closed(): self._add_reopen_button() elif self._sellable.can_remove(): self._add_delete_button() elif self._sellable.can_close(): self._add_close_button() self.set_main_tab_label(self.model_name) price_slave = CategoryPriceSlave(self.store, self.model.sellable, self.visual_mode) self.add_extra_tab(_(u'Category Prices'), price_slave) self._setup_ui_forms() self._update_print_labels()
class SellableEditor(BaseEditor): """This is a base class for ProductEditor and ServiceEditor and should be used when editing sellable objects. Note that sellable objects are instances inherited by Sellable.""" # This must be be properly defined in the child classes model_name = None model_type = None gladefile = 'SellableEditor' confirm_widgets = ['description', 'cost', 'price'] ui_form_name = None sellable_tax_widgets = ['tax_constant', 'tax_value'] sellable_widgets = [ 'code', 'barcode', 'description', 'category_combo', 'cost', 'price', 'status_str', 'default_sale_cfop', 'unit_combo' ] proxy_widgets = (sellable_tax_widgets + sellable_widgets) def __init__(self, store, model=None, visual_mode=False): from stoqlib.gui.slaves.sellableslave import CategoryPriceSlave is_new = not model self._sellable = None self._demo_mode = sysparam.get_bool('DEMO_MODE') self._requires_weighing_text = ( "<b>%s</b>" % api.escape(_("This unit type requires weighing"))) if self.ui_form_name: self.db_form = DatabaseForm(self.ui_form_name) else: self.db_form = None BaseEditor.__init__(self, store, model, visual_mode) self.enable_window_controls() if self._demo_mode: self._add_demo_warning() # Code suggestion. We need to do this before disabling sensitivity, # otherwise, the sellable will not be updated. if not self.code.read(): self._update_default_sellable_code() edit_code_product = sysparam.get_bool('EDIT_CODE_PRODUCT') self.code.set_sensitive(not edit_code_product and not self.visual_mode) self.description.grab_focus() self.table.set_focus_chain([ self.code, self.barcode, self.default_sale_cfop, self.description, self.cost_hbox, self.price_hbox, self.category_combo, self.tax_hbox, self.unit_combo, ]) self._print_labels_btn = self.add_button('print_labels', gtk.STOCK_PRINT) self._print_labels_btn.connect('clicked', self.on_print_labels_clicked, 'print_labels') label = self._print_labels_btn.get_children()[0] label = label.get_children()[0].get_children()[1] label.set_label(_(u'Print labels')) self.setup_widgets() if not is_new and not self.visual_mode: # Although a sellable can be both removed/closed, we show only one, # to avoid having *lots* of buttons. If it's closed, provide a way # to reopen it, else, show a delete button if it can be removed # or a close button if it can be closed if self._sellable.is_closed(): self._add_reopen_button() elif self._sellable.can_remove(): self._add_delete_button() elif self._sellable.can_close(): self._add_close_button() self.set_main_tab_label(self.model_name) price_slave = CategoryPriceSlave(self.store, self.model.sellable, self.visual_mode) self.add_extra_tab(_(u'Category Prices'), price_slave) self._setup_ui_forms() self._update_print_labels() def _add_demo_warning(self): fmt = _("This is a demostration mode of Stoq, you cannot " "create more than %d products.\n" "To avoid this limitation, enable production mode.") self.set_message(fmt % (_DEMO_PRODUCT_LIMIT)) if self.store.find(Sellable).count() > _DEMO_PRODUCT_LIMIT: self.disable_ok() def _add_extra_button(self, label, stock=None, callback_func=None, connect_on='clicked'): button = self.add_button(label, stock) if callback_func: button.connect(connect_on, callback_func, label) def _add_delete_button(self): self._add_extra_button(_('Remove'), gtk.STOCK_DELETE, self._on_delete_button__clicked) def _add_close_button(self): if self._sellable.product: label = _('Close Product') else: label = _('Close Service') self._add_extra_button(label, None, self._on_close_sellable_button__clicked) def _add_reopen_button(self): if self._sellable.product: label = _('Reopen Product') else: label = _('Reopen Service') self._add_extra_button(label, None, self._on_reopen_sellable_button__clicked) def _update_default_sellable_code(self): code = Sellable.get_max_value(self.store, Sellable.code) self.code.update(next_value_for(code)) def _update_print_labels(self): sellable = self.model.sellable self._print_labels_btn.set_sensitive( all([ sellable.code, sellable.barcode, sellable.description, sellable.price ])) def _setup_ui_forms(self): if not self.db_form: return self.db_form.update_widget(self.code, other=self.code_lbl) self.db_form.update_widget(self.barcode, other=self.barcode_lbl) self.db_form.update_widget(self.category_combo, other=self.category_lbl) # # Public API # def set_main_tab_label(self, tabname): self.sellable_notebook.set_tab_label(self.sellable_tab, gtk.Label(tabname)) def add_extra_tab(self, tabname, tabslave): self.sellable_notebook.set_show_tabs(True) self.sellable_notebook.set_show_border(True) event_box = gtk.EventBox() event_box.show() self.sellable_notebook.append_page(event_box, gtk.Label(tabname)) self.attach_slave(tabname, tabslave, event_box) def set_widget_formats(self): for widget in (self.cost, self.price): widget.set_adjustment( gtk.Adjustment(lower=0, upper=MAX_INT, step_incr=1)) self.requires_weighing_label.set_size("small") self.requires_weighing_label.set_text("") def edit_sale_price(self): sellable = self.model.sellable self.store.savepoint('before_run_editor_sellable_price') result = run_dialog(SellablePriceEditor, self.get_toplevel().get_toplevel(), self.store, sellable) if result: self.sellable_proxy.update('price') else: self.store.rollback_to_savepoint( 'before_run_editor_sellable_price') def setup_widgets(self): raise NotImplementedError def update_requires_weighing_label(self): unit = self._sellable.unit if unit and unit.unit_index == UnitType.WEIGHT: self.requires_weighing_label.set_text(self._requires_weighing_text) else: self.requires_weighing_label.set_text("") def _update_tax_value(self): if not hasattr(self, 'tax_proxy'): return self.tax_proxy.update('tax_constant.tax_value') def get_taxes(self): """Subclasses may override this method to provide a custom tax selection. :returns: a list of tuples containing the tax description and a :class:`stoqlib.domain.sellable.SellableTaxConstant` object. """ return [] def _fill_categories(self): categories = self.store.find(SellableCategory) self.category_combo.set_sensitive( any(categories) and not self.visual_mode) self.category_combo.prefill( api.for_combo(categories, attr='full_description')) # # BaseEditor hooks # def update_visual_mode(self): self.add_category.set_sensitive(False) self.sale_price_button.set_sensitive(False) def setup_sellable_combos(self): self._fill_categories() self.edit_category.set_sensitive(False) cfops = CfopData.get_for_sale(self.store) self.default_sale_cfop.prefill(api.for_combo(cfops, empty='')) self.setup_unit_combo() def setup_unit_combo(self): units = self.store.find(SellableUnit) self.unit_combo.prefill(api.for_combo(units, empty=_('No units'))) def setup_tax_constants(self): taxes = self.get_taxes() self.tax_constant.prefill(taxes) def setup_proxies(self): self.set_widget_formats() self._sellable = self.model.sellable self.add_category.set_tooltip_text(_("Add a new category")) self.edit_category.set_tooltip_text(_("Edit the selected category")) self.setup_sellable_combos() self.setup_tax_constants() self.tax_proxy = self.add_proxy(self._sellable, SellableEditor.sellable_tax_widgets) self.sellable_proxy = self.add_proxy(self._sellable, SellableEditor.sellable_widgets) self.update_requires_weighing_label() def setup_slaves(self): from stoqlib.gui.slaves.sellableslave import SellableDetailsSlave details_slave = SellableDetailsSlave(self.store, self.model.sellable, visual_mode=self.visual_mode) self.attach_slave('slave_holder', details_slave) if isinstance(self.model, Product) and self.model.parent is not None: details_slave.notes.set_property('sensitive', False) # Make everything aligned by pytting notes_lbl on the same size group self.left_labels_group.add_widget(details_slave.notes_lbl) def _run_category_editor(self, category=None): self.store.savepoint('before_run_editor_sellable_category') model = run_dialog(SellableCategoryEditor, self, self.store, category) if model: self._fill_categories() self.category_combo.select(model) else: self.store.rollback_to_savepoint( 'before_run_editor_sellable_category') # # Kiwi handlers # def _on_delete_button__clicked(self, button, parent_button_label=None): sellable_description = self._sellable.get_description() msg = (_("This will delete '%s' from the database. Are you sure?") % sellable_description) if not yesno(msg, gtk.RESPONSE_NO, _("Delete"), _("Keep")): return try: self._sellable.remove() except IntegrityError as details: warning( _("It was not possible to remove '%s'") % sellable_description, str(details)) return # We are doing this by hand instead of calling confirm/cancel because, # if we call self.cancel(), the transaction will not be committed. If # we call self.confirm(), it will, but some on_confirm hooks (like # ProductComponentSlave's one) will try to create other objects and # relate them with this product that doesn't exist anymore (we removed # them above), resulting in an IntegrityError. self.retval = self.model self.store.retval = self.retval self.main_dialog.close() def _on_close_sellable_button__clicked(self, button, parent_button_label=None): msg = (_("Do you really want to close '%s'?\n" "Please note that when it's closed, you won't be able to " "commercialize it anymore.") % self._sellable.get_description()) if not yesno(msg, gtk.RESPONSE_NO, parent_button_label, _("Don't close")): return self._sellable.close() self.confirm() def _on_reopen_sellable_button__clicked(self, button, parent_button_label=None): msg = (_("Do you really want to reopen '%s'?\n" "Note that when it's opened, you will be able to " "commercialize it again.") % self._sellable.get_description()) if not yesno(msg, gtk.RESPONSE_NO, parent_button_label, _("Keep closed")): return self._sellable.set_available() self.confirm() def on_category_combo__content_changed(self, category): self.edit_category.set_sensitive(bool(category.get_selected())) def on_tax_constant__changed(self, combo): self._update_tax_value() def on_unit_combo__changed(self, combo): self.update_requires_weighing_label() def on_sale_price_button__clicked(self, button): self.edit_sale_price() def on_add_category__clicked(self, widget): self._run_category_editor() def on_edit_category__clicked(self, widget): self._run_category_editor(self.category_combo.get_selected()) def on_code__validate(self, widget, value): if not value: return ValidationError(_(u'The code can not be empty.')) if self.model.sellable.check_code_exists(value): return ValidationError(_(u'The code %s already exists.') % value) def on_barcode__validate(self, widget, value): if not value: return if value and len(value) > 14: return ValidationError(_(u'Barcode must have 14 digits or less.')) if self.model.sellable.check_barcode_exists(value): return ValidationError(_('The barcode %s already exists') % value) if self._demo_mode and value not in _DEMO_BAR_CODES: return ValidationError( _("Cannot create new barcodes in " "demonstration mode")) def on_price__validate(self, entry, value): if value <= 0: return ValidationError(_("Price cannot be zero or negative")) def on_cost__validate(self, entry, value): if value <= 0: return ValidationError(_("Cost cannot be zero or negative")) def after_description__changed(self, widget): self._update_print_labels() def after_code__changed(self, widget): self._update_print_labels() def after_barcode__changed(self, widget): self._update_print_labels() def after_price__changed(self, widget): self._update_print_labels() def on_print_labels_clicked(self, button, parent_label_button=None): label_data = run_dialog(PrintLabelEditor, None, self.store, self.model.sellable) if label_data: print_labels(label_data, self.store)
class _PersonEditorTemplate(BaseEditorSlave): model_type = Person gladefile = 'PersonEditorTemplate' proxy_widgets = ('name', 'phone_number', 'fax_number', 'mobile_number', 'email') def __init__(self, store, model, visual_mode, ui_form_name, parent): self._parent = parent if ui_form_name: self.db_form = DatabaseForm(store, ui_form_name) else: self.db_form = None super(_PersonEditorTemplate, self).__init__(store, model, visual_mode=visual_mode) self._check_new_person() def _check_new_person(self): self.is_new_person = False # If this person is not in the default store, then it was created # inside another transaction that was not commited yet. default_store = api.get_default_store() if default_store.find(Person, id=self.model.id).is_empty(): self.is_new_person = True # # BaseEditorSlave hooks # def create_model(self, store): return Person(name=u"", store=store) def setup_proxies(self): self._setup_widgets() self._setup_form_fields() self.proxy = self.add_proxy(self.model, _PersonEditorTemplate.proxy_widgets) def setup_slaves(self): self.address_slave = AddressSlave( self.store, self.model, self.model.get_main_address(), visual_mode=self.visual_mode, db_form=self.db_form) self.attach_slave('address_holder', self.address_slave) self.attach_model_slave('note_holder', NoteSlave, self.model) def on_confirm(self): main_address = self.address_slave.model main_address.person = self.model # # Public API # def add_extra_tab(self, tab_label, slave, position=None): """Adds an extra tab to the editor :param tab_label: the label that will be display on the tab :param slave: the slave that will be attached to the new tab :param position: the position the tab will be attached """ event_box = gtk.EventBox() self.person_notebook.append_page(event_box, gtk.Label(tab_label)) self.attach_slave(tab_label, slave, event_box) event_box.show() if position is not None: self.person_notebook.reorder_child(event_box, position) self.person_notebook.set_current_page(position) def attach_role_slave(self, slave): self.attach_slave('role_holder', slave) def attach_model_slave(self, name, slave_type, slave_model): slave = slave_type(self.store, slave_model, visual_mode=self.visual_mode) self.attach_slave(name, slave) return slave # # Kiwi handlers # def on_name__map(self, entry): self.name.grab_focus() def on_address_button__clicked(self, button): main_address = self.model.get_main_address() if not main_address.is_valid_model(): msg = _(u"You must define a valid main address before\n" "adding additional addresses") warning(msg) return result = run_dialog(AddressAdditionDialog, self._parent, self.store, person=self.model, reuse_store=self.is_new_person) if not result: return new_main_address = self.model.get_main_address() if new_main_address is not main_address: self.address_slave.set_model(new_main_address) def on_contact_info_button__clicked(self, button): run_dialog(ContactInfoListDialog, self._parent, self.store, person=self.model, reuse_store=self.is_new_person) def on_calls_button__clicked(self, button): run_dialog(CallsSearch, self._parent, self.store, person=self.model, reuse_store=self.is_new_person) def on_credit_check_history_button__clicked(self, button): run_dialog(CreditCheckHistorySearch, self._parent, self.store, client=self.model.client, reuse_store=self.is_new_person) # # Private API # def _setup_widgets(self): individual = self.model.individual company = self.model.company if not (individual or company): raise DatabaseInconsistency('A person must have at least a ' 'company or an individual set.') tab_child = self.person_data_tab if individual and company: tab_text = _('Individual/Company Data') self.company_frame.set_label(_('Company Data')) self.company_frame.show() self.individual_frame.set_label(_('Individual Data')) self.individual_frame.show() elif individual: tab_text = _('Individual Data') self.company_frame.hide() self.individual_frame.set_label('') self.individual_frame.show() else: tab_text = _('Company Data') self.individual_frame.hide() self.company_frame.set_label('') self.company_frame.show() self.person_notebook.set_tab_label_text(tab_child, tab_text) addresses = self.model.get_total_addresses() if addresses == 2: self.address_button.set_label(_("1 More Address...")) elif addresses > 2: self.address_button.set_label(_("%i More Addresses...") % (addresses - 1)) if not self.model.client: self.credit_check_history_button.hide() def _setup_form_fields(self): if not self.db_form: return self.db_form.update_widget(self.name, other=self.name_lbl) self.db_form.update_widget(self.phone_number, other=self.phone_number_lbl) self.db_form.update_widget(self.fax_number, u'fax', other=self.fax_lbl) self.db_form.update_widget(self.email, other=self.email_lbl) self.db_form.update_widget(self.mobile_number, other=self.mobile_lbl)
def __init__(self, store, model=None, visual_mode=False): from stoqlib.gui.slaves.sellableslave import CategoryPriceSlave is_new = not model self._sellable = None self._demo_mode = sysparam.get_bool('DEMO_MODE') self._requires_weighing_text = ( "<b>%s</b>" % api.escape(_("This unit type requires weighing"))) if self.ui_form_name: self.db_form = DatabaseForm(store, self.ui_form_name) else: self.db_form = None BaseEditor.__init__(self, store, model, visual_mode) self.enable_window_controls() if self._demo_mode: self._add_demo_warning() # code suggestion edit_code_product = sysparam.get_bool('EDIT_CODE_PRODUCT') self.code.set_sensitive(not edit_code_product and not self.visual_mode) if not self.code.read(): self._update_default_sellable_code() self.description.grab_focus() self.table.set_focus_chain([self.code, self.barcode, self.default_sale_cfop, self.description, self.cost_hbox, self.price_hbox, self.category_combo, self.tax_hbox, self.unit_combo, ]) print_labels_button = self.add_button('print_labels', gtk.STOCK_PRINT) print_labels_button.connect('clicked', self.on_print_labels_clicked, 'print_labels') label = print_labels_button.get_children()[0] label = label.get_children()[0].get_children()[1] label.set_label(_(u'Print labels')) self.setup_widgets() if not is_new and not self.visual_mode: # Although a sellable can be both removed/closed, we show only one, # to avoid having *lots* of buttons. If it's closed, provide a way # to reopen it, else, show a delete button if it can be removed # or a close button if it can be closed if self._sellable.is_closed(): self._add_reopen_button() elif self._sellable.can_remove(): self._add_delete_button() elif self._sellable.can_close(): self._add_close_button() self.set_main_tab_label(self.model_name) price_slave = CategoryPriceSlave(self.store, self.model.sellable, self.visual_mode) self.add_extra_tab(_(u'Category Prices'), price_slave) self._setup_ui_forms()
class SellableEditor(BaseEditor): """This is a base class for ProductEditor and ServiceEditor and should be used when editing sellable objects. Note that sellable objects are instances inherited by Sellable.""" # This must be be properly defined in the child classes model_name = None model_type = None gladefile = 'SellableEditor' confirm_widgets = ['description', 'cost', 'price'] ui_form_name = None sellable_tax_widgets = ['tax_constant', 'tax_value'] sellable_widgets = ['code', 'barcode', 'description', 'category_combo', 'cost', 'price', 'status_str', 'default_sale_cfop', 'unit_combo'] proxy_widgets = (sellable_tax_widgets + sellable_widgets) def __init__(self, store, model=None, visual_mode=False): from stoqlib.gui.slaves.sellableslave import CategoryPriceSlave is_new = not model self._sellable = None self._demo_mode = sysparam.get_bool('DEMO_MODE') self._requires_weighing_text = ( "<b>%s</b>" % api.escape(_("This unit type requires weighing"))) if self.ui_form_name: self.db_form = DatabaseForm(store, self.ui_form_name) else: self.db_form = None BaseEditor.__init__(self, store, model, visual_mode) self.enable_window_controls() if self._demo_mode: self._add_demo_warning() # code suggestion edit_code_product = sysparam.get_bool('EDIT_CODE_PRODUCT') self.code.set_sensitive(not edit_code_product and not self.visual_mode) if not self.code.read(): self._update_default_sellable_code() self.description.grab_focus() self.table.set_focus_chain([self.code, self.barcode, self.default_sale_cfop, self.description, self.cost_hbox, self.price_hbox, self.category_combo, self.tax_hbox, self.unit_combo, ]) print_labels_button = self.add_button('print_labels', gtk.STOCK_PRINT) print_labels_button.connect('clicked', self.on_print_labels_clicked, 'print_labels') label = print_labels_button.get_children()[0] label = label.get_children()[0].get_children()[1] label.set_label(_(u'Print labels')) self.setup_widgets() if not is_new and not self.visual_mode: # Although a sellable can be both removed/closed, we show only one, # to avoid having *lots* of buttons. If it's closed, provide a way # to reopen it, else, show a delete button if it can be removed # or a close button if it can be closed if self._sellable.is_closed(): self._add_reopen_button() elif self._sellable.can_remove(): self._add_delete_button() elif self._sellable.can_close(): self._add_close_button() self.set_main_tab_label(self.model_name) price_slave = CategoryPriceSlave(self.store, self.model.sellable, self.visual_mode) self.add_extra_tab(_(u'Category Prices'), price_slave) self._setup_ui_forms() def _add_demo_warning(self): fmt = _("This is a demostration mode of Stoq, you cannot " "create more than %d products.\n" "To avoid this limitation, enable production mode.") self.set_message(fmt % (_DEMO_PRODUCT_LIMIT)) if self.store.find(Sellable).count() > _DEMO_PRODUCT_LIMIT: self.disable_ok() def _add_extra_button(self, label, stock=None, callback_func=None, connect_on='clicked'): button = self.add_button(label, stock) if callback_func: button.connect(connect_on, callback_func, label) def _add_delete_button(self): self._add_extra_button(_('Remove'), gtk.STOCK_DELETE, self._on_delete_button__clicked) def _add_close_button(self): if self._sellable.product: label = _('Close Product') else: label = _('Close Service') self._add_extra_button(label, None, self._on_close_sellable_button__clicked) def _add_reopen_button(self): if self._sellable.product: label = _('Reopen Product') else: label = _('Reopen Service') self._add_extra_button(label, None, self._on_reopen_sellable_button__clicked) def _update_default_sellable_code(self): code = Sellable.get_max_value(self.store, Sellable.code) self.code.update(next_value_for(code)) def _setup_ui_forms(self): if not self.db_form: return self.db_form.update_widget(self.code, other=self.code_lbl) self.db_form.update_widget(self.barcode, other=self.barcode_lbl) self.db_form.update_widget(self.category_combo, other=self.category_lbl) # # Public API # def set_main_tab_label(self, tabname): self.sellable_notebook.set_tab_label(self.sellable_tab, gtk.Label(tabname)) def add_extra_tab(self, tabname, tabslave): self.sellable_notebook.set_show_tabs(True) self.sellable_notebook.set_show_border(True) event_box = gtk.EventBox() event_box.show() self.sellable_notebook.append_page(event_box, gtk.Label(tabname)) self.attach_slave(tabname, tabslave, event_box) def set_widget_formats(self): for widget in (self.cost, self.price): widget.set_adjustment(gtk.Adjustment(lower=0, upper=MAX_INT, step_incr=1)) self.requires_weighing_label.set_size("small") self.requires_weighing_label.set_text("") def edit_sale_price(self): sellable = self.model.sellable self.store.savepoint('before_run_editor_sellable_price') result = run_dialog(SellablePriceEditor, self, self.store, sellable) if result: self.sellable_proxy.update('price') else: self.store.rollback_to_savepoint('before_run_editor_sellable_price') def setup_widgets(self): raise NotImplementedError def update_requires_weighing_label(self): unit = self._sellable.unit if unit and unit.unit_index == UnitType.WEIGHT: self.requires_weighing_label.set_text(self._requires_weighing_text) else: self.requires_weighing_label.set_text("") def _update_tax_value(self): if not hasattr(self, 'tax_proxy'): return self.tax_proxy.update('tax_constant.tax_value') def get_taxes(self): """Subclasses may override this method to provide a custom tax selection. :returns: a list of tuples containing the tax description and a :class:`stoqlib.domain.sellable.SellableTaxConstant` object. """ return [] def _fill_categories(self): categories = self.store.find(SellableCategory) self.category_combo.prefill(api.for_combo(categories, attr='full_description')) # # BaseEditor hooks # def update_visual_mode(self): self.add_category.set_sensitive(False) self.sale_price_button.set_sensitive(False) def setup_sellable_combos(self): self._fill_categories() self.edit_category.set_sensitive(False) cfops = self.store.find(CfopData) self.default_sale_cfop.prefill(api.for_combo(cfops, empty='')) self.setup_unit_combo() def setup_unit_combo(self): units = self.store.find(SellableUnit) self.unit_combo.prefill(api.for_combo(units, empty=_('No units'))) def setup_tax_constants(self): taxes = self.get_taxes() self.tax_constant.prefill(taxes) def setup_proxies(self): self.set_widget_formats() self._sellable = self.model.sellable self.add_category.set_tooltip_text(_("Add a new category")) self.edit_category.set_tooltip_text(_("Edit the selected category")) self.setup_sellable_combos() self.setup_tax_constants() self.tax_proxy = self.add_proxy(self._sellable, SellableEditor.sellable_tax_widgets) self.sellable_proxy = self.add_proxy(self._sellable, SellableEditor.sellable_widgets) self.update_requires_weighing_label() def setup_slaves(self): from stoqlib.gui.slaves.sellableslave import SellableDetailsSlave details_slave = SellableDetailsSlave(self.store, self.model.sellable, visual_mode=self.visual_mode) self.attach_slave('slave_holder', details_slave) # Make everything aligned by pytting notes_lbl on the same size group self.left_labels_group.add_widget(details_slave.notes_lbl) def _run_category_editor(self, category=None): self.store.savepoint('before_run_editor_sellable_category') model = run_dialog(SellableCategoryEditor, self, self.store, category) if model: self._fill_categories() self.category_combo.select(model) else: self.store.rollback_to_savepoint('before_run_editor_sellable_category') # # Kiwi handlers # def _on_delete_button__clicked(self, button, parent_button_label=None): sellable_description = self._sellable.get_description() msg = (_("This will delete '%s' from the database. Are you sure?") % sellable_description) if not yesno(msg, gtk.RESPONSE_NO, _("Delete"), _("Keep")): return try: self._sellable.remove() except IntegrityError as details: warning(_("It was not possible to remove '%s'") % sellable_description, str(details)) return # We are doing this by hand instead of calling confirm/cancel because, # if we call self.cancel(), the transaction will not be committed. If # we call self.confirm(), it will, but some on_confirm hooks (like # ProductComponentSlave's one) will try to create other objects and # relate them with this product that doesn't exist anymore (we removed # them above), resulting in an IntegrityError. self.retval = self.model self.store.retval = self.retval self.main_dialog.close() def _on_close_sellable_button__clicked(self, button, parent_button_label=None): msg = (_("Do you really want to close '%s'?\n" "Please note that when it's closed, you won't be able to " "commercialize it anymore.") % self._sellable.get_description()) if not yesno(msg, gtk.RESPONSE_NO, parent_button_label, _("Don't close")): return self._sellable.close() self.confirm() def _on_reopen_sellable_button__clicked(self, button, parent_button_label=None): msg = (_("Do you really want to reopen '%s'?\n" "Note that when it's opened, you will be able to " "commercialize it again.") % self._sellable.get_description()) if not yesno(msg, gtk.RESPONSE_NO, parent_button_label, _("Keep closed")): return self._sellable.set_available() self.confirm() def on_category_combo__content_changed(self, category): self.edit_category.set_sensitive(bool(category.get_selected())) def on_tax_constant__changed(self, combo): self._update_tax_value() def on_unit_combo__changed(self, combo): self.update_requires_weighing_label() def on_sale_price_button__clicked(self, button): self.edit_sale_price() def on_add_category__clicked(self, widget): self._run_category_editor() def on_edit_category__clicked(self, widget): self._run_category_editor(self.category_combo.get_selected()) def on_code__validate(self, widget, value): if not value: return ValidationError(_(u'The code can not be empty.')) if self.model.sellable.check_code_exists(value): return ValidationError(_(u'The code %s already exists.') % value) def on_barcode__validate(self, widget, value): if not value: return if value and len(value) > 14: return ValidationError(_(u'Barcode must have 14 digits or less.')) if self.model.sellable.check_barcode_exists(value): return ValidationError(_('The barcode %s already exists') % value) if self._demo_mode and value not in _DEMO_BAR_CODES: return ValidationError(_("Cannot create new barcodes in " "demonstration mode")) def on_price__validate(self, entry, value): if value <= 0: return ValidationError(_("Price cannot be zero or negative")) def on_cost__validate(self, entry, value): if value <= 0: return ValidationError(_("Cost cannot be zero or negative")) def on_print_labels_clicked(self, button, parent_label_button=None): label_data = run_dialog(PrintLabelEditor, None, self.store, self.model.sellable) if label_data: print_labels(label_data, self.store)
class _PersonEditorTemplate(BaseEditorSlave): model_type = Person gladefile = 'PersonEditorTemplate' proxy_widgets = ('name', 'phone_number', 'fax_number', 'mobile_number', 'email') def __init__(self, store, model, visual_mode, ui_form_name, parent): self._parent = parent if ui_form_name: self.db_form = DatabaseForm(ui_form_name) else: self.db_form = None super(_PersonEditorTemplate, self).__init__(store, model, visual_mode=visual_mode) # # BaseEditorSlave hooks # def create_model(self, store): return Person(name=u"", store=store) def setup_proxies(self): self._setup_widgets() self._setup_form_fields() self.proxy = self.add_proxy(self.model, _PersonEditorTemplate.proxy_widgets) def setup_slaves(self): self.address_slave = AddressSlave(self.store, self.model, self.model.get_main_address(), visual_mode=self.visual_mode, db_form=self.db_form) self.attach_slave('address_holder', self.address_slave) self.attach_model_slave('note_holder', NoteSlave, self.model) def on_confirm(self): main_address = self.address_slave.model main_address.person = self.model # # Public API # def add_extra_tab(self, tab_label, slave, position=None): """Adds an extra tab to the editor :param tab_label: the label that will be display on the tab :param slave: the slave that will be attached to the new tab :param position: the position the tab will be attached """ event_box = gtk.EventBox() self.person_notebook.append_page(event_box, gtk.Label(tab_label)) self.attach_slave(tab_label, slave, event_box) event_box.show() if position is not None: self.person_notebook.reorder_child(event_box, position) self.person_notebook.set_current_page(position) def attach_role_slave(self, slave): self.attach_slave('role_holder', slave) def attach_model_slave(self, name, slave_type, slave_model): slave = slave_type(self.store, slave_model, visual_mode=self.visual_mode) self.attach_slave(name, slave) return slave # # Kiwi handlers # def on_name__map(self, entry): self.name.grab_focus() def on_address_button__clicked(self, button): main_address = self.model.get_main_address() if not main_address.is_valid_model(): msg = _(u"You must define a valid main address before\n" "adding additional addresses") warning(msg) return result = run_dialog(AddressAdditionDialog, self._parent, self.store, person=self.model, reuse_store=not self.visual_mode) if not result: return new_main_address = self.model.get_main_address() if new_main_address is not main_address: self.address_slave.set_model(new_main_address) def on_contact_info_button__clicked(self, button): run_dialog(ContactInfoListDialog, self._parent, self.store, person=self.model, reuse_store=not self.visual_mode) def on_calls_button__clicked(self, button): run_dialog(CallsSearch, self._parent, self.store, person=self.model, reuse_store=not self.visual_mode) def on_credit_check_history_button__clicked(self, button): run_dialog(CreditCheckHistorySearch, self._parent, self.store, client=self.model.client, reuse_store=not self.visual_mode) # # Private API # def _setup_widgets(self): individual = self.model.individual company = self.model.company if not (individual or company): raise DatabaseInconsistency('A person must have at least a ' 'company or an individual set.') tab_child = self.person_data_tab if individual and company: tab_text = _('Individual/Company Data') self.company_frame.set_label(_('Company Data')) self.company_frame.show() self.individual_frame.set_label(_('Individual Data')) self.individual_frame.show() elif individual: tab_text = _('Individual Data') self.company_frame.hide() label_widget = self.individual_frame.get_label_widget() if label_widget is not None: label_widget.hide() self.individual_frame.show() else: tab_text = _('Company Data') self.individual_frame.hide() label_widget = self.company_frame.get_label_widget() if label_widget is not None: label_widget.hide() self.company_frame.show() self.person_notebook.set_tab_label_text(tab_child, tab_text) addresses = self.model.get_total_addresses() if addresses == 2: self.address_button.set_label(_("1 More Address...")) elif addresses > 2: self.address_button.set_label( _("%i More Addresses...") % (addresses - 1)) if not self.model.client: self.credit_check_history_button.hide() def _setup_form_fields(self): if not self.db_form: return self.db_form.update_widget(self.name, other=self.name_lbl) self.db_form.update_widget(self.phone_number, other=self.phone_number_lbl) self.db_form.update_widget(self.fax_number, u'fax', other=self.fax_lbl) self.db_form.update_widget(self.email, other=self.email_lbl) self.db_form.update_widget(self.mobile_number, other=self.mobile_lbl)
def __init__(self, store, model, visual_mode=None, ui_form_name=None): self.db_form = DatabaseForm(ui_form_name) if ui_form_name else None BaseEditorSlave.__init__(self, store, model, visual_mode=visual_mode)