Exemple #1
0
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
Exemple #2
0
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
Exemple #3
0
    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()
Exemple #4
0
 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)
Exemple #5
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')
Exemple #6
0
 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"))
Exemple #7
0
    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)
Exemple #8
0
    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()
Exemple #9
0
    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"]))
Exemple #10
0
    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')
Exemple #11
0
    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)
Exemple #12
0
    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()
Exemple #13
0
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)
Exemple #14
0
 def _payComissionWhenConfirmed(self):
     sysparam.set_bool(
         self.store,
         "SALE_PAY_COMMISSION_WHEN_CONFIRMED",
         True)
     self.failUnless(
         sysparam.get_bool('SALE_PAY_COMMISSION_WHEN_CONFIRMED'))
Exemple #15
0
    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()
Exemple #16
0
    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()
Exemple #17
0
    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, ))
Exemple #18
0
 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))
Exemple #19
0
    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
Exemple #20
0
    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)
Exemple #21
0
    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()))
Exemple #22
0
    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']))
Exemple #23
0
    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']))
Exemple #24
0
    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
Exemple #26
0
    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)
Exemple #27
0
 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
Exemple #28
0
 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()
Exemple #29
0
 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')
Exemple #30
0
    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()
Exemple #31
0
    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
Exemple #32
0
    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()
Exemple #33
0
    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))
Exemple #34
0
    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()
Exemple #35
0
    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()
Exemple #36
0
 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)
Exemple #37
0
    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))
Exemple #38
0
    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
Exemple #39
0
    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])
Exemple #40
0
 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)]
Exemple #41
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 = 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')
Exemple #42
0
 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)
Exemple #43
0
    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)))
Exemple #44
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)
Exemple #45
0
    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__')
Exemple #46
0
 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)
Exemple #47
0
 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'))
Exemple #48
0
    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)
Exemple #49
0
    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()
Exemple #50
0
    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)
Exemple #51
0
 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()
Exemple #52
0
    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
Exemple #53
0
 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))
Exemple #54
0
    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)
Exemple #55
0
 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)
Exemple #56
0
    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)
Exemple #57
0
    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
Exemple #58
0
 def _allow_unknown_sales(self):
     return sysparam.get_bool('ALLOW_TRADE_NOT_REGISTERED_SALES')
Exemple #59
0
    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"))
Exemple #60
0
    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()