def return_sale(parent, sale, store): from stoqlib.gui.wizards.salereturnwizard import SaleReturnWizard cancel_last_coupon = sysparam.get_bool('ALLOW_CANCEL_LAST_COUPON') # A plugin (e.g. ECF) can avoid the cancelation of a sale # because it wants it to be cancelled using another way if cancel_last_coupon and SaleAvoidCancelEvent.emit(sale): return if sale.can_return(): need_document = not sysparam.get_bool('ACCEPT_SALE_RETURN_WITHOUT_DOCUMENT') if need_document and not sale.get_client_document(): warning(_('It is not possible to accept a returned sale from clients ' 'without document. Please edit the client document or change ' 'the sale client')) return returned_sale = sale.create_sale_return_adapter() retval = run_dialog(SaleReturnWizard, parent, store, returned_sale) elif sale.can_cancel(): retval = cancel_sale(sale) else: retval = False return retval
def return_sale(parent, sale, store): from stoqlib.gui.wizards.salereturnwizard import SaleReturnWizard cancel_last_coupon = sysparam.get_bool('ALLOW_CANCEL_LAST_COUPON') if cancel_last_coupon and ECFIsLastSaleEvent.emit(sale): info(_("That is last sale in ECF. Return using the menu " "ECF - Cancel Last Document")) return if sale.can_return(): need_document = not sysparam.get_bool('ACCEPT_SALE_RETURN_WITHOUT_DOCUMENT') if need_document and not sale.get_client_document(): warning(_('It is not possible to accept a returned sale from clients ' 'without document. Please edit the client document or change ' 'the sale client')) return returned_sale = sale.create_sale_return_adapter() retval = run_dialog(SaleReturnWizard, parent, store, returned_sale) elif sale.can_cancel(): retval = cancel_sale(sale) else: retval = False return retval
def _validate_percentage(self, value, type_text): if value >= 100: self.model.discount_percentage = 0 return ValidationError(_(u'%s can not be greater or equal ' 'to 100%%.') % type_text) if value < 0: self.model.discount_percentage = 0 return ValidationError(_("%s can not be less than 0") % type_text) if (not sysparam.get_bool('USE_TRADE_AS_DISCOUNT') and value > self.max_discount): self.model.discount_percentage = 0 return ValidationError(_("%s can not be greater than %d%%") % (type_text, self.max_discount)) discount = self.max_discount / 100 * self.original_sale_amount perc = discount * 100 / self.model.get_sale_subtotal() new_discount = quantize(self.original_discount + perc) #XXX: If the discount is less than the trade value. What to do? if (sysparam.get_bool('USE_TRADE_AS_DISCOUNT') and value > new_discount): self.model.discount_percentage = 0 return ValidationError(_(u'%s can not be greater than (%.2f%%).') % (type_text, new_discount)) old_discount = self.model.discount_value # Avoid unecessary updates if the discount didnt change if self.model.discount_value != old_discount: self.update_sale_discount()
def update_discount_and_surcharge(self): marker("update_discount_and_surcharge") # Here we need avoid to reset sale data defined when creating the # Sale in the POS application, i.e, we should not reset the # discount and surcharge if they are already set (this is the # case when one of the parameters, CONFIRM_SALES_ON_TILL or # USE_TRADE_AS_DISCOUNT is enabled). if (not sysparam.get_bool('CONFIRM_SALES_ON_TILL') and not sysparam.get_bool('USE_TRADE_AS_DISCOUNT')): self.model.discount_value = currency(0) self.model.surcharge_value = currency(0)
def setup_widgets(self): marker('Setting up widgets') # Only quotes have expire date. self.expire_date.hide() self.expire_label.hide() # Hide client category widgets self.client_category_lbl.hide() self.client_category.hide() # if the NF-e plugin is active, the client is mandantory in this # wizard (in this situation, we have only quote sales). if self.model.status == Sale.STATUS_QUOTE: manager = get_plugin_manager() mandatory_client = manager.is_active('nfe') self.client.set_property('mandatory', mandatory_client) marker('Filling sales persons') salespersons = self.store.find(SalesPerson) self.salesperson.prefill(api.for_person_combo(salespersons)) marker('Finished filling sales persons') marker('Read parameter') if not sysparam.get_bool('ACCEPT_CHANGE_SALESPERSON'): self.salesperson.set_sensitive(False) else: self.salesperson.grab_focus() marker('Finished reading parameter') self._fill_clients_combo() self._fill_transporter_combo() self._fill_cost_center_combo() if sysparam.get_bool('ASK_SALES_CFOP'): self._fill_cfop_combo() else: self.cfop_lbl.hide() self.cfop.hide() self.create_cfop.hide() # the maximum number allowed for an invoice is 999999999. self.invoice_number.set_adjustment( gtk.Adjustment(lower=1, upper=999999999, step_incr=1)) if not self.model.invoice_number: new_invoice_number = Sale.get_last_invoice_number(self.store) + 1 self.invoice_model.invoice_number = new_invoice_number else: new_invoice_number = self.model.invoice_number self.invoice_model.invoice_number = new_invoice_number self.invoice_number.set_sensitive(False) self.invoice_model.original_invoice = new_invoice_number marker('Finished setting up widgets')
def on_open_date__validate(self, widget, date): if sysparam.get_bool('ALLOW_OUTDATED_OPERATIONS'): return if date < localtoday().date(): return ValidationError( _("Open date must be set to today or " "a future date"))
def version(self, store, app_version): """Fetches the latest version :param store: a store :param app_version: application version :returns: a deferred with the version_string as a parameter """ try: bdist_type = library.bdist_type except Exception: bdist_type = None if os.path.exists(os.path.join('etc', 'init.d', 'stoq-bootstrap')): source = 'livecd' elif bdist_type in ['egg', 'wheel']: source = 'pypi' elif is_developer_mode(): source = 'devel' else: source = 'ppa' params = { 'hash': sysparam.get_string('USER_HASH'), 'demo': sysparam.get_bool('DEMO_MODE'), 'dist': platform.dist(), 'cnpj': get_main_cnpj(store), 'plugins': InstalledPlugin.get_plugin_names(store), 'product_key': get_product_key(), 'time': datetime.datetime.today().isoformat(), 'uname': platform.uname(), 'version': app_version, 'source': source, } params.update(self._get_company_details(store)) params.update(self._get_usage_stats(store)) return self._do_request('GET', 'version.json', **params)
def checkout(self, cancel_clear=False): """Initiates the sale wizard to confirm sale. :param cancel_clear: If cancel_clear is true, the sale will be cancelled if the checkout is cancelled. """ assert len(self.sale_items) >= 1 if self._current_store: store = self._current_store savepoint = 'before_run_fiscalprinter_confirm' store.savepoint(savepoint) else: store = api.new_store() savepoint = None if self._trade: if self._get_subtotal() < self._trade.returned_total: info(_("Traded value is greater than the new sale's value. " "Please add more items or return it in Sales app, " "then make a new sale")) return sale = self._create_sale(store) self._trade.new_sale = sale self._trade.trade() else: sale = self._create_sale(store) if sysparam.get_bool('CONFIRM_SALES_ON_TILL'): sale.order() store.commit() else: assert self._coupon ordered = self._coupon.confirm(sale, store, savepoint, subtotal=self._get_subtotal()) # Dont call store.confirm() here, since coupon.confirm() # above already did it if not ordered: # FIXME: Move to TEF plugin manager = get_plugin_manager() if manager.is_active('tef') or cancel_clear: self._cancel_order(show_confirmation=False) elif not self._current_store: # Just do that if a store was created above and # if _cancel_order wasn't called (it closes the connection) store.rollback(close=True) return log.info("Checking out") self._coupon = None POSConfirmSaleEvent.emit(sale, self.sale_items[:]) # We must close the connection only after the event is emmited, since it # may use value from the sale that will become invalid after it is # closed store.close() self._clear_order()
def on_cost__validate(self, widget, value): sellable = self.proxy.model.sellable if not sellable: return # Dont allow numbers bigger than MAX_INT (see stoqlib.lib.defaults) if value > MAX_INT: return ValidationError(_("Price cannot be bigger than %s") % MAX_INT) if value <= 0: return ValidationError(_(u"Cost must be greater than zero.")) if self.validate_price: category = getattr(self.model, "client_category", None) default_price = sellable.get_price_for_category(category) if not sysparam.get_bool("ALLOW_HIGHER_SALE_PRICE") and value > default_price: return ValidationError(_(u"The sell price cannot be greater " "than %s.") % default_price) manager = self.manager or api.get_current_user(self.store) client = getattr(self.model, "client", None) category = client and client.category extra_discount = self.get_extra_discount(sellable) valid_data = sellable.is_valid_price(value, category, manager, extra_discount=extra_discount) if not valid_data["is_valid"]: return ValidationError((_(u"Max discount for this product is %.2f%%.") % valid_data["max_discount"]))
def setup_slaves(self): marker('Setting up slaves') BaseMethodSelectionStep.setup_slaves(self) marker('Finished parent') self.pm_slave.set_client(self.model.client, total_amount=self.wizard.get_total_to_pay()) marker('Setting discount') self.discount_slave = SaleDiscountSlave(self.store, self.model, self.model_type) if sysparam.get_bool('USE_TRADE_AS_DISCOUNT'): self.subtotal_expander.set_expanded(True) self.discount_slave.discount_value_ck.set_active(True) self.discount_slave.update_sale_discount() marker('Finshed setting up discount') self.discount_slave.connect('discount-changed', self.on_discount_slave_changed) slave_holder = 'discount_surcharge_slave' if self.get_slave(slave_holder): self.detach_slave(slave_holder) self.attach_slave(slave_holder, self.discount_slave) marker('Finished setting up slaves')
def _add_ecf_menu(self, uimanager): ui_string = """<ui> <menubar name="menubar"> <placeholder name="ExtraMenubarPH"> <menu action="ECFMenu"> <menuitem action="CancelLastDocument"/> <menuitem action="Summary"/> <menuitem action="ReadMemory"/> </menu> </placeholder> </menubar> </ui>""" group = get_accels('plugin.ecf') ag = gtk.ActionGroup('ECFMenuActions') ag.add_actions([ ('ECFMenu', None, _('ECF')), ('ReadMemory', None, _('Read Memory'), group.get('read_memory'), None, self._on_ReadMemory__activate), ('CancelLastDocument', None, _('Cancel Last Document'), None, None, self._on_CancelLastDocument__activate), ]) ag.add_action_with_accel(self._till_summarize_action, group.get('summarize')) can_cancel = sysparam.get_bool('ALLOW_CANCEL_LAST_COUPON') cancel_coupon_menu = ag.get_action('CancelLastDocument') cancel_coupon_menu.set_visible(can_cancel) uimanager.insert_action_group(ag, 0) self._ui = uimanager.add_ui_from_string(ui_string)
def _update_till_status(self, closed, blocked): # Three different situations; # # - Till is closed # - Till is opened # - Till was not closed the previous fiscal day (blocked) self.set_sensitive([self.TillOpen], closed) self.set_sensitive([self.TillClose, self.PaymentReceive], not closed or blocked) widgets = [self.TillVerify, self.TillAddCash, self.TillRemoveCash, self.SearchTillHistory, self.app_vbox] self.set_sensitive(widgets, not closed and not blocked) if closed: text = _(u"Till closed") self.clear() self.setup_focus() elif blocked: text = _(u"Till blocked from previous day") else: till = Till.get_current(self.store) text = _(u"Till opened on %s") % till.opening_date.strftime('%x') self.till_status_label.set_text(text) self._update_toolbar_buttons() self._update_total() if sysparam.get_bool('SHOW_TOTAL_PAYMENTS_ON_TILL'): self._update_payment_total()
def _register_branch_station(caller_store, station_name): import gtk from stoqlib.lib.parameters import sysparam if not sysparam.get_bool('DEMO_MODE'): fmt = _(u"The computer '%s' is not registered to the Stoq " u"server at %s.\n\n" u"Do you want to register it " u"(requires administrator access) ?") if not yesno(fmt % (station_name, db_settings.address), gtk.RESPONSE_YES, _(u"Register computer"), _(u"Quit")): raise SystemExit from stoqlib.gui.utils.login import LoginHelper h = LoginHelper(username="******") try: user = h.validate_user() except LoginError as e: error(str(e)) if not user: error(_("Must login as 'admin'")) from stoqlib.domain.station import BranchStation with new_store() as store: branch = sysparam.get_object(store, 'MAIN_COMPANY') station = BranchStation.create(store, branch=branch, name=station_name) return caller_store.fetch(station)
def _payComissionWhenConfirmed(self): sysparam.set_bool( self.store, "SALE_PAY_COMMISSION_WHEN_CONFIRMED", True) self.failUnless( sysparam.get_bool('SALE_PAY_COMMISSION_WHEN_CONFIRMED'))
def _check_param_online_services(self): from stoqlib.database.runtime import new_store from stoqlib.lib.parameters import sysparam from gi.repository import Gtk if sysparam.get_bool('ONLINE_SERVICES') is None: from kiwi.ui.dialogs import HIGAlertDialog # FIXME: All of this is to avoid having to set markup as the default # in kiwi/ui/dialogs:HIGAlertDialog.set_details, after 1.0 # this can be simplified when we fix so that all descriptions # sent to these dialogs are properly escaped dialog = HIGAlertDialog( parent=None, flags=Gtk.DialogFlags.MODAL, type=Gtk.MessageType.WARNING) dialog.add_button(_("Not right now"), Gtk.ResponseType.NO) dialog.add_button(_("Enable online services"), Gtk.ResponseType.YES) dialog.set_primary(_('Do you want to enable Stoq online services?')) dialog.set_details(PRIVACY_STRING, use_markup=True) dialog.set_default_response(Gtk.ResponseType.YES) response = dialog.run() dialog.destroy() store = new_store() sysparam.set_bool(store, 'ONLINE_SERVICES', response == Gtk.ResponseType.YES) store.commit() store.close()
def finish(self): missing = get_missing_items(self.model, self.store) if missing: # We want to close the checkout, so the user will be back to the # list of items in the sale. self.close() run_dialog(MissingItemsDialog, self, self.model, missing) return False group = self.model.group # FIXME: This is set too late on Sale.confirm(). If PaymentGroup don't # have a payer, we won't be able to print bills/booklets. group.payer = self.model.client and self.model.client.person invoice_ok = InvoiceSetupEvent.emit() if invoice_ok is False: # If there is any problem with the invoice, the event will display an error # message and the dialog is kept open so the user can fix whatever is wrong. # If this is the second time the user is trying to confirm, an # error message is being displayed saying that the payment can't be # created twice, so we clear the payments created to avoid the message # TODO: Create the payments on the wizard finish event (here) self.payment_group.clear_unused() return self.retval = True self.close() retval = ConfirmSaleWizardFinishEvent.emit(self.model) if retval is not None: self.retval = retval if sysparam.get_bool('PRINT_SALE_DETAILS_ON_POS'): self.print_sale_details()
def on_close_date__validate(self, widget, date): if sysparam.get_bool('ALLOW_OUTDATED_OPERATIONS'): return if date > localtoday().date() or date < self.model.open_date: return ValidationError(_("Paid date must be between " "%s and today") % (self.model.open_date, ))
def sysparam(self, **kwargs): """ Updates a set of system parameters within a context. The values will be reverted when leaving the scope. kwargs contains a dictionary of parameter name->value """ from stoqlib.lib.parameters import sysparam old_values = {} for param, value in kwargs.items(): if type(value) is bool: old_values[param] = sysparam.get_bool(param) sysparam.set_bool(self.store, param, value) elif isinstance(value, Domain) or value is None: old_values[param] = sysparam.get_object(self.store, param) sysparam.set_object(self.store, param, value) else: raise NotImplementedError(type(value)) try: yield finally: for param, value in old_values.items(): if type(value) is bool: sysparam.set_bool(self.store, param, value) elif isinstance(value, Domain) or value is None: sysparam.set_object(self.store, param, value) else: raise NotImplementedError(type(value))
def _identify_customer(self, coupon, sale=None): if not sysparam.get_bool('ENABLE_DOCUMENT_ON_INVOICE'): return model = None initial_client_document = None if sale: model = self._get_client_document(sale) # Sale may have no client. if model: initial_client_document = model.document model = run_dialog(PaulistaInvoiceDialog, self._current_app, self.default_store, model) # The user has chosen the 'without cpf' option, but we still need to # inform a invalid CPF, otherwise the current client's cpf will be used if not model: coupon.identify_customer('-', '-', u'', None) return # The document didn't change. if model.document == initial_client_document: return coupon.identify_customer('-', '-', model.document, model.document_type) return model.document
def trade(self): """Do a trade for this return Almost the same as :meth:`.return_`, but unlike it, this won't generate reversed payments to the client. Instead, it'll generate an inpayment using :obj:`.returned_total` value, so it can be used as an "already paid quantity" on :obj:`.new_sale`. """ assert self.new_sale if self.sale: assert self.sale.can_return() self._clean_not_used_items() store = self.store group = self.group method = PaymentMethod.get_by_name(store, u'trade') description = _(u'Traded items for sale %s') % ( self.new_sale.identifier, ) value = self.returned_total self._return_items() value_as_discount = sysparam.get_bool('USE_TRADE_AS_DISCOUNT') if value_as_discount: self.new_sale.discount_value = self.returned_total else: payment = method.create_payment(Payment.TYPE_IN, group, self.branch, value, description=description) payment.set_pending() payment.pay() self._revert_fiscal_entry() if self.sale: self.sale.return_(self)
def _setup_widgets(self): # Salesperson combo salespersons = SalesPerson.get_active_salespersons(self.store) self.salesperson.prefill(salespersons) change_salesperson = sysparam.get_int('ACCEPT_CHANGE_SALESPERSON') if change_salesperson == ChangeSalespersonPolicy.ALLOW: self.salesperson.grab_focus() elif change_salesperson == ChangeSalespersonPolicy.DISALLOW: self.salesperson.set_sensitive(False) elif change_salesperson == ChangeSalespersonPolicy.FORCE_CHOOSE: self.model.salesperson = None self.salesperson.grab_focus() else: raise AssertionError # CFOP combo if sysparam.get_bool('ASK_SALES_CFOP'): cfops = CfopData.get_for_sale(self.store) self.cfop.prefill(api.for_combo(cfops)) else: self.cfop_lbl.hide() self.cfop.hide() self.create_cfop.hide() self._fill_clients_category_combo() self._setup_clients_widget() self._client_credit_set_visible(bool(self.client.read()))
def on_price__validate(self, widget, value): if value <= 0: return ValidationError(_(u"The price must be greater than zero.")) if (not sysparam.get_bool('ALLOW_HIGHER_SALE_PRICE') and value > self.model.base_price): return ValidationError(_(u'The sell price cannot be greater ' 'than %s.') % self.model.base_price) sellable = self.model.sellable manager = self.manager or api.get_current_user(self.store) if api.sysparam.get_bool('REUTILIZE_DISCOUNT'): extra_discount = self.model.sale.get_available_discount_for_items( user=manager, exclude_item=self.model) else: extra_discount = None valid_data = sellable.is_valid_price( value, category=self.model.sale.client_category, user=manager, extra_discount=extra_discount) if not valid_data['is_valid']: return ValidationError( (_(u'Max discount for this product is %.2f%%.') % valid_data['max_discount']))
def on_cost__validate(self, widget, value): sellable = self.proxy.model.sellable if not sellable: return if value <= 0: return ValidationError(_(u'Cost must be greater than zero.')) if self.validate_price: category = getattr(self.model, 'client_category', None) default_price = sellable.get_price_for_category(category) if (not sysparam.get_bool('ALLOW_HIGHER_SALE_PRICE') and value > default_price): return ValidationError(_(u'The sell price cannot be greater ' 'than %s.') % default_price) manager = self.manager or api.get_current_user(self.store) client = getattr(self.model, 'client', None) category = client and client.category extra_discount = self.get_extra_discount(sellable) valid_data = sellable.is_valid_price(value, category, manager, extra_discount=extra_discount) if not valid_data['is_valid']: return ValidationError( (_(u'Max discount for this product is %.2f%%.') % valid_data['max_discount']))
def _add_pos_menus(self, uimanager): if sysparam.get_bool('POS_SEPARATE_CASHIER'): return ui_string = """<ui> <menubar name="menubar"> <placeholder name="ExtraMenubarPH"> <menu action="ECFMenu"> <menuitem action="CancelLastDocument"/> <menuitem action="Summary"/> <menuitem action="ReadMemory"/> </menu> </placeholder> </menubar> </ui>""" group = get_accels('plugin.ecf') ag = gtk.ActionGroup('ECFMenuActions') ag.add_actions([ ('ECFMenu', None, _('ECF')), ('ReadMemory', None, _('Read Memory'), group.get('read_memory'), None, self._on_ReadMemory__activate), ('CancelLastDocument', None, _('Cancel Last Document'), None, None, self._on_CancelLastDocument__activate), ]) ag.add_action_with_accel(self._till_summarize_action, group.get('summarize')) uimanager.insert_action_group(ag, 0) self._ui = uimanager.add_ui_from_string(ui_string)
def get_columns(self): columns = [ IdentifierColumn('identifier', title=_('Sale #'), sorted=True), SearchColumn('salesperson_name', title=_('Salesperson'), data_type=str, expand=True), SearchColumn('method_description', title=_('Method'), data_type=str, search_attribute='method_name', valid_values=self._get_method_values()), # This column evals to an integer, and due to a bug # in kiwi, its not searchable Column('commission_percentage', title=_('Commission (%)'), data_type=Decimal, format="%.2f"), # negative commissions are shown in red color ColoredColumn('commission_value', title=_('Commission'), color='red', data_func=lambda x: x < 0, data_type=currency)] # FIXME: The date here depends on the parameter. We could use # it as a property on the view, but then it would not be searchable. # Find a better way of handling this sometime in the future. if sysparam.get_bool('SALE_PAY_COMMISSION_WHEN_CONFIRMED'): columns.append(SearchColumn('confirm_date', title=_('Date'), data_type=datetime.date)) else: columns.append(SearchColumn('paid_date', title=_('Date'), data_type=datetime.date)) columns.extend([ Column('payment_amount', title=_('Payment value'), data_type=currency), Column('total_amount', title=_('Sale total'), data_type=currency)]) return columns
def _get_parameter_value(self, obj): """Given a ParameterData object, returns a string representation of its current value. """ detail = sysparam.get_detail_by_name(obj.field_name) if detail.type == unicode: data = sysparam.get_string(obj.field_name) elif detail.type == bool: data = sysparam.get_bool(obj.field_name) elif detail.type == int: data = sysparam.get_int(obj.field_name) elif detail.type == decimal.Decimal: data = sysparam.get_decimal(obj.field_name) elif isinstance(detail.type, basestring): data = sysparam.get_object(self.store, obj.field_name) else: raise NotImplementedError(detail.type) if isinstance(data, Domain): if not (IDescribable in providedBy(data)): raise TypeError(u"Parameter `%s' must implement IDescribable " "interface." % obj.field_name) return data.get_description() elif detail.options: return detail.options[int(obj.field_value)] elif isinstance(data, bool): return [_(u"No"), _(u"Yes")][data] elif obj.field_name == u'COUNTRY_SUGGESTED': return dgettext("iso_3166", data) elif isinstance(data, unicode): # FIXME: workaround to handle locale specific data return _(data) return unicode(data)
def _on_SaleAvoidCancel(self, sale): if not sysparam.get_bool('ALLOW_CANCEL_LAST_COUPON'): return False if self._is_ecf_last_sale(sale): info(_("That is last sale in ECF. Return using the menu " "ECF - Cancel Last Document")) return True return False
def on_object_changed(self, attr, old_value, value): if attr == 'cost': self.cost_last_updated = localnow() if (self.product and sysparam.get_bool('UPDATE_PRODUCT_COST_ON_COMPONENT_UPDATE')): self.product.update_production_cost(value) elif attr == 'base_price': self.price_last_updated = localnow()
def setup_proxies(self): marker('Setting up proxies') self.setup_widgets() self.proxy = self.add_proxy(self.model, self.proxy_widgets) if self.model.client: self.client_gadget.set_editable(False) if sysparam.get_bool('ASK_SALES_CFOP'): self.add_proxy(self.model, SalesPersonStep.cfop_widgets) marker('Finished setting up proxies')
def _setup_widgets(self): self.confirm_date.set_sensitive(False) self._fill_employee_combo() self._fill_branch_combo() self._fill_cfop_combo() self._fill_cost_center_combo() if not sysparam.get_bool('CREATE_PAYMENTS_ON_STOCK_DECREASE'): self.create_payments.hide()
def _needs_cat52(self, printer): # If the param is not enabled, we dont need. if not sysparam.get_bool('ENABLE_DOCUMENT_ON_INVOICE'): return False # Even if the parameter is enabled, we can only generate cat52 for # the printer we support, and that dont have MFD: # If the printer has an MFD, it should not be present in the # MODEL_CODES variable model = MODEL_CODES.get((printer.brand, printer.model)) if not model: return False return True
def _setup_widgets(self): self.confirm_date.set_sensitive(False) self._fill_employee_combo() self._fill_branch_combo() self._fill_cfop_combo() self._fill_cost_center_combo() self._fill_person_combo() manager = get_plugin_manager() nfe_is_active = manager.is_active('nfe') self.person.set_property('mandatory', nfe_is_active) if not sysparam.get_bool('CREATE_PAYMENTS_ON_STOCK_DECREASE'): self.create_payments.hide()
def setup_proxies(self): self._setup_widgets() self.sale_proxy = self.add_proxy(self.model, StartSaleQuoteStep.sale_widgets) self.invoice_proxy = self.add_proxy(self.model.invoice, StartSaleQuoteStep.invoice_widgets) if sysparam.get_bool('ASK_SALES_CFOP'): self.add_proxy(self.model, StartSaleQuoteStep.cfop_widgets) expire_delta = sysparam.get_int('EXPIRATION_SALE_QUOTE_DATE') if expire_delta > 0 and not self.model.expire_date: # Only set the expire date if id doesn't already have one. self.expire_date.update(localtoday() + relativedelta(days=expire_delta))
def _update_till_status(self, closed, blocked): # Three different situations: # # - Till is closed # - Till is opened # - Till was not closed the previous fiscal day (blocked) self.set_sensitive([self.TillOpen], closed) self.set_sensitive([self.TillClose], not closed or blocked) widgets = [ self.TillVerify, self.TillAddCash, self.TillRemoveCash, self.SearchTillHistory, self.search_holder, self.PaymentReceive ] self.set_sensitive(widgets, not closed and not blocked) def large(s): return '<span weight="bold" size="xx-large">%s</span>' % ( api.escape(s), ) if closed: text = large(_(u"Till closed")) self.search_holder.hide() self.footer_hbox.hide() self.large_status.show() self.clear() self.setup_focus() # Adding the label on footer without the link self.small_status.set_text(text) if not blocked: text += '\n\n<span size="large"><a href="open-till">%s</a></span>' % ( api.escape(_('Open till'))) self.status_link.set_markup(text) elif blocked: self.search_holder.hide() self.footer_hbox.hide() text = large(_(u"Till blocked")) self.status_link.set_markup(text) self.small_status.set_text(text) else: self.search_holder.show() self.footer_hbox.show() self.large_status.hide() till = Till.get_current(self.store) text = _(u"Till opened on %s") % till.opening_date.strftime('%x') self.small_status.set_text(text) self._update_toolbar_buttons() self._update_total() if sysparam.get_bool('SHOW_TOTAL_PAYMENTS_ON_TILL'): self._update_payment_total()
def finish(self): missing = get_missing_items(self.model, self.store) if missing: # We want to close the checkout, so the user will be back to the # list of items in the sale. self.close() run_dialog(MissingItemsDialog, self, self.model, missing) return False self.retval = True invoice_number = self.invoice_model.invoice_number # Workaround for bug 4218: If the invoice is was already used by # another store (another cashier), try using the next one # available, or show a warning if the number was manually set. while True: try: self.store.savepoint('before_set_invoice_number') self.model.invoice_number = invoice_number # We need to flush the database here, or a possible collision # of invoice_number will only be detected later on, when the # execution flow is not in the try-except anymore. self.store.flush() except IntegrityError: self.store.rollback_to_savepoint('before_set_invoice_number') if self._invoice_changed(): warning(_(u"The invoice number %s is already used. " "Confirm the sale again to chose another one.") % invoice_number) self.retval = False break else: invoice_number += 1 else: break self.close() group = self.model.group # FIXME: This is set too late on Sale.confirm(). If PaymentGroup don't # have a payer, we won't be able to print bills/booklets. group.payer = self.model.client and self.model.client.person retval = ConfirmSaleWizardFinishEvent.emit(self.model) if retval is not None: self.retval = retval if sysparam.get_bool('PRINT_SALE_DETAILS_ON_POS'): self.print_sale_details()
def __init__(self, store, model, model_type, visual_mode=False): self._proxy = None self.model = model self.model_type = model_type self.manager = None self.default_max_discount = sysparam.get_decimal('MAX_SALE_DISCOUNT') self.max_discount = self.default_max_discount # If there is an original discount, this means the sale was from a trade, # and the parameter USE_TRADE_AS_DISCOUNT must be set. self.original_discount = self.model.discount_value * 100 / self.model.get_sale_subtotal( ) if self.original_discount: assert sysparam.get_bool('USE_TRADE_AS_DISCOUNT') self.original_sale_amount = self.model.get_total_sale_amount() BaseEditorSlave.__init__(self, store, model, visual_mode=visual_mode)
def _validate_percentage(self, value, type_text): if value >= 100: self.model.discount_percentage = 0 return ValidationError(_(u'%s can not be greater or equal ' 'to 100%%.') % type_text) if value < 0: self.model.discount_percentage = 0 return ValidationError(_("%s can not be less than 0") % type_text) if (not sysparam.get_bool('USE_TRADE_AS_DISCOUNT') and value > self.max_discount): self.model.discount_percentage = 0 return ValidationError(_("%s can not be greater than %d%%") % (type_text, self.max_discount)) discount = self.max_discount / 100 * self.original_sale_amount perc = discount * 100 / self.model.get_sale_subtotal() new_discount = quantize(self.original_discount + perc) #XXX: If the discount is less than the trade value. What to do? if (sysparam.get_bool('USE_TRADE_AS_DISCOUNT') and value > new_discount): self.model.discount_percentage = 0 return ValidationError(_(u'%s can not be greater than (%.2f%%).') % (type_text, new_discount))
def get_columns(self): columns = [ IdentifierColumn('identifier', title=_('Sale #'), sorted=True), SearchColumn('salesperson_name', title=_('Salesperson'), data_type=str, expand=True), SearchColumn('method_description', title=_('Method'), data_type=str, search_attribute='method_name', valid_values=self._get_method_values()), # This column evals to an integer, and due to a bug # in kiwi, its not searchable Column('commission_percentage', title=_('Commission (%)'), data_type=Decimal, format="%.2f"), # negative commissions are shown in red color ColoredColumn('commission_value', title=_('Commission'), color='red', data_func=lambda x: x < 0, data_type=currency) ] # FIXME: The date here depends on the parameter. We could use # it as a property on the view, but then it would not be searchable. # Find a better way of handling this sometime in the future. if sysparam.get_bool('SALE_PAY_COMMISSION_WHEN_CONFIRMED'): columns.append( SearchColumn('confirm_date', title=_('Date'), data_type=datetime.date)) else: columns.append( SearchColumn('paid_date', title=_('Date'), data_type=datetime.date)) columns.extend([ Column('payment_amount', title=_('Payment value'), data_type=currency), Column('total_amount', title=_('Sale total'), data_type=currency) ]) return columns
def create_filters(self): self.set_text_field_columns(['salesperson_name', 'identifier_str']) self._salesperson_filter = self.create_salesperson_filter(_("Sold by:")) self.add_filter(self._salesperson_filter, SearchFilterPosition.TOP, callback=self._get_salesperson_query) # Adding a filter by date with custom interval self._date_filter = DateSearchFilter(_("Date:")) self._date_filter.select(data=DateSearchFilter.Type.USER_INTERVAL) if sysparam.get_bool('SALE_PAY_COMMISSION_WHEN_CONFIRMED'): self.add_filter(self._date_filter, SearchFilterPosition.BOTTOM, columns=[self.search_spec.confirm_date]) else: self.add_filter(self._date_filter, SearchFilterPosition.BOTTOM, columns=[self.search_spec.paid_date])
def get_columns(self): if sysparam.get_bool('SHOW_FULL_DATETIME_ON_RECEIVABLE'): get_date = datetime.datetime else: get_date = datetime.date return [IdentifierColumn('identifier', title=_('Payment #')), SearchColumn('description', title=_('Description'), data_type=str, ellipsize=Pango.EllipsizeMode.END, expand=True, pack_end=True), SearchColumn('sale_open_date', title=_('Sale date'), data_type=get_date, width=100), Column('color', title=_('Description'), width=20, data_type=GdkPixbuf.Pixbuf, format_func=render_pixbuf, column='description'), SearchColumn('method_description', title=_('Payment Method'), data_type=str, search_attribute='method_id', valid_values=self._get_payment_methods(), width=100, visible=False), SearchColumn('card_type', title=_('Card Type'), format_func=self._format_card_type, data_type=str, valid_values=self._get_card_types(), width=100, visible=False), Column('comments_number', title=_(u'Comments'), visible=False), SearchColumn('drawee', title=_('Drawee'), data_type=str, ellipsize=Pango.EllipsizeMode.END, width=140), SearchColumn('drawee_fancy_name', title=_('Drawee fancy name'), visible=False, data_type=str, ellipsize=Pango.EllipsizeMode.END, width=140), SearchColumn('open_date', title=_('Open date'), data_type=datetime.date, width=100, visible=False), SearchColumn('due_date', title=_('Due date'), data_type=datetime.date, width=100, sorted=True), SearchColumn('paid_date', title=_('Paid date'), data_type=datetime.date, width=100), SearchColumn('status_str', title=_('Status'), width=100, data_type=str, search_attribute='status', valid_values=self._get_status_values(), visible=False), SearchColumn('value', title=_('Value'), data_type=currency, width=90), SearchColumn('paid_value', title=_('Paid'), long_title=_('Paid value'), data_type=currency, width=90), SearchColumn('category', title=_('Category'), data_type=str, long_title=_('Payment category'), width=110, visible=False)]
def setup_widgets(self): marker('Setting up widgets') # Only quotes have expire date. self.expire_date.hide() self.expire_label.hide() # Hide client category widgets self.client_category_lbl.hide() self.client_category.hide() # if the NF-e plugin is active, the client is mandantory in this # wizard (in this situation, we have only quote sales). if self.model.status == Sale.STATUS_QUOTE: manager = get_plugin_manager() mandatory_client = manager.is_active('nfe') self.client.set_property('mandatory', mandatory_client) marker('Filling sales persons') salespersons = SalesPerson.get_active_salespersons( self.store, api.get_current_branch(self.store)) self.salesperson.prefill(salespersons) marker('Finished filling sales persons') marker('Read parameter') change_salesperson = sysparam.get_int('ACCEPT_CHANGE_SALESPERSON') if change_salesperson == ChangeSalespersonPolicy.ALLOW: self.salesperson.grab_focus() elif change_salesperson == ChangeSalespersonPolicy.DISALLOW: self.salesperson.set_sensitive(False) elif change_salesperson == ChangeSalespersonPolicy.FORCE_CHOOSE: self.model.salesperson = None self.salesperson.grab_focus() else: raise AssertionError marker('Finished reading parameter') self._setup_clients_widget() self._fill_transporter_combo() self._fill_cost_center_combo() if sysparam.get_bool('ASK_SALES_CFOP'): self._fill_cfop_combo() else: self.cfop_lbl.hide() self.cfop.hide() self.create_cfop.hide() marker('Finished setting up widgets')
def version(self, store, app_version): """Fetches the latest version :param store: a store :param app_version: application version :returns: a deferred with the version_string as a parameter """ params = { 'demo': sysparam.get_bool('DEMO_MODE'), 'dist': platform.dist(), 'cnpj': get_main_cnpj(store), 'plugins': InstalledPlugin.get_plugin_names(store), 'product_key': get_product_key(), 'time': datetime.datetime.today().isoformat(), 'uname': platform.uname(), 'version': app_version, } return self._do_request('GET', 'version.json', **params)
def _populate_printers(self): supported_ifaces = get_supported_printers_by_iface(ICouponPrinter).items() printers = [] for brand, printer_classes in supported_ifaces: for printer_class in printer_classes: printer = _PrinterModel(brand, printer_class) printers.append((printer.get_description(), printer)) # Allow to use virtual printer for both demo mode and developer mode # so it's easier for testers and developers to test ecf functionality if sysparam.get_bool('DEMO_MODE') or is_developer_mode(): from stoqdrivers.printers.virtual.Simple import Simple printer = _PrinterModel('virtual', Simple) printers.append((printer.get_description(), printer)) self.printer.prefill(locale_sorted( printers, key=operator.itemgetter(0)))
def _setup_widgets(self): # SearchSale is here because it's possible to return a sale inside it self._inventory_widgets = [self.Confirm, self.SearchSale, self.Return] self.register_sensitive_group(self._inventory_widgets, lambda: not self.has_open_inventory()) self.total_label.set_size('xx-large') self.total_label.set_bold(True) if not sysparam.get_bool('SHOW_TOTAL_PAYMENTS_ON_TILL'): self.total_payment_label.hide() else: self.total_payment_label.set_size('large') self.total_payment_label.set_bold(True) self.total_label.set_size('large') self.small_status.set_size('xx-large') self.small_status.set_bold(True)
def __init__(self, store, model, subtotal, total_paid=0, current_document=None): """Creates a new SaleWizard that confirms a sale. To avoid excessive querying of the database we pass some data already queried/calculated before hand. :param store: a store :param model: a |sale| :param subtotal: subtotal of the sale :param total_paid: totaly value already paid :param current_document: the current document of the identified client, if any """ marker('ConfirmSaleWizard') self._check_payment_group(model, store) self._subtotal = subtotal self._total_paid = total_paid self._current_document = current_document self.model = model # invoice_model is a Settable so avoid bug 4218, where more # than one checkout may try to use the same invoice number. self.invoice_model = Settable(invoice_number=None, original_invoice=None) adjusted_batches = model.check_and_adjust_batches() if not adjusted_batches: first_step = ConfirmSaleBatchStep(store, self, model, None) else: marker('running SalesPersonStep') first_step = SalesPersonStep(self, store, model, self.payment_group, self.invoice_model) marker('finished creating SalesPersonStep') BaseWizard.__init__(self, store, first_step, model) if not sysparam.get_bool('CONFIRM_SALES_ON_TILL'): # This was added to allow us to work even if an error # happened while adding a payment, where we already order # but cannot confirm and are thrown back to the main # POS interface if self.model.can_order(): self.model.order() marker('leaving ConfirmSaleWizard.__init__')
def feedback(self, screen, email, feedback): default_store = get_default_store() params = { 'hash': sysparam.get_string('USER_HASH'), 'cnpj': get_main_cnpj(default_store), 'demo': sysparam.get_bool('DEMO_MODE'), 'dist': ' '.join(platform.dist()), 'email': email, 'feedback': feedback, 'plugins': ', '.join(InstalledPlugin.get_plugin_names(default_store)), 'product_key': get_product_key(), 'screen': screen, 'time': datetime.datetime.today().isoformat(), 'uname': ' '.join(platform.uname()), 'version': self._get_version(), } return self._do_request('GET', 'feedback.json', **params)
def _add_ecf_menu(self, app): group = get_accels('plugin.ecf') actions = [ ('ReadMemory', None, _('Read Memory'), group.get('read_memory'), None, self._on_ReadMemory__activate), ('CancelLastDocument', None, _('Cancel Last Document'), None, None, self._on_CancelLastDocument__activate), ('Summary', None, _('Summary'), group.get('read_memory'), None, self._on_TillSummary__activate), ] app.add_ui_actions(actions) items = [ app.Summary, app.ReadMemory, ] if sysparam.get_bool('ALLOW_CANCEL_LAST_COUPON'): items.insert(0, app.CancelLastDocument) app.window.add_extra_items(items, _('ECF'))
def feedback(self, screen, email, feedback, **kwargs): default_store = get_default_store() params = { 'cnpj': get_main_cnpj(default_store), 'demo': sysparam.get_bool('DEMO_MODE'), 'dist': ' '.join(platform.dist()), 'email': email, 'feedback': feedback, 'plugins': ', '.join(InstalledPlugin.get_plugin_names(default_store)), 'product_key': get_product_key(), 'screen': screen, 'time': datetime.datetime.today().isoformat(), 'uname': ' '.join(platform.uname()), 'version': self._get_version(), } endpoint = 'api/stoq/v1/feedback/%s' % (sysparam.get_string('USER_HASH'), ) return self._do_request('POST', endpoint, json=params, **kwargs)
def finish(self): missing = get_missing_items(self.model, self.store) if missing: # We want to close the checkout, so the user will be back to the # list of items in the sale. self.close() run_dialog(MissingItemsDialog, self, self.model, missing) return False for item in self.model.get_items(): sellable = item.sellable if not sellable.is_available(self.model.branch): self.close() warning( _("%s is not available for sale. Try making it " "available first or change it on sale and then try again." ) % (sellable.get_description())) return False group = self.model.group # FIXME: This is set too late on Sale.confirm(). If PaymentGroup don't # have a payer, we won't be able to print bills/booklets. group.payer = self.model.client and self.model.client.person invoice_ok = InvoiceSetupEvent.emit() if invoice_ok is False: # If there is any problem with the invoice, the event will display an error # message and the dialog is kept open so the user can fix whatever is wrong. # If this is the second time the user is trying to confirm, an # error message is being displayed saying that the payment can't be # created twice, so we clear the payments created to avoid the message # TODO: Create the payments on the wizard finish event (here) self.payment_group.clear_unused() return self.retval = True self.close() retval = ConfirmSaleWizardFinishEvent.emit(self.model) if retval is not None: self.retval = retval if sysparam.get_bool('PRINT_SALE_DETAILS_ON_POS'): self.print_sale_details()
def trade(self): """Do a trade for this return Almost the same as :meth:`.return_`, but unlike it, this won't generate reversed payments to the client. Instead, it'll generate an inpayment using :obj:`.returned_total` value, so it can be used as an "already paid quantity" on :obj:`.new_sale`. """ assert self.new_sale if self.sale: assert self.sale.can_return() self._clean_not_used_items() store = self.store group = self.group method = PaymentMethod.get_by_name(store, u'trade') description = _(u'Traded items for sale %s') % ( self.new_sale.identifier, ) value = self.returned_total value_as_discount = sysparam.get_bool('USE_TRADE_AS_DISCOUNT') if value_as_discount: self.new_sale.discount_value = self.returned_total else: payment = method.create_payment(Payment.TYPE_IN, group, self.branch, value, description=description) payment.set_pending() payment.pay() self._revert_fiscal_entry() login_user = api.get_current_user(self.store) if self.sale: self.sale.return_(self) if self.sale.branch == self.branch: self.confirm(login_user) else: # When trade items without a registered sale, confirm the # new returned sale. self.confirm(login_user)
def __init__(self, store, model=None, previous_day=False, close_db=True, close_ecf=True): """ Create a new TillClosingEditor object. :param previous_day: If the till wasn't closed previously """ self._previous_day = previous_day self.till = Till.get_last(store, api.get_current_station(store)) if close_db: assert self.till self._close_db = close_db self._close_ecf = close_ecf self._blind_close = sysparam.get_bool('TILL_BLIND_CLOSING') BaseEditor.__init__(self, store, model) self._setup_widgets()
def get_columns(self): columns = [ Column('sellable.code', title=_('Code'), data_type=str, visible=False), Column('sellable.barcode', title=_('Barcode'), data_type=str, visible=False), Column('sellable.description', title=_('Description'), data_type=str, expand=True, searchable=True, format_func=self._format_description, format_func_data=True), Column('manufacturer', title=_('Manufacturer'), data_type=str, visible=False), Column('model', title=_('Model'), data_type=str, visible=False), Column('sellable.category_description', title=_('Category'), data_type=str, expand=True, searchable=True), Column('quantity', title=_('Quantity'), data_type=Decimal, format_func=format_quantity), Column('sellable.unit_description', title=_('Unit'), data_type=str)] if sysparam.get_bool('SHOW_COST_COLUMN_IN_SALES'): columns.append(Column('sellable.cost', title=_('Cost'), data_type=currency, width=80)) manager = get_plugin_manager() show_invoice_columns = (manager.is_any_active(['nfe', 'nfce']) and not manager.is_active('ecf')) columns.extend([ Column('cfop_code', title=_('CFOP'), data_type=str, visible=show_invoice_columns), Column('icms_info.v_bc', title=_('ICMS BC'), data_type=currency, visible=show_invoice_columns), Column('icms_info.v_icms', title=_('ICMS'), data_type=currency, visible=show_invoice_columns), Column('ipi_info.v_ipi', title=_('IPI'), data_type=currency, visible=show_invoice_columns), Column('base_price', title=_('Original Price'), data_type=currency), Column('price', title=_('Sale Price'), data_type=currency), Column('sale_discount', title=_('Discount'), data_type=Decimal, format_func=get_formatted_percentage), Column('total', title=_('Total'), data_type=currency)]) return columns
def sysparam(self, **kwargs): """ Updates a set of system parameters within a context. The values will be reverted when leaving the scope. kwargs contains a dictionary of parameter name->value """ from stoqlib.lib.parameters import sysparam old_values = {} for param, value in kwargs.items(): if isinstance(value, bool): old_values[param] = sysparam.get_bool(param) sysparam.set_bool(self.store, param, value) elif isinstance(value, int): old_values[param] = sysparam.get_int(param) sysparam.set_int(self.store, param, value) elif isinstance(value, Domain) or value is None: old_values[param] = sysparam.get_object(self.store, param) sysparam.set_object(self.store, param, value) elif isinstance(value, str): old_values[param] = sysparam.get_string(param) sysparam.set_string(self.store, param, value) elif isinstance(value, Decimal): old_values[param] = sysparam.get_decimal(param) sysparam.set_decimal(self.store, param, value) else: raise NotImplementedError(type(value)) try: yield finally: for param, value in old_values.items(): if isinstance(value, bool): sysparam.set_bool(self.store, param, value) elif isinstance(value, int): sysparam.set_int(self.store, param, value) elif isinstance(value, Domain) or value is None: sysparam.set_object(self.store, param, value) elif isinstance(value, str): sysparam.set_string(self.store, param, value) elif isinstance(value, Decimal): sysparam.set_decimal(self.store, param, value) else: raise NotImplementedError(type(value))
def version(self, store, app_version, **kwargs): """Fetches the latest version :param store: a store :param app_version: application version """ import stoq try: bdist_type = library.bdist_type except Exception: bdist_type = None # We should use absolute paths when looking for /etc if os.path.exists( os.path.join(os.sep, 'etc', 'init.d', 'stoq-bootstrap')): source = 'livecd' elif stoq.trial_mode: source = 'trial' elif bdist_type in ['egg', 'wheel']: source = 'pypi' elif is_developer_mode(): source = 'devel' else: source = 'ppa' params = { 'demo': sysparam.get_bool('DEMO_MODE'), 'dist': ' '.join(platform.dist()), 'cnpj': get_main_cnpj(store), 'plugins': ' '.join(InstalledPlugin.get_plugin_names(store)), 'product_key': get_product_key(), 'uname': ' '.join(platform.uname()), 'version': app_version, 'source': source, } params.update(self._get_company_details(store)) params.update(self._get_usage_stats(store)) endpoint = 'api/stoq/v1/version/%s' % ( sysparam.get_string('USER_HASH'), ) return self._do_request('POST', endpoint, json=params, **kwargs)
def feedback(self, screen, email, feedback): app_info = get_utility(IAppInfo, None) if app_info: app_version = app_info.get('version') else: app_version = 'Unknown' default_store = get_default_store() params = { 'cnpj': get_main_cnpj(default_store), 'demo': sysparam.get_bool('DEMO_MODE'), 'dist': ' '.join(platform.dist()), 'email': email, 'feedback': feedback, 'plugins': ', '.join(InstalledPlugin.get_plugin_names(default_store)), 'product_key': get_product_key(), 'screen': screen, 'time': datetime.datetime.today().isoformat(), 'uname': ' '.join(platform.uname()), 'version': app_version, } return self._do_request('GET', 'feedback.json', **params)
def __init__(self, filename, workorders): self.workorders = workorders self.workorder_items = [] # The workorders are always from the same sale. self.sale = workorders[0].sale if self.sale: self.subtitle = _("Sale number: %s") % self.sale.identifier else: assert len(workorders) == 1 self.subtitle = _("Work order: %s") % self.workorders[0].identifier self.method_summary = {} if self.sale: payments = self.sale.payments for payment in payments.find(Payment.group == self.sale.group): self.method_summary.setdefault(payment.method, 0) self.method_summary[payment.method] += payment.value for order in workorders: self.workorder_items.extend(order.get_items()) self.use_wo_description = sysparam.get_bool('CUSTOM_WORK_ORDER_DESCRIPTION') super(OpticalWorkOrderReceiptReport, self).__init__(filename)
def create_coupon(self): """ Creates a new fiscal coupon :returns: a new coupon """ if sysparam.get_bool('DEMO_MODE'): branch = api.get_current_branch(self.store) company = branch.person.company if company and company.cnpj not in [ '24.198.774/7322-35', '66.873.574/0001-82' ]: # FIXME: Find a better description for the warning bellow. warning( _("You are not allowed to sell in branches not " "created by the demonstration mode")) coupon = FiscalCoupon(self._parent) try: CouponCreatedEvent.emit(coupon) except (DriverError, DeviceError): warning(_("It wasn't possible to open the coupon")) coupon = None return coupon
def _allow_unknown_sales(self): return sysparam.get_bool('ALLOW_TRADE_NOT_REGISTERED_SALES')
def on_expected_receival_date__validate(self, widget, date): if sysparam.get_bool('ALLOW_OUTDATED_OPERATIONS'): return if date < localtoday().date(): return ValidationError(_("Expected receival date must be set to a future date"))
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()