Exemple #1
0
 def _get_columns(self):
     return [Column('description', title=_(u'Product'),
                    data_type=str, expand=True),
             Column('ordered', title=_(u'Ordered'),
                    data_type=int),
             Column('stock', title=_(u'Stock'),
                    data_type=int)]
Exemple #2
0
    def run_wizard(cls, parent):
        """Run the wizard to create a product

        This will run the wizard and after finishing, ask if the user
        wants to create another product alike. The product will be
        cloned and `stoqlib.gui.editors.producteditor.ProductEditor`
        will run as long as the user chooses to create one alike
        """
        with api.new_store() as store:
            rv = run_dialog(cls, parent, store)

        if rv:
            inner_rv = rv

            while yesno(_("Would you like to register another product alike?"),
                        gtk.RESPONSE_NO, _("Yes"), _("No")):
                with api.new_store() as store:
                    template = store.fetch(rv)
                    inner_rv = run_dialog(ProductEditor, parent, store,
                                          product_type=template.product_type,
                                          template=template)

                if not inner_rv:
                    break

        # We are insterested in the first rv that means that at least one
        # obj was created.
        return rv
Exemple #3
0
    def _check_param_online_services(self):
        from stoqlib.database.runtime import get_default_store, new_store
        from stoqlib.lib.parameters import sysparam
        import gtk

        sparam = sysparam(get_default_store())
        if sparam.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.DIALOG_MODAL,
                type=gtk.MESSAGE_WARNING)
            dialog.add_button(_("Not right now"), gtk.RESPONSE_NO)
            dialog.add_button(_("Enable online services"), gtk.RESPONSE_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.RESPONSE_YES)
            response = dialog.run()
            dialog.destroy()
            store = new_store()
            sysparam(store).ONLINE_SERVICES = int(bool(response == gtk.RESPONSE_YES))
            store.commit()
            store.close()
Exemple #4
0
def validate_invoice_number(invoice_number, store):
    if not 0 < invoice_number <= 999999999:
        return ValidationError(
            _("Invoice number must be between 1 and 999999999"))

    if not store.find(Invoice, invoice_number=invoice_number).is_empty():
        return ValidationError(_(u'Invoice number already used.'))
Exemple #5
0
    def _write_account_cells(self, sheet, cells):
        for y, cell in enumerate(cells):
            sheet.write(2 + y, 0, cell, HEADER_LEFT_STYLE)

        n_rows = len(cells)
        sheet.write(n_rows + 2, 0, _(u"Average"), HEADER_LEFT_STYLE)
        sheet.write(n_rows + 3, 0, _(u"Total"), HEADER_LEFT_STYLE)
Exemple #6
0
    def _write_headers(self, sheet, n_columns):
        for x in range(n_columns):
            month_name = get_month_names()[x]
            sheet.write(1, 1 + x, month_name, HEADER_TOP_STYLE)

        sheet.write(1, n_columns + 1, _(u"Average"), HEADER_TOP_STYLE)
        sheet.write(1, n_columns + 2, _(u"Total"), HEADER_TOP_STYLE)
Exemple #7
0
 def on_return_quantity__validate(self, widget, value):
     if value < self._original_return_qty:
         return ValidationError(_(u'Can not decrease this quantity.'))
     total = value + self.model.sale_quantity
     if total > self.model.quantity:
         return ValidationError(_(u'Sale and return quantity is greater '
                                  'than the total quantity.'))
Exemple #8
0
    def on_SalesCancel__activate(self, action):
        sale_view = self.results.get_selected()
        can_cancel = api.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 can_cancel and SaleAvoidCancelEvent.emit(sale_view.sale):
            return

        store = api.new_store()
        sale = store.fetch(sale_view.sale)
        msg_text = _(u"This will cancel the sale, Are you sure?")
        model = SaleComment(store=store, sale=sale,
                            author=api.get_current_user(store))

        retval = self.run_dialog(
            NoteEditor, store, model=model, attr_name='comment',
            message_text=msg_text, label_text=_(u"Reason"),
            mandatory=True, ok_button_label=_(u"Cancel sale"),
            cancel_button_label=_(u"Don't cancel"))

        if not retval:
            store.rollback()
            return

        sale.cancel()
        store.commit(close=True)
        self.refresh()
Exemple #9
0
    def _install_postgres(self):
        self.wizard.disable_back()
        self.wizard.disable_next()
        self.label.set_markup(
            _("Please wait while the package installation is completing."))

        packageinstaller = library.get_resource_filename(
            'stoq', 'scripts', 'packageinstaller.py')
        p = subprocess.Popen(
            [sys.executable, packageinstaller,
             'postgresql', 'postgresql-contrib', 'stoq-server'],
            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        stdout, stderr = p.communicate()

        self.wizard.enable_back()
        if p.returncode == 0:
            self.done = True
            self.wizard.enable_next()
            self.label.set_markup(
                _("Postgresql installation succeeded. You may now proceed "
                  "to the next step by clicking the <b>Forward</b> button"))
        elif p.returncode == 11:
            self.wizard.enable_next()
            self.label.set_markup(
                _("Authorization failed, try again or connect to "
                  "another database"))
        else:
            warning(_("Something went wrong while trying to install "
                      "the PostgreSQL server."))
            self.label.set_markup(
                _("Sorry, something went wrong while installing PostgreSQL, "
                  "try again manually or go back and configure Stoq to connect "
                  "to another."))
Exemple #10
0
    def _maybe_create_database(self):
        logger.info('_maybe_create_database (db_is_local=%s, enable_production=%s)'
                    % (self.wizard.db_is_local, self.wizard.enable_production))
        if self.wizard.db_is_local:
            self._launch_stoqdbadmin()
            return
        elif self.wizard.enable_production:
            self._launch_stoqdbadmin()
            return

        self.wizard.write_pgpass()
        settings = self.wizard.settings
        self.wizard.config.load_settings(settings)

        store = settings.create_super_store()
        version = get_database_version(store)

        if version < (9, 1):
            store.close()
            error(_("Stoq requires PostgresSQL 9.1 or later, but %s found") % (
                ".".join(map(str, version))))

        try:
            check_extensions(store=store)
        except ValueError:
            store.close()
            error(_("Missing PostgreSQL extension on the server, "
                    "please install postgresql-contrib on it"))

        store.close()
        self.process_view.feed("** Creating database\r\n")
        self._launch_stoqdbadmin()
Exemple #11
0
    def on_SalesCancel__activate(self, action):
        sale_view = self.results.get_selected()
        can_cancel = api.sysparam.get_bool('ALLOW_CANCEL_LAST_COUPON')
        if can_cancel and ECFIsLastSaleEvent.emit(sale_view.sale):
            info(_("That is last sale in ECF. Return using the menu "
                   "ECF - Cancel Last Document"))
            return

        store = api.new_store()
        sale = store.fetch(sale_view.sale)
        msg_text = _(u"This will cancel the sale, Are you sure?")
        model = SaleComment(store=store, sale=sale,
                            author=api.get_current_user(store))

        retval = self.run_dialog(
            NoteEditor, store, model=model, attr_name='comment',
            message_text=msg_text, label_text=_(u"Reason"),
            mandatory=True, ok_button_label=_(u"Cancel sale"),
            cancel_button_label=_(u"Don't cancel"))

        if not retval:
            store.rollback()
            return

        sale.cancel()
        store.commit(close=True)
        self.refresh()
 def _get_lonely_payments_columns(self):
     return [IdentifierColumn('identifier', title=_('Payment #'), sorted=True),
             Column('method', title=_('Method'), data_type=str),
             Column('description', title=_('Description'), expand=True,
                    data_type=str),
             Column('branch', title=_('Branch'), data_type=str, visible=False),
             Column('value', title=_('Payment Value'), data_type=currency)]
 def _get_sales_columns(self):
     return [IdentifierColumn('identifier', title=_('Sale #'), sorted=True),
             Column('salesperson', title=_('Sales Person'), data_type=str),
             Column('client', title=_('Client'), data_type=str, expand=True),
             Column('branch', title=_('Branch'), data_type=str, visible=False),
             Column('value', title=_('Value'), data_type=str,
                    justify=Gtk.Justification.RIGHT)]
Exemple #14
0
    def _get_new_value(self):
        operation = self._entry.get_text().strip()
        operation = operation.replace(',', '.')

        if operation.endswith('%'):
            op_value = operation[:-1]
            percentage = True
        else:
            op_value = operation
            percentage = False

        if not operation:
            return

        if operation[0] in ['+', '-']:
            raise ValueError(_("Operator signals are not supported..."))

        if self._mode == self.MODE_SUB:
            op = operator.sub
        elif self._mode == self.MODE_ADD:
            op = operator.add

        try:
            op_value = decimal.Decimal(op_value)
        except decimal.InvalidOperation:
            raise ValueError(
                _("'%s' is not a valid operation...") % (operation,))

        if percentage:
            value = op(self._new_value, self._new_value * (op_value / 100))
        else:
            value = op(self._new_value, op_value)

        return value
Exemple #15
0
    def _maybe_create_database(self):
        logger.info('_maybe_create_database (db_is_local=%s, remove_demo=%s)'
                    % (self.wizard.db_is_local, self.wizard.remove_demo))
        if self.wizard.db_is_local:
            self._launch_stoqdbadmin()
            return
        elif self.wizard.remove_demo:
            self._launch_stoqdbadmin()
            return

        self.wizard.write_pgpass()
        settings = self.wizard.settings
        self.wizard.config.load_settings(settings)

        # store = settings.get_super_store()
        # version = store.dbVersion()
        # if version < (8, 1):
        #     info(_("Stoq requires PostgresSQL 8.1 or later, but %s found") %
        #          ".".join(map(str, version)))
        #     store.close()
        #     return False

        # Secondly, ask the user if he really wants to create the database,
        dbname = settings.dbname
        if yesno(_(u"The specified database '%s' does not exist.\n"
                   u"Do you want to create it?") % dbname,
                 gtk.RESPONSE_YES, _(u"Create database"), _(u"Don't create")):
            self.process_view.feed("** Creating database\r\n")
            self._launch_stoqdbadmin()
        else:
            self.process_view.feed("** Not creating database\r\n")
            self.wizard.disable_next()
Exemple #16
0
    def _check_branch(self):
        from stoqlib.database.runtime import (get_default_store, new_store,
                                              get_current_station,
                                              set_current_branch_station)
        from stoqlib.domain.person import Company
        from stoqlib.lib.parameters import sysparam
        from stoqlib.lib.message import info

        default_store = get_default_store()

        compaines = default_store.find(Company)
        if (compaines.count() == 0 or
                not sysparam.has_object('MAIN_COMPANY')):
            from stoqlib.gui.base.dialogs import run_dialog
            from stoqlib.gui.dialogs.branchdialog import BranchDialog
            if self._ran_wizard:
                info(_("You need to register a company before start using Stoq"))
            else:
                info(_("Could not find a company. You'll need to register one "
                       "before start using Stoq"))
            store = new_store()
            person = run_dialog(BranchDialog, None, store)
            if not person:
                raise SystemExit
            branch = person.branch
            sysparam.set_object(store, 'MAIN_COMPANY', branch)
            current_station = get_current_station(store)
            if current_station is not None:
                current_station.branch = branch
            store.commit()
            store.close()

        set_current_branch_station(default_store, station_name=None)
Exemple #17
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 #18
0
 def print_quote_details(self, model, payments_created=False):
     msg = _('Would you like to print the quote details now?')
     # We can only print the details if the quote was confirmed.
     if yesno(msg, gtk.RESPONSE_YES,
              _("Print quote details"), _("Don't print")):
         orders = WorkOrder.find_by_sale(self.model.store, self.model)
         print_report(OpticalWorkOrderReceiptReport, list(orders))
    def _remove_work_order(self, holder, name, work_order, work_order_id):
        if work_order.is_finished():
            warning(_("You cannot remove workorder with the status '%s'")
                    % work_order.status_str)
            return
        if not work_order.get_items().find().is_empty():
            warning(_("This workorder already has items and cannot be removed"))
            return

        # We cannot remove the WO from the database (since it already has some
        # history), but we can disassociate it from the sale, cancel and leave
        # a reason for it.
        reason = (_(u'Removed from sale %s') % work_order.sale.identifier)
        work_order.sale = None
        work_order.cancel(reason=reason)

        self._work_order_ids.remove(work_order_id)

        # Remove the tab
        self.detach_slave(name)
        pagenum = self.work_orders_nb.page_num(holder)
        self.work_orders_nb.remove_page(pagenum)

        # And remove the WO
        self.wizard.workorders.remove(work_order)

        self.force_validation()
Exemple #20
0
 def _update_filter_items(self):
     options = [
         FilterItem(_('Received payments'), 'status:paid'),
         FilterItem(_('To receive'), 'status:not-paid'),
         FilterItem(_('Late payments'), 'status:late'),
     ]
     self.add_filter_items(PaymentCategory.TYPE_RECEIVABLE, options)
Exemple #21
0
 def get_subtitle(self):
     """Returns a subtitle text
     """
     if self.end_date:
         return _('Till movement on %s to %s') % (self.start_date,
                                                  self.end_date)
     return _('Till movement on %s') % self.start_date
Exemple #22
0
 def get_descriptions(self):
     app_desc = []
     for name, (label, description) in sorted(_APPLICATIONS.items()):
         icon = get_application_icon(name)
         app_desc.append((name, _(label),
                          icon, _(description)))
     return app_desc
Exemple #23
0
    def create_ui(self):
        if api.sysparam(self.store).SMART_LIST_LOADING:
            self.search.enable_lazy_search()

        self.popup = self.uimanager.get_widget('/StockSelection')
        self.window.add_new_items([self.NewReceiving, self.NewTransfer,
                                   self.NewStockDecrease, self.LoanNew])
        self.window.add_search_items([
            self.SearchStockItems,
            self.SearchStockDecrease,
            self.SearchClosedStockItems,
            self.SearchProductHistory,
            self.SearchPurchasedStockItems,
            self.SearchTransfer,
        ])
        self.window.Print.set_tooltip(
            _("Print a report of these products"))
        self._inventory_widgets = [self.NewTransfer, self.NewReceiving,
                                   self.StockInitial, self.NewStockDecrease,
                                   self.LoanNew, self.LoanClose]
        self.register_sensitive_group(self._inventory_widgets,
                                      lambda: not self.has_open_inventory())

        self.image_viewer = None

        self.image = gtk.Image()
        self.edit_button = self.uimanager.get_widget('/toolbar/AppToolbarPH/EditProduct')
        self.edit_button.set_icon_widget(self.image)
        self.image.show()

        self.search.set_summary_label(column='stock',
                                      label=_('<b>Stock Total:</b>'),
                                      format='<b>%s</b>',
                                      parent=self.get_statusbar_message_area())
Exemple #24
0
    def _till_status_changed(self, closed, blocked):
        def large(s):
            return '<span weight="bold" size="xx-large">%s</span>' % (
                api.escape(s), )

        if closed:
            text = large(_("Till closed"))
            if not blocked:
                text += '\n\n<span size="large"><a href="open-till">%s</a></span>' % (
                    api.escape(_('Open till')))
        elif blocked:
            text = large(_("Till blocked"))
        else:
            text = large(_("Till open"))
        self.till_status_label.set_use_markup(True)
        self.till_status_label.set_justify(gtk.JUSTIFY_CENTER)
        self.till_status_label.set_markup(text)

        self.set_sensitive([self.TillOpen], closed)
        self.set_sensitive([self.TillVerify], not closed and not blocked)
        self.set_sensitive([
            self.TillClose, self.NewTrade, self.LoanClose, self.WorkOrderClose
        ], not closed or blocked)

        self._set_sale_sensitive(not closed and not blocked)
Exemple #25
0
    def _add_sellable(self, sellable, batch=None):
        quantity = self._read_quantity()
        if quantity == 0:
            return

        if not sellable.is_valid_quantity(quantity):
            warning(
                _(u"You cannot sell fractions of this product. "
                  u"The '%s' unit does not allow that") %
                sellable.unit_description)
            return

        if sellable.product:
            # If the sellable has a weight unit specified and we have a scale
            # configured for this station, go and check what the scale says.
            if (sellable and sellable.unit
                    and sellable.unit.unit_index == UnitType.WEIGHT
                    and self._scale_settings):
                self._read_scale(sellable)

        storable = sellable.product_storable
        if storable is not None:
            if not self._check_available_stock(storable, sellable):
                info(
                    _("You cannot sell more items of product %s. "
                      "The available quantity is not enough.") %
                    sellable.get_description())
                self.barcode.set_text('')
                self.barcode.grab_focus()
                return

        self._update_list(sellable, batch=batch)
        self.barcode.grab_focus()
Exemple #26
0
    def on_NewTrade__activate(self, action):
        if self._trade:
            if yesno(
                    _("There is already a trade in progress... Do you "
                      "want to cancel it and start a new one?"),
                    gtk.RESPONSE_NO, _("Cancel trade"), _("Finish trade")):
                self._clear_trade(remove=True)
            else:
                return

        if self._current_store:
            store = self._current_store
            store.savepoint('before_run_wizard_saletrade')
        else:
            store = api.new_store()

        trade = self.run_dialog(SaleTradeWizard, store)
        if trade:
            self._trade = trade
            self._current_store = store
        elif self._current_store:
            store.rollback_to_savepoint('before_run_wizard_saletrade')
        else:
            store.rollback(close=True)

        self._show_trade_infobar(trade)
Exemple #27
0
    def _get_account_columns(self):
        def format_withdrawal(value):
            if value < 0:
                return currency(abs(value)).format(symbol=True, precision=2)

        def format_deposit(value):
            if value > 0:
                return currency(value).format(symbol=True, precision=2)

        if self.model.account_type == Account.TYPE_INCOME:
            color_func = lambda x: False
        else:
            color_func = lambda x: x < 0
        return [Column('date', title=_("Date"), data_type=datetime.date, sorted=True),
                Column('code', title=_("Code"), data_type=unicode),
                Column('description', title=_("Description"),
                       data_type=unicode, expand=True),
                Column('account', title=_("Account"), data_type=unicode),
                Column('value',
                       title=self.model.account.get_type_label(out=False),
                       data_type=currency,
                       format_func=format_deposit),
                Column('value',
                       title=self.model.account.get_type_label(out=True),
                       data_type=currency,
                       format_func=format_withdrawal),
                ColoredColumn('total', title=_("Total"), data_type=currency,
                              color='red',
                              data_func=color_func)]
Exemple #28
0
 def get_columns(self):
     return [
         Column(
             'code',
             title=_('Reference'),
             data_type=str,
             width=130,
             justify=gtk.JUSTIFY_RIGHT),
         Column(
             'full_description',
             title=_('Description'),
             data_type=str,
             expand=True,
             searchable=True,
             ellipsize=pango.ELLIPSIZE_END),
         Column(
             'price',
             title=_('Price'),
             data_type=currency,
             width=110,
             justify=gtk.JUSTIFY_RIGHT),
         Column(
             'quantity_unit',
             title=_('Quantity'),
             data_type=unicode,
             width=110,
             justify=gtk.JUSTIFY_RIGHT),
         Column(
             'total',
             title=_('Total'),
             data_type=currency,
             justify=gtk.JUSTIFY_RIGHT,
             width=100)
     ]
Exemple #29
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()
Exemple #30
0
 def on_quantity__validate(self, widget, value):
     if self._expanded_edition:
         return
     if value <= 0:
         return ValidationError(_(u'The quantity should be positive.'))
     if value and not self._has_stock(value):
         return ValidationError(_(u'Quantity not available in stock.'))
Exemple #31
0
 def get_description(self):
     return _('%s (upid: %s)') % (self.person.name, self.crm_number)
Exemple #32
0
class OpticalPatientMeasures(Domain):

    __storm_table__ = 'optical_patient_measures'

    EYE_LEFT = u'left'
    EYE_RIGHT = u'right'

    eye_options = {
        EYE_LEFT: _('Left Eye'),
        EYE_RIGHT: _('Right Eye'),
    }

    create_date = DateTimeCol(default_factory=StatementTimestamp)

    client_id = IdCol(allow_none=False)
    #: The related client
    client = Reference(client_id, 'Client.id')

    responsible_id = IdCol(allow_none=False)
    #: The user that registred this information
    responsible = Reference(responsible_id, 'LoginUser.id')

    dominant_eye = EnumCol(allow_none=False, default=EYE_LEFT)

    le_keratometer_horizontal = UnicodeCol()
    le_keratometer_vertical = UnicodeCol()
    le_keratometer_axis = UnicodeCol()

    re_keratometer_horizontal = UnicodeCol()
    re_keratometer_vertical = UnicodeCol()
    re_keratometer_axis = UnicodeCol()

    le_eyebrown = UnicodeCol()
    le_eyelash = UnicodeCol()
    le_conjunctiva = UnicodeCol()
    le_sclerotic = UnicodeCol()
    le_iris_diameter = UnicodeCol()
    le_eyelid = UnicodeCol()
    le_eyelid_opening = UnicodeCol()
    le_cornea = UnicodeCol()
    #: Tear breakup time. How much time the eye takes to produce a tear
    le_tbut = UnicodeCol()

    #: test that checks how much tear the eye produces
    le_schirmer = UnicodeCol()

    re_eyebrown = UnicodeCol()
    re_eyelash = UnicodeCol()
    re_conjunctiva = UnicodeCol()
    re_sclerotic = UnicodeCol()
    re_iris_diameter = UnicodeCol()
    re_eyelid = UnicodeCol()
    re_eyelid_opening = UnicodeCol()
    re_cornea = UnicodeCol()

    #: Tear breakup time. How much time the eye takes to produce a tear
    re_tbut = UnicodeCol()

    #: test that checks how much tear the eye produces
    re_schirmer = UnicodeCol()

    notes = UnicodeCol()

    @property
    def responsible_name(self):
        return self.responsible.get_description()
Exemple #33
0
class OpticalPatientHistory(Domain):

    __storm_table__ = 'optical_patient_history'

    #: Never used lenses before
    TYPE_FIRST_USER = u'first-user'

    #: Is currently a user
    TYPE_SECOND_USER = u'second-user'

    #: Has used lenses before, but stopped
    TYPE_EX_USER = u'ex-user'

    user_types = collections.OrderedDict([
        (TYPE_FIRST_USER, _('First User')),
        (TYPE_SECOND_USER, _('Second User')),
        (TYPE_EX_USER, _('Ex-User')),
    ])

    create_date = DateTimeCol(default_factory=StatementTimestamp)

    client_id = IdCol(allow_none=False)
    #: The related client
    client = Reference(client_id, 'Client.id')

    responsible_id = IdCol(allow_none=False)
    #: The user that registred this information
    responsible = Reference(responsible_id, 'LoginUser.id')

    #
    #   Section 1: General questions
    #

    #: If the patient is a first time user for contact lenses or not.
    user_type = EnumCol(allow_none=False, default=TYPE_FIRST_USER)

    #: What is the occupation of the patient
    occupation = UnicodeCol()

    #: Details about the work environment (if it as air conditioning, dust,
    #: chemical products)
    work_environment = UnicodeCol()

    #
    #   First time user
    #

    #: If the patient has ever tested any contact lenses
    has_tested = UnicodeCol()

    #: What brands the patient has tested
    tested_brand = UnicodeCol()

    #: If previous tests irritated the eye
    eye_irritation = UnicodeCol()

    #: What is the main purpose for using contact lenses?
    purpose_of_use = UnicodeCol()

    #: How many hours per day the patient intends to use the contact lenses
    intended_hour_usage = UnicodeCol()

    #
    #   Second time / ex user
    #

    #: Previous brand of the client.
    previous_brand = UnicodeCol()

    #: What the previous brand felt like
    previous_feeling = UnicodeCol()

    #: Have ever had any cornea issues
    cornea_issues = UnicodeCol()

    #: How many hours per day the client used the lenses
    hours_per_day_usage = UnicodeCol()

    #
    #   Second time user
    #

    #: For how long is a user
    user_since = UnicodeCol()

    #: Bring the previous lenses?
    has_previous_lenses = UnicodeCol()

    #: Previous lenses observations
    previous_lenses_notes = UnicodeCol()

    #
    #   Ex User
    #

    #: How long since the last use.
    last_use = UnicodeCol()

    #: why stopped using
    stop_reason = UnicodeCol()

    #: Did frequent removal of proteins?
    protein_removal = UnicodeCol()

    #: What cleaning product used?
    cleaning_product = UnicodeCol()

    #: Free notes.
    history_notes = UnicodeCol()

    #
    #   Section 2: Adaptation test
    #

    #: If the patient ever had eye injuries
    eye_injury = UnicodeCol()

    #: Any kind of recent pathology, like pink-eye
    recent_pathology = UnicodeCol()

    #: Is currently using eye drops
    using_eye_drops = UnicodeCol()

    #: Does the patient have health problems
    health_problems = UnicodeCol()

    #: Is the patient is using any kind of medicament
    using_medicament = UnicodeCol()

    #: Does the patient family has any health problems
    family_health_problems = UnicodeCol()

    #: How the eyes feel at the end of the day (burn, itch, etc...)
    end_of_day_feeling = UnicodeCol()

    #: Free notes.
    adaptation_notes = UnicodeCol()

    @property
    def responsible_name(self):
        return self.responsible.get_description()
Exemple #34
0
class OpticalWorkOrder(Domain):
    """This holds the necessary information to execute an work order for optical
    stores.

    This includes all the details present in the prescription.

    For reference:
    http://en.wikipedia.org/wiki/Eyeglass_prescription

    See http://en.wikipedia.org/wiki/Eyeglass_prescription#Abbreviations_and_terms
    for reference no the names used here.

    In some places, RE is used as a short for right eye, and LE for left eye
    """
    __storm_table__ = 'optical_work_order'

    #: Lens used in glasses
    LENS_TYPE_OPHTALMIC = u'ophtalmic'

    #: Contact lenses
    LENS_TYPE_CONTACT = u'contact'

    #: The frame for the lens is a closed ring
    FRAME_TYPE_CLOSED_RING = u'closed-ring'

    #: The frame uses a nylon string to hold the lenses.
    FRAME_TYPE_NYLON = u'nylon'

    #: The frame is made 3 pieces
    FRAME_TYPE_3_PIECES = u'3-pieces'

    lens_types = {
        LENS_TYPE_OPHTALMIC: _('Ophtalmic'),
        LENS_TYPE_CONTACT: _('Contact'),
    }

    frame_types = {
        # Translators: Aro fechado
        FRAME_TYPE_3_PIECES: _('Closed ring'),

        # Translators: Fio de nylon
        FRAME_TYPE_NYLON: _('Nylon String'),

        # Translators: 3 preças
        FRAME_TYPE_CLOSED_RING: _('3 pieces'),
    }

    work_order_id = IdCol(allow_none=False)
    work_order = Reference(work_order_id, 'WorkOrder.id')

    medic_id = IdCol()
    medic = Reference(medic_id, 'OpticalMedic.id')

    prescription_date = DateTimeCol()

    #: The name of the patient. Note that we already have the client of the work
    #: order, but the patient may be someone else (like the son, father,
    #: etc...). Just the name is enough
    patient = UnicodeCol()

    #: The type of the lens, Contact or Ophtalmic
    lens_type = EnumCol(default=LENS_TYPE_OPHTALMIC)

    #
    #   Frame
    #

    #: The type of the frame. One of OpticalWorkOrder.FRAME_TYPE_*
    frame_type = EnumCol(default=FRAME_TYPE_CLOSED_RING)

    #: The vertical frame measure
    frame_mva = DecimalCol(default=decimal.Decimal(0))

    #: The horizontal frame measure
    frame_mha = DecimalCol(default=decimal.Decimal(0))

    #: The diagonal frame measure
    frame_mda = DecimalCol(default=decimal.Decimal(0))

    #: The brige is the part of the frame between the two lenses, above the nose.
    frame_bridge = DecimalCol()

    #
    # Left eye distance vision
    #

    le_distance_spherical = DecimalCol(default=0)
    le_distance_cylindrical = DecimalCol(default=0)
    le_distance_axis = DecimalCol(default=0)
    le_distance_prism = DecimalCol(default=0)
    le_distance_base = DecimalCol(default=0)
    le_distance_height = DecimalCol(default=0)

    #: Pupil distance (DNP in pt_BR)
    le_distance_pd = DecimalCol(default=0)
    le_addition = DecimalCol(default=0)

    #
    # Left eye distance vision
    #
    le_near_spherical = DecimalCol(default=0)
    le_near_cylindrical = DecimalCol(default=0)
    le_near_axis = DecimalCol(default=0)

    #: Pupil distance (DNP in pt_BR)
    le_near_pd = DecimalCol(default=0)

    #
    # Right eye distance vision
    #

    re_distance_spherical = DecimalCol(default=0)
    re_distance_cylindrical = DecimalCol(default=0)
    re_distance_axis = DecimalCol(default=0)
    re_distance_prism = DecimalCol(default=0)
    re_distance_base = DecimalCol(default=0)
    re_distance_height = DecimalCol(default=0)

    #: Pupil distance (DNP in pt_BR)
    re_distance_pd = DecimalCol(default=0)
    re_addition = DecimalCol(default=0)

    #
    # Right eye near vision
    #
    re_near_spherical = DecimalCol(default=0)
    re_near_cylindrical = DecimalCol(default=0)
    re_near_axis = DecimalCol(default=0)

    #: Pupil distance (DNP in pt_BR)
    re_near_pd = DecimalCol(default=0)

    #
    # Class methods
    #

    @classmethod
    def find_by_work_order(cls, store, work_order):
        return store.find(cls, work_order_id=work_order.id).one()

    #
    # Properties
    #

    @property
    def frame_type_str(self):
        return self.frame_types.get(self.frame_type, '')

    @property
    def lens_type_str(self):
        return self.lens_types.get(self.lens_type, '')

    #
    # Public API
    #

    def can_create_purchase(self):
        work_order = self.work_order
        if work_order.status != WorkOrder.STATUS_WORK_IN_PROGRESS:
            return False

        if not work_order.sale:
            return False

        purchases = [i.purchase_item for i in work_order.get_items()]
        # If there are any item in this work order that was not purchased yet, then we
        # can still create a purchase
        return None in purchases

    def create_purchase(self, supplier, work_order_item, is_freebie):
        """Create a purchase

        :param supplier: the |supplier| of that purchase
        :param work_order_item: The work order item that a purchase is being created
        for.
        :param is_freebie: indicates if the item is a freebie
        """
        sellable = work_order_item.sellable
        store = self.work_order.store
        current_branch = api.get_current_branch(store)
        purchase = PurchaseOrder(store=store,
                                 status=PurchaseOrder.ORDER_PENDING,
                                 supplier=supplier,
                                 responsible=api.get_current_user(store),
                                 branch=current_branch,
                                 work_order=self.work_order)
        if is_freebie:
            purchase.notes = _('The product %s is a freebie') % sellable.description
            # FIXME We may want the cost 0, but as it is we wont be able to
            # receive this purchase without changing the receiving. We must
            # evaluate the consequences of changing the receiving a little bit
            # further in order to change that behavior.
            cost = decimal.Decimal('0.01')
        else:
            psi = ProductSupplierInfo.find_by_product_supplier(store, sellable.product,
                                                               supplier)
            cost = psi.base_cost if psi else sellable.cost

        # Add the sellable to the purchase
        purchase_item = purchase.add_item(sellable,
                                          quantity=work_order_item.quantity,
                                          cost=cost)
        work_order_item.purchase_item = purchase_item

        purchase.confirm()
        return purchase

    def can_receive_purchase(self, purchase):
        work_order = self.work_order
        if not work_order.status == WorkOrder.STATUS_WORK_FINISHED:
            return False

        # XXX Lets assume that there is only on purchase
        return purchase and purchase.status == PurchaseOrder.ORDER_CONFIRMED

    def receive_purchase(self, purchase_order, reserve=False):
        receiving = purchase_order.create_receiving_order()
        receiving.confirm()
        if reserve:
            self.reserve_products(purchase_order)

    def reserve_products(self, purchase_order):
        for item in self.work_order.get_items():
            if not item.purchase_item:
                continue
            sale_item = item.sale_item
            to_reserve = sale_item.quantity - sale_item.quantity_decreased
            if to_reserve > 0:
                sale_item.reserve(quantize(to_reserve))

    def copy(self, target):
        """Make a copy of self into a target |work_order|

        :param target: a |work_order|
        """
        props = ['lens_type', 'le_distance_spherical', 'le_distance_cylindrical',
                 'le_distance_axis', 'le_distance_prism', 'le_distance_base',
                 'le_distance_height', 'le_distance_pd', 'le_addition',
                 'le_near_spherical', 'le_near_cylindrical', 'le_near_axis',
                 'le_near_pd', 're_distance_spherical', 're_distance_cylindrical',
                 're_distance_axis', 're_distance_prism', 're_distance_base',
                 're_distance_height', 're_distance_pd', 're_addition',
                 're_near_spherical', 're_near_cylindrical', 're_near_axis', 're_near_pd']

        for prop in props:
            value = getattr(self, prop)
            setattr(target, prop, value)
Exemple #35
0
    def refresh(self):
        if stoq.trial_mode:
            self.status = ResourceStatus.STATUS_NA
            self.reason = (_('Online features are not available in trial mode'))
            self.reason_long = _('Online features require a subscription of Stoq.link')
            return

        if not api.sysparam.get_bool('ONLINE_SERVICES'):
            self.status = ResourceStatus.STATUS_NA
            self.reason = _('Backup service not running because '
                            '"Online Services" is disabled')
            self.reason_long = _('Enable the parameter "Online Services" '
                                 'on the "Admin" app to solve this issue')
            return

        try:
            key = self._get_key()
        except ServerError:
            pass
        else:
            if not key:
                self.status = self.STATUS_WARNING
                self.reason = _("Backup key not configured")
                self.reason_long = _('Click on "Configure" button to '
                                     'configure the backup key')
                return

        try:
            response = self._get_server_status()
        except Exception as e:
            self.status = self.STATUS_WARNING
            self.reason = _("Could not communicate with Stoq.link")
            self.reason_long = str(e)
            return

        if response.status_code != 200:
            self.status = self.STATUS_WARNING
            self.reason = _("Could not communicate with Stoq.link")
            self.reason_long = None
            return

        data = response.json()
        if data['latest_backup_date']:
            backup_date = dateutil.parser.parse(data['latest_backup_date'])
            delta = datetime.datetime.today() - backup_date

            if delta.days > 3:
                self.status = self.STATUS_WARNING
                self.reason = _("Backup is late. Last backup date is %s") % (
                    backup_date.strftime('%x'))
                self.reason_long = _("Check your Stoq Server logs to see if "
                                     "there's any problem with it")
            else:
                self.status = self.STATUS_OK
                self.reason = _("Backup is up-to-date. Last backup date is %s") % (
                    backup_date.strftime('%x'))
                self.reason_long = None
        else:
            self.status = self.STATUS_WARNING
            self.reason = _("There's no backup data yet")
            self.reason_long = None
Exemple #36
0
 def _on_manager__action_finished(self, manager, action, retval):
     if isinstance(retval, Exception):
         warning(_('An error happened when executing "%s"') % (action.label, ),
                 str(retval))
     self._update_ui()
Exemple #37
0
class TillDailyMovementDialog(BaseEditor):
    """Shows informations related to till operations over a daterange.
    It can also be filtered by branch.
    """

    title = _("Daily Movement")
    hide_footer = True
    size = (950, 450)
    model_type = Settable
    gladefile = "TillDailyMovementDialog"
    proxy_widgets = ['branch', 'in_subtotal', 'in_credit', 'in_total',
                     'out_subtotal', 'out_credit', 'out_total']

    #
    #  Private
    #

    def _setup_widgets(self):
        # Branches combo
        items = api.get_branches_for_filter(self.store)
        self.branch.prefill(items)

        # Daterange filter
        self.date_filter = DateSearchFilter(_(u'Date:'))
        self.date_filter.clear_options()
        self.date_filter.add_custom_options()
        for option in [Today, Yesterday, LastWeek, LastMonth]:
            self.date_filter.add_option(option)
        self.date_filter.select(position=0)
        self.daterange_hbox.pack_start(self.date_filter, False, False, 0)
        self.date_filter.show_all()

        # Setting report lists' columns
        self.sales_list.set_columns(self._get_sales_columns())
        self.inpayments_list.set_columns(self._get_lonely_payments_columns())
        self.purchases_list.set_columns(self._get_purchases_columns())
        self.outpayments_list.set_columns(self._get_lonely_payments_columns())
        self.return_sales_list.set_columns(self._get_return_sales_columns())
        self.supplies_list.set_columns(self._get_till_columns())
        self.removals_list.set_columns(self._get_till_columns())
        self.permethod_list.set_columns(self._get_permethod_columns())
        self.percard_list.set_columns(self._get_percard_columns())

        # Print button is insensitive, until the first report is generated
        self.print_button.set_sensitive(False)

        self._setup_summary_labels()

    def _get_sales_columns(self):
        return [IdentifierColumn('identifier', title=_('Sale #'), sorted=True),
                Column('salesperson', title=_('Sales Person'), data_type=str),
                Column('client', title=_('Client'), data_type=str, expand=True),
                Column('branch', title=_('Branch'), data_type=str, visible=False),
                Column('value', title=_('Value'), data_type=str,
                       justify=Gtk.Justification.RIGHT)]

    def _get_lonely_payments_columns(self):
        return [IdentifierColumn('identifier', title=_('Payment #'), sorted=True),
                Column('method', title=_('Method'), data_type=str),
                Column('description', title=_('Description'), expand=True,
                       data_type=str),
                Column('branch', title=_('Branch'), data_type=str, visible=False),
                Column('value', title=_('Payment Value'), data_type=currency)]

    def _get_purchases_columns(self):
        return [IdentifierColumn('identifier', title=_('Code #'), sorted=True),
                Column('status_str', title=_('Status'), data_type=str),
                Column('responsible_name', title=_('Responsible'), expand=True,
                       data_type=str),
                Column('branch_name', title=_('Branch'), data_type=str),
                Column('notes', title=_('Notes'), data_type=str),
                Column('supplier_name', title=_('Supplier'), data_type=str),
                Column('purchase_total', title=_('Value'), data_type=currency)]

    def _get_return_sales_columns(self):
        return [IdentifierColumn('identifier', title=_('Code #'), sorted=True),
                Column('salesperson', title=_('Sales Person'), data_type=str),
                Column('client', title=_('Client'), expand=True, data_type=str),
                Column('return_date', title=_('Return Date'),
                       data_type=datetime.date),
                Column('branch', title=_('Branch'), data_type=str, visible=False),
                Column('value', title=_('Sale Value'), data_type=currency)]

    def _get_till_columns(self):
        return [IdentifierColumn('identifier', title=_('Entry #'), sorted=True),
                Column('description', title=_('Description'), data_type=str,
                       expand=True),
                Column('branch_name', title=_('Branch'), data_type=str, visible=False),
                Column('value', title=_('Value'), data_type=currency)]

    def _get_permethod_columns(self):
        return [Column('method', title=_('Payment Method'), sorted=True,
                       expand=True),
                Column('in_value', title=_('Income Total'), data_type=currency),
                Column('out_value', title=_('Outgoing Total'),
                       data_type=currency)]

    def _get_percard_columns(self):
        return [Column('provider', title=_('Provider Name'), data_type=str,
                       expand=True),
                Column('income', title=_('Income Total'), data_type=currency)]

    def _create_summary_label(self, report, column='value', label=None):
        # Setting tha data
        obj_list = getattr(self, report + '_list')
        box = getattr(self, report + '_vbox')
        if label is None:
            label = _('Total:')
        label = '<b>%s</b>' % api.escape(label)
        value_format = '<b>%s</b>'

        # Creating the label
        label = SummaryLabel(klist=obj_list, column=column, label=label,
                             value_format=value_format)

        # Displaying the label
        box.pack_start(label, False, False, 0)
        label.show()
        return label

    def _setup_summary_labels(self):
        # Supplies
        self.supplies_label = self._create_summary_label('supplies')
        # Removals
        self.removals_label = self._create_summary_label('removals')
        # Percard
        self.percard_label = self._create_summary_label('percard',
                                                        column='income')

    def _update_summary_labels(self):
        self.supplies_label.update_total()
        self.removals_label.update_total()
        self.percard_label.update_total()
        self.proxy.update_many(('in_subtotal', 'in_credit', 'in_total',
                                'out_subtotal', 'out_credit', 'out_total'))

    def _generate_dailymovement_data(self, store):
        query = And(Payment.status.is_in([Payment.STATUS_PENDING,
                                          Payment.STATUS_PAID]),
                    self._get_query(Payment.open_date, Payment.branch))

        # Keys are the sale objects, and values are lists with all payments
        self.sales = collections.OrderedDict()

        # Keys are the returned sale objects, and values are lists with all payments
        self.return_sales = collections.OrderedDict()
        self.purchases = collections.OrderedDict()

        # lonely input and output payments
        self.lonely_in_payments = []
        self.lonely_out_payments = []

        # values are lists with the first element the summary of the input, and
        # the second the summary of the output
        method_summary = {}
        self.card_summary = {}

        result = store.find(DailyInPaymentView, query)
        for p in result.order_by(Sale.identifier, Payment.identifier):
            if p.sale:
                subtotal = p.sale_subtotal
                total = p.sale.get_total_sale_amount(subtotal)
                salesperson = p.salesperson_name or _('Not Specified')
                client = p.client_name or _('Not Specified')
                sale = DailyMovementSale(identifier=p.sale.identifier,
                                         salesperson=salesperson,
                                         client=client,
                                         branch=p.branch_name,
                                         value=get_formatted_price(total))
                sale_payments = self.sales.setdefault(sale, {})
                details = ''
                method_desc = p.method.get_description()
                if p.check_data:
                    account = p.check_data.bank_account
                    numbers = [payment.payment_number for payment in p.sale.payments
                               if bool(payment.payment_number)]
                    # Ensure that the check numbers are ordered
                    numbers.sort()
                    parts = []
                    if account.bank_number:
                        parts.append(_(u'Bank: %s') % account.bank_number)
                    if account.bank_branch:
                        parts.append(_(u'Agency: %s') % account.bank_branch)
                    if account.bank_account:
                        parts.append(_(u'Account: %s') % account.bank_account)
                    if numbers:
                        parts.append(_(u'Numbers: %s') % ', '.join(numbers))
                    details = ' / '.join(parts)

                if p.card_data:
                    if p.card_data.card_type == CreditCardData.TYPE_DEBIT:
                        method_desc += ' ' + _('Debit')
                    else:
                        method_desc += ' ' + _(u'Credit')
                    details = '%s - %s - %s' % (p.card_data.auth,
                                                p.card_data.provider.short_name or '',
                                                p.card_data.device.description or '')

                key = (method_desc, details)
                item = sale_payments.setdefault(key, [0, 0])
                item[0] += p.value
                item[1] += 1

            else:
                self.lonely_in_payments.append(p)

            method_summary.setdefault(p.method, [0, 0])
            method_summary[p.method][0] += p.value
            if p.card_data:
                type_desc = p.card_data.short_desc[p.card_data.card_type]
                key = (p.card_data.provider.short_name, type_desc)
                self.card_summary.setdefault(key, 0)
                self.card_summary[key] += p.value

        result = store.find(DailyOutPaymentView, query)
        for p in result.order_by(Payment.identifier):
            if p.purchase:
                purchase_payments = self.purchases.setdefault(p.purchase, [])
                purchase_payments.append(p)
            elif p.sale:
                subtotal = p.sale_subtotal
                value = p.sale.get_total_sale_amount(subtotal)
                salesperson = p.salesperson_name or _('Not Specified')
                client = p.client_name or _('Not Specified')
                sale = DailyMovementSale(identifier=p.sale.identifier,
                                         salesperson=salesperson,
                                         client=client,
                                         return_date=p.sale.return_date,
                                         branch=p.branch_name,
                                         value=value)
                return_sales_payment = self.return_sales.setdefault(sale, [])
                return_sales_payment.append(p)
            else:
                self.lonely_out_payments.append(p)

            method_summary.setdefault(p.method, [0, 0])
            method_summary[p.method][1] += p.value

        self.method_summary = []
        for method, (in_value, out_value) in method_summary.items():
            self.method_summary.append((method,
                                        in_value,
                                        out_value))
        self.method_summary.sort(key=lambda m: _(m[0].description))

        # Till removals
        query = And(Eq(TillEntry.payment_id, None),
                    self._get_query(TillEntry.date, TillEntry.branch),
                    TillEntry.value < 0)
        self.till_removals = store.find(TillEntry, query)

        # Till supply
        query = And(Eq(TillEntry.payment_id, None),
                    self._get_query(TillEntry.date, TillEntry.branch),
                    TillEntry.value > 0)
        self.till_supplies = store.find(TillEntry, query)

    def _show_lonely_payments(self, payments, widget):
        widget.clear()
        for payment in payments:
            payment_data = Settable(identifier=payment.identifier,
                                    method=payment.method.get_description(),
                                    description=payment.description,
                                    branch=payment.branch_name,
                                    value=payment.value)
            widget.append(payment_data)

    def _show_report(self):
        self._generate_dailymovement_data(self.store)

        # Sale data
        self.sales_list.clear()
        for sale, payments in self.sales.items():
            self.sales_list.append(None, sale)
            for details, values in payments.items():
                value = '%s (%sx)' % (get_formatted_price(values[0]), values[1])
                payment_data = Settable(identifier=None,
                                        salesperson=details[0],
                                        client=details[1],
                                        value=value)
                self.sales_list.append(sale, payment_data)

        # Lonely in payments
        self._show_lonely_payments(self.lonely_in_payments,
                                   self.inpayments_list)

        # Purchase data
        self.purchases_list.clear()
        for purchase, payments in self.purchases.items():
            self.purchases_list.append(None, purchase)
            for payment in payments:
                # TODO Add details refering to Bank, Agency later
                payment_data = Settable(identifier=payment.identifier,
                                        notes=payment.method.get_description())
                self.purchases_list.append(purchase, payment_data)

        # Lonely out payments
        self._show_lonely_payments(self.lonely_out_payments,
                                   self.outpayments_list)

        # Return sales
        self.return_sales_list.clear()
        for sale, payments in self.return_sales.items():
            self.return_sales_list.append(None, sale)
            for payment in payments:
                payment_data = Settable(identifier=payment.identifier,
                                        salesperson=payment.method.get_description(),
                                        client=payment.description,
                                        value=get_formatted_price(payment.value))
                self.return_sales_list.append(sale, payment_data)

        # Supplies
        self.supplies_list.clear()
        self.supplies_list.add_list(self.till_supplies)

        # Removals
        self.removals_list.clear()
        self.removals_list.add_list(self.till_removals)

        # Summary's per payment method data
        self.permethod_list.clear()
        self.model.in_subtotal = self.model.out_subtotal = 0
        self.model.in_credit = self.model.out_credit = currency(0)
        for method in self.method_summary:
            method_data = Settable(method=_(method[0].description),
                                   in_value=method[1],
                                   out_value=method[2])
            self.permethod_list.append(method_data)
            self.model.in_subtotal += method[1]
            self.model.out_subtotal += method[2]
            if method[0].method_name == 'credit':
                self.model.in_credit = currency(method[1])
                self.model.out_credit = currency(method[2])

        self.model.in_subtotal = currency(self.model.in_subtotal)
        self.model.out_subtotal = currency(self.model.out_subtotal)
        self.model.in_total = currency(self.model.in_subtotal -
                                       self.model.in_credit)
        self.model.out_total = currency(self.model.out_subtotal -
                                        self.model.out_credit)

        # Summary's per card provider data
        self.percard_list.clear()
        keys = list(self.card_summary.keys())
        for key in sorted(keys):
            card_summary_data = Settable(provider=key[0] + ' ' + key[1],
                                         income=self.card_summary[key])
            self.percard_list.append(card_summary_data)

        self._update_summary_labels()

    def _get_query(self, date_attr, branch_attr):
        daterange = self.get_daterange()
        query = [Date(date_attr) >= Date(daterange[0]),
                 Date(date_attr) <= Date(daterange[1])]

        branch = self.model.branch
        if branch is not None:
            query.append(branch_attr == branch)
        return And(*query)

    #
    # Public API
    #

    def get_daterange(self):
        start = self.date_filter.get_start_date()
        end = self.date_filter.get_end_date()
        return (start, end)

    def set_daterange(self, start, end=None):
        self.date_filter.set_state(start, end)

    #
    # BaseEditor Hooks
    #

    def create_model(self, store):
        return Settable(branch=api.get_current_branch(store),
                        in_total=currency(0), in_credit=currency(0),
                        in_subtotal=currency(0), out_total=currency(0),
                        out_credit=currency(0), out_subtotal=currency(0))

    def setup_proxies(self):
        self._setup_widgets()
        self.proxy = self.add_proxy(self.model, TillDailyMovementDialog.proxy_widgets)

    #
    # Callbacks
    #

    def on_search_button__clicked(self, widget):
        self._show_report()
        self.print_button.set_sensitive(True)

    def on_print_button__clicked(self, widget):
        branch = self.model.branch
        daterange = self.get_daterange()
        print_report(TillDailyMovementReport, self.store, branch, daterange, self)
Exemple #38
0
    def _show_report(self):
        self._generate_dailymovement_data(self.store)

        # Sale data
        self.sales_list.clear()
        for sale, payments in self.sales.items():
            self.sales_list.append(None, sale)
            for details, values in payments.items():
                value = '%s (%sx)' % (get_formatted_price(values[0]), values[1])
                payment_data = Settable(identifier=None,
                                        salesperson=details[0],
                                        client=details[1],
                                        value=value)
                self.sales_list.append(sale, payment_data)

        # Lonely in payments
        self._show_lonely_payments(self.lonely_in_payments,
                                   self.inpayments_list)

        # Purchase data
        self.purchases_list.clear()
        for purchase, payments in self.purchases.items():
            self.purchases_list.append(None, purchase)
            for payment in payments:
                # TODO Add details refering to Bank, Agency later
                payment_data = Settable(identifier=payment.identifier,
                                        notes=payment.method.get_description())
                self.purchases_list.append(purchase, payment_data)

        # Lonely out payments
        self._show_lonely_payments(self.lonely_out_payments,
                                   self.outpayments_list)

        # Return sales
        self.return_sales_list.clear()
        for sale, payments in self.return_sales.items():
            self.return_sales_list.append(None, sale)
            for payment in payments:
                payment_data = Settable(identifier=payment.identifier,
                                        salesperson=payment.method.get_description(),
                                        client=payment.description,
                                        value=get_formatted_price(payment.value))
                self.return_sales_list.append(sale, payment_data)

        # Supplies
        self.supplies_list.clear()
        self.supplies_list.add_list(self.till_supplies)

        # Removals
        self.removals_list.clear()
        self.removals_list.add_list(self.till_removals)

        # Summary's per payment method data
        self.permethod_list.clear()
        self.model.in_subtotal = self.model.out_subtotal = 0
        self.model.in_credit = self.model.out_credit = currency(0)
        for method in self.method_summary:
            method_data = Settable(method=_(method[0].description),
                                   in_value=method[1],
                                   out_value=method[2])
            self.permethod_list.append(method_data)
            self.model.in_subtotal += method[1]
            self.model.out_subtotal += method[2]
            if method[0].method_name == 'credit':
                self.model.in_credit = currency(method[1])
                self.model.out_credit = currency(method[2])

        self.model.in_subtotal = currency(self.model.in_subtotal)
        self.model.out_subtotal = currency(self.model.out_subtotal)
        self.model.in_total = currency(self.model.in_subtotal -
                                       self.model.in_credit)
        self.model.out_total = currency(self.model.out_subtotal -
                                        self.model.out_credit)

        # Summary's per card provider data
        self.percard_list.clear()
        keys = list(self.card_summary.keys())
        for key in sorted(keys):
            card_summary_data = Settable(provider=key[0] + ' ' + key[1],
                                         income=self.card_summary[key])
            self.percard_list.append(card_summary_data)

        self._update_summary_labels()
Exemple #39
0
 def _get_percard_columns(self):
     return [Column('provider', title=_('Provider Name'), data_type=str,
                    expand=True),
             Column('income', title=_('Income Total'), data_type=currency)]
Exemple #40
0
 def _get_permethod_columns(self):
     return [Column('method', title=_('Payment Method'), sorted=True,
                    expand=True),
             Column('in_value', title=_('Income Total'), data_type=currency),
             Column('out_value', title=_('Outgoing Total'),
                    data_type=currency)]
Exemple #41
0
 def _get_till_columns(self):
     return [IdentifierColumn('identifier', title=_('Entry #'), sorted=True),
             Column('description', title=_('Description'), data_type=str,
                    expand=True),
             Column('branch_name', title=_('Branch'), data_type=str, visible=False),
             Column('value', title=_('Value'), data_type=currency)]
Exemple #42
0
class StatusDialog(BasicDialog):
    size = (700, 400)
    title = _("System Status")

    def __init__(self, *args, **kwargs):
        kwargs.setdefault('size', self.size)
        kwargs.setdefault('title', self.title)

        super(StatusDialog, self).__init__(*args, **kwargs)

        self._manager = ResourceStatusManager.get_instance()
        self._manager.connect('status-changed',
                              self._on_manager__status_changed)
        self._manager.connect('action-finished',
                              self._on_manager__action_finished)

        with api.new_store() as store:
            user = api.get_current_user(store)
            self._is_admin = user.profile.check_app_permission(u'admin')

        self._widgets = {}
        self._setup_ui()

    #
    #  Private
    #

    def _setup_ui(self):
        self.cancel_button.hide()
        for child in self.vbox.get_children():
            self.vbox.remove(child)

        self._refresh_btn = Gtk.Button(stock=Gtk.STOCK_REFRESH)
        action_area = self.toplevel.get_action_area()
        action_area.pack_start(self._refresh_btn, True, True, 6)
        action_area.set_child_secondary(self._refresh_btn, True)
        self._refresh_btn.connect('clicked', self._on_refresh_btn__clicked)
        self._refresh_btn.show()

        sw = Gtk.ScrolledWindow()
        sw.set_policy(Gtk.PolicyType.AUTOMATIC,
                      Gtk.PolicyType.AUTOMATIC)
        self.vbox.add(sw)

        viewport = Gtk.Viewport()
        viewport.set_shadow_type(Gtk.ShadowType.NONE)
        sw.add(viewport)

        alignment = Gtk.Alignment.new(0.0, 0.0, 1.0, 1.0)
        alignment.set_padding(6, 6, 6, 6)
        viewport.add(alignment)

        vbox = Gtk.VBox(spacing=12)
        alignment.add(vbox)

        resources = self._manager.resources.items()
        for i, (name, resource) in enumerate(resources):
            hbox = Gtk.HBox(spacing=6)

            img = Gtk.Image()
            hbox.pack_start(img, False, True, 0)
            lbl = Gtk.Label()
            hbox.pack_start(lbl, False, True, 0)

            buttonbox = Gtk.HButtonBox()
            hbox.pack_end(buttonbox, False, True, 0)

            self._widgets[name] = (img, lbl, buttonbox)
            vbox.pack_start(hbox, False, True, 6)

            if i < len(resources) - 1:
                separator = Gtk.HSeparator()
                vbox.pack_start(separator, False, True, 0)

        self.vbox.show_all()
        self._update_ui()

    def _update_ui(self):
        running_action = self._manager.running_action
        self._refresh_btn.set_sensitive(running_action is None)

        for name, resource in self._manager.resources.items():
            img, lbl, buttonbox = self._widgets[name]

            status_stock, _ignored = _status_mapper[resource.status]
            img.set_from_stock(status_stock, Gtk.IconSize.LARGE_TOOLBAR)
            if resource.reason and resource.reason_long:
                text = "<b>%s</b>: %s\n<i>%s</i>" % (
                    api.escape(resource.label),
                    api.escape(resource.reason),
                    api.escape(resource.reason_long))
            elif resource.reason:
                text = "<b>%s</b>: %s" % (
                    api.escape(resource.label),
                    api.escape(resource.reason))
            else:
                text = _("Status not available...")

            for child in buttonbox.get_children():
                buttonbox.remove(child)
            for action in resource.get_actions():
                btn = Gtk.Button.new_with_label(action.label)

                if running_action is not None:
                    btn.set_sensitive(False)

                if action.admin_only and not self._is_admin:
                    btn.set_sensitive(False)
                    btn.set_tooltip_text(
                        _("Only admins can execute this action"))

                # If the action is the running action, add a spinner together
                # with the label to indicate that it is running
                if action == running_action:
                    spinner = Gtk.Spinner()
                    hbox = Gtk.HBox(spacing=6)
                    child = btn.get_child()
                    btn.remove(child)
                    hbox.add(child)
                    hbox.add(spinner)
                    btn.add(hbox)
                    spinner.start()
                    hbox.show_all()

                btn.show()
                btn.connect('clicked', self._on_action_btn__clicked, action)
                buttonbox.add(btn)

            lbl.set_markup(text)

    def _handle_action(self, action):
        retval = self._manager.handle_action(action)
        if action.threaded:
            thread = retval

            msg = _('Executing "%s". This might take a while...') % (
                action.label, )
            progress_dialog = ProgressDialog(msg, pulse=True)
            progress_dialog.start(wait=100)
            progress_dialog.set_transient_for(self.get_toplevel())
            progress_dialog.set_title(action.resource.label)
            progress_dialog.connect(
                'cancel', lambda d: terminate_thread(thread))

            while thread.is_alive():
                if Gtk.events_pending():
                    Gtk.main_iteration_do(False)

            progress_dialog.stop()

        self._update_ui()

    #
    #  Callbacks
    #

    def _on_manager__status_changed(self, manager, status):
        self._update_ui()

    def _on_manager__action_finished(self, manager, action, retval):
        if isinstance(retval, Exception):
            warning(_('An error happened when executing "%s"') % (action.label, ),
                    str(retval))
        self._update_ui()

    def _on_action_btn__clicked(self, btn, action):
        self._handle_action(action)

    def _on_refresh_btn__clicked(self, btn):
        self._manager.refresh_and_notify()
Exemple #43
0
    def download_plugin(self, plugin_name, channel=None):
        """Download a plugin from webservice

        :param plugin_name: the name of the plugin to download
        :param channel: the channel the plugin belongs
        :returns: a deferred
        """
        from stoqlib.lib.webservice import WebService

        default_store = get_default_store()
        existing_egg = default_store.find(PluginEgg,
                                          plugin_name=plugin_name).one()
        md5sum = existing_egg and existing_egg.egg_md5sum
        webapi = WebService()
        r = webapi.download_plugin(plugin_name, md5sum=md5sum, channel=channel)

        try:
            response = r.get_response()
        except Exception as e:
            return False, _("Failed to do the request: %s" % (e, ))

        code = response.status_code
        if code == 204:
            msg = _("No update needed. The plugin is already up to date.")
            log.info(msg)
            return True, msg

        if code != 200:
            return_messages = {
                400: _("Plugin not available for this stoq version"),
                401: _("The instance is not authorized to download the plugin"),
                404: _("Plugin does not exist"),
                405: _("This instance has not acquired the specified plugin"),
            }
            msg = return_messages.get(code, str(code))
            log.warning(msg)
            return False, msg

        try:
            with io.BytesIO() as f:
                f.write(response.content)
                with ZipFile(f) as egg:
                    if egg.testzip() is not None:
                        raise BadZipfile

                md5sum = hashlib.md5(f.getvalue()).hexdigest()
                with new_store() as store:
                    existing_egg = store.find(PluginEgg,
                                              plugin_name=plugin_name).one()
                    if existing_egg is not None:
                        existing_egg.egg_content = f.getvalue()
                        existing_egg.egg_md5sum = md5sum
                    else:
                        PluginEgg(
                            store=store,
                            plugin_name=plugin_name,
                            egg_md5sum=md5sum,
                            egg_content=f.getvalue(),
                        )
        except BadZipfile:
            return False, _("The downloaded plugin is corrupted")

        self._reload()
        return True, _("Plugin download successful")
Exemple #44
0
from stoqlib.gui.stockicons import (STOQ_FEEDBACK,
                                    STOQ_STATUS_NA,
                                    STOQ_STATUS_OK,
                                    STOQ_STATUS_WARNING,
                                    STOQ_STATUS_ERROR)
from stoqlib.lib.message import warning
from stoqlib.lib.translation import stoqlib_gettext as _
from stoqlib.lib.threadutils import terminate_thread
from stoq.lib.status import ResourceStatus, ResourceStatusManager


# FIXME: Improve those strings
_status_mapper = {
    None: (
        Gtk.STOCK_REFRESH,
        _("Checking status...")),
    ResourceStatus.STATUS_NA: (
        STOQ_STATUS_NA,
        _("Status not available")),
    ResourceStatus.STATUS_OK: (
        STOQ_STATUS_OK,
        _("Everything is running fine")),
    ResourceStatus.STATUS_WARNING: (
        STOQ_STATUS_WARNING,
        _("Some services are in a warning state")),
    ResourceStatus.STATUS_ERROR: (
        STOQ_STATUS_ERROR,
        _("Some services are in an error state")),
}

Exemple #45
0
    def create_actions(self):
        group = get_accels('app.purchase')
        actions = [
            # File
            ("OrderMenu", None, _("Order")),
            ("NewOrder", Gtk.STOCK_NEW, _("Order..."), group.get('new_order'),
             _("Create a new purchase order")),
            ("NewQuote", Gtk.STOCK_INDEX, _("Quote..."),
             group.get('new_quote'), _("Create a new purchase quote")),
            ("NewConsignment", None, _("Consignment..."),
             group.get('new_consignment'),
             _("Create a new purchase consignment")),
            ("NewProduct", None, _("Product..."), group.get('new_product'),
             _("Create a new product")),
            ('Reconciliation', None, _('Purchase reconciliation...'),
             group.get('new_reconciliation')),
            ("CloseInConsignment", None, _("Close consigment...")),
            ("ExportFilizola", None, _("Export Filizola File...")),

            # Edit
            ("StockCost", None, _("_Stock cost...")),

            # Search
            ("Categories", None, _("Categories..."),
             group.get("search_categories")),
            ("Products", None, _("Products..."), group.get("search_products")),
            ("ProductUnits", None, _("Product units..."),
             group.get("search_product_units")),
            ("ProductManufacturers", None, _("Manufacturers..."),
             group.get("search_product_manufacturers")),
            ("Services", None, _("Services..."), group.get("search_services")),
            ("SearchStockItems", None, _("Stock items..."),
             group.get("search_stock_items")),
            ("SearchClosedStockItems", None, _("Closed stock items..."),
             group.get("search_closed_stock_items")),
            ("Suppliers", None, _("Suppliers..."),
             group.get("search_suppliers")),
            ("Transporter", None, _("Transporters..."),
             group.get("search_transporters")),
            ("SearchQuotes", None, _("Quotes..."), group.get("search_quotes")),
            ("SearchPurchasedItems", None, _("Purchased items..."),
             group.get("search_purchased_items")),
            ("ProductsSoldSearch", None, _("Sold products..."),
             group.get("search_products_sold")),
            ("ProductsPriceSearch", None, _("Prices..."),
             group.get("search_prices")),
            ("SearchInConsignmentItems", None, _("Search consigment items..."),
             group.get("search_consignment_items")),

            # Order
            ("Confirm", Gtk.STOCK_APPLY, _("Confirm..."),
             group.get('order_confirm'),
             _("Confirm the selected order(s), marking it as sent to the "
               "supplier")),
            ("Cancel", Gtk.STOCK_CANCEL, _("Cancel..."),
             group.get('order_cancel'), _("Cancel the selected order")),
            ("Edit", Gtk.STOCK_EDIT, _("Edit..."), group.get('order_edit'),
             _("Edit the selected order, allowing you to change it's details")
             ),
            ("Details", Gtk.STOCK_INFO, _("Details..."),
             group.get('order_details'),
             _("Show details of the selected order")),
            ("Finish", Gtk.STOCK_APPLY, _("Finish..."),
             group.get('order_finish'),
             _('Complete the selected partially received order')),
        ]

        self.purchase_ui = self.add_ui_actions(actions)
        self.set_help_section(_("Purchase help"), 'app-purchase')
Exemple #46
0
def get_date_format():
    # Translators: This is the default date format in excel
    # columns, see the xlwt python library for more information
    return _('YY-MMM-D')
Exemple #47
0
 def on_ExportFilizola__activate(self, action):
     dest = generate_filizola_file(self.store)
     info(_('File saved in %s') % dest)
Exemple #48
0
class PurchaseApp(ShellApp):

    app_title = _('Purchase')
    gladefile = "purchase"
    search_spec = PurchaseOrderView
    search_label = _('matching:')
    report_table = PurchaseReport

    action_permissions = {
        'ProductUnits': ('ProductUnit', PermissionManager.PERM_SEARCH),
        'NewProduct': ('Product', PermissionManager.PERM_CREATE),
        'Products': ('Product', PermissionManager.PERM_SEARCH),
        'Services': ('Service', PermissionManager.PERM_SEARCH),
    }

    #
    # Application
    #

    def create_actions(self):
        group = get_accels('app.purchase')
        actions = [
            # File
            ("OrderMenu", None, _("Order")),
            ("NewOrder", Gtk.STOCK_NEW, _("Order..."), group.get('new_order'),
             _("Create a new purchase order")),
            ("NewQuote", Gtk.STOCK_INDEX, _("Quote..."),
             group.get('new_quote'), _("Create a new purchase quote")),
            ("NewConsignment", None, _("Consignment..."),
             group.get('new_consignment'),
             _("Create a new purchase consignment")),
            ("NewProduct", None, _("Product..."), group.get('new_product'),
             _("Create a new product")),
            ('Reconciliation', None, _('Purchase reconciliation...'),
             group.get('new_reconciliation')),
            ("CloseInConsignment", None, _("Close consigment...")),
            ("ExportFilizola", None, _("Export Filizola File...")),

            # Edit
            ("StockCost", None, _("_Stock cost...")),

            # Search
            ("Categories", None, _("Categories..."),
             group.get("search_categories")),
            ("Products", None, _("Products..."), group.get("search_products")),
            ("ProductUnits", None, _("Product units..."),
             group.get("search_product_units")),
            ("ProductManufacturers", None, _("Manufacturers..."),
             group.get("search_product_manufacturers")),
            ("Services", None, _("Services..."), group.get("search_services")),
            ("SearchStockItems", None, _("Stock items..."),
             group.get("search_stock_items")),
            ("SearchClosedStockItems", None, _("Closed stock items..."),
             group.get("search_closed_stock_items")),
            ("Suppliers", None, _("Suppliers..."),
             group.get("search_suppliers")),
            ("Transporter", None, _("Transporters..."),
             group.get("search_transporters")),
            ("SearchQuotes", None, _("Quotes..."), group.get("search_quotes")),
            ("SearchPurchasedItems", None, _("Purchased items..."),
             group.get("search_purchased_items")),
            ("ProductsSoldSearch", None, _("Sold products..."),
             group.get("search_products_sold")),
            ("ProductsPriceSearch", None, _("Prices..."),
             group.get("search_prices")),
            ("SearchInConsignmentItems", None, _("Search consigment items..."),
             group.get("search_consignment_items")),

            # Order
            ("Confirm", Gtk.STOCK_APPLY, _("Confirm..."),
             group.get('order_confirm'),
             _("Confirm the selected order(s), marking it as sent to the "
               "supplier")),
            ("Cancel", Gtk.STOCK_CANCEL, _("Cancel..."),
             group.get('order_cancel'), _("Cancel the selected order")),
            ("Edit", Gtk.STOCK_EDIT, _("Edit..."), group.get('order_edit'),
             _("Edit the selected order, allowing you to change it's details")
             ),
            ("Details", Gtk.STOCK_INFO, _("Details..."),
             group.get('order_details'),
             _("Show details of the selected order")),
            ("Finish", Gtk.STOCK_APPLY, _("Finish..."),
             group.get('order_finish'),
             _('Complete the selected partially received order')),
        ]

        self.purchase_ui = self.add_ui_actions(actions)
        self.set_help_section(_("Purchase help"), 'app-purchase')

    def create_ui(self):
        if api.sysparam.get_bool('SMART_LIST_LOADING'):
            self.search.enable_lazy_search()

        self.window.add_print_items()
        self.window.add_export_items([self.ExportFilizola])
        self.window.add_extra_items([self.CloseInConsignment, self.StockCost])

        self.window.add_new_items([
            self.NewOrder, self.NewQuote, self.NewProduct, self.NewConsignment,
            self.Reconciliation
        ])
        self.window.add_search_items([
            self.Products,
            self.Services,
            self.Categories,
            self.ProductUnits,
            self.ProductManufacturers,
            self.SearchStockItems,
            self.SearchClosedStockItems,
            self.Transporter,
            self.Suppliers,
            self.SearchQuotes,
            self.SearchPurchasedItems,
            self.ProductsSoldSearch,
            self.ProductsPriceSearch,
            self.SearchInConsignmentItems,
        ])
        self.search.set_summary_label(column='total',
                                      label=('<b>%s</b>' %
                                             api.escape(_('Orders total:'))),
                                      format='<b>%s</b>',
                                      parent=self.get_statusbar_message_area())
        self.results.set_selection_mode(Gtk.SelectionMode.MULTIPLE)
        self.set_sensitive([self.Confirm], False)

        self._inventory_widgets = [
            self.NewConsignment, self.CloseInConsignment
        ]
        self.register_sensitive_group(self._inventory_widgets,
                                      lambda: not self.has_open_inventory())

    def activate(self, refresh=True):
        if refresh:
            self.refresh()

        self._update_view()
        self.results.set_selection_mode(Gtk.SelectionMode.MULTIPLE)
        self.check_open_inventory()

        self.search.focus_search_entry()

    def get_domain_options(self):
        options = [
            ('fa-info-circle-symbolic', _('Details'), 'purchase.Details',
             True),
            ('fa-edit-symbolic', _('Edit'), 'purchase.Edit', True),
            ('fa-check-symbolic', _('Confirm'), 'purchase.Confirm', True),
            ('fa-ban-symbolic', _('Cancel'), 'purchase.Cancel', True),
            ('', _('Finish'), 'purchase.Finish', False),
        ]

        return options

    def search_completed(self, results, states):
        if len(results):
            return

        supplier, status = states[:2]
        if len(states) > 2 or (supplier.text == '' and status.value is None):
            self.search.set_message(
                "%s\n\n%s" %
                (_("No orders could be found."), _("Would you like to %s ?") %
                 ('<a href="new_order">%s</a>' %
                  (api.escape(_("create a new order"), )))))

        # FIXME: Push number of results to Statusbar

    def set_open_inventory(self):
        self.set_sensitive(self._inventory_widgets, False)

    def create_filters(self):
        self.set_text_field_columns(
            ['supplier_name', 'identifier_str', 'invoice_numbers'])
        self.status_filter = ComboSearchFilter(_('Show orders'),
                                               self._get_status_values())
        self.add_filter(self.status_filter, SearchFilterPosition.TOP,
                        ['status'])
        self.branch_filter = self.create_branch_filter(
            column=PurchaseOrderView.branch_id)

    def get_columns(self):
        return [
            IdentifierColumn('identifier', title=_('Purchase #')),
            SearchColumn('status_str',
                         title=_('Status'),
                         width=100,
                         data_type=str,
                         search_attribute='status',
                         valid_values=self._get_status_values(),
                         visible=False),
            SearchColumn('invoice_numbers',
                         title=_('Invoice'),
                         width=100,
                         data_type=str,
                         visible=False),
            SearchColumn('open_date',
                         title=_('Opened'),
                         long_title=_('Date Opened'),
                         width=90,
                         data_type=datetime.date,
                         sorted=True,
                         order=Gtk.SortType.DESCENDING),
            SearchColumn('receival_date',
                         title=_('Receival'),
                         long_title=_('Received date'),
                         width=90,
                         data_type=datetime.date,
                         order=Gtk.SortType.DESCENDING),
            SearchColumn('supplier_name',
                         title=_('Supplier'),
                         data_type=str,
                         searchable=True,
                         expand=True,
                         ellipsize=Pango.EllipsizeMode.END),
            SearchColumn('ordered_quantity',
                         title=_('Ordered'),
                         data_type=Decimal,
                         width=90,
                         format_func=format_quantity),
            SearchColumn('received_quantity',
                         title=_('Received'),
                         data_type=Decimal,
                         width=90,
                         format_func=format_quantity),
            SearchColumn('total',
                         title=_('Total'),
                         data_type=currency,
                         width=120)
        ]

    def print_report(self, *args, **kwargs):
        # PurchaseReport needs a status arg
        kwargs['status'] = self.status_filter.get_state().value
        super(PurchaseApp, self).print_report(*args, **kwargs)

    def search_for_date(self, date):
        dfilter = DateSearchFilter(_("Expected receival date"))
        dfilter.set_removable()
        dfilter.select(data=DateSearchFilter.Type.USER_DAY)
        self.add_filter(dfilter, columns=["expected_receival_date"])
        dfilter.start_date.set_date(date)
        self.refresh()

    #
    # Private
    #

    def _update_totals(self):
        self._update_view()

    def _update_list_aware_widgets(self, has_items):
        self.set_sensitive([self.Edit, self.Details], has_items)

    def _update_view(self):
        self._update_list_aware_widgets(len(self.results))
        selection = self.results.get_selected_rows()
        can_edit = one_selected = len(selection) == 1
        can_finish = False
        if selection:
            can_send_supplier = all(order.status == PurchaseOrder.ORDER_PENDING
                                    for order in selection)
            can_cancel = all(order_view.purchase.can_cancel()
                             for order_view in selection)
        else:
            can_send_supplier = False
            can_cancel = False

        if one_selected:
            can_edit = (selection[0].status == PurchaseOrder.ORDER_PENDING
                        or selection[0].status == PurchaseOrder.ORDER_QUOTING
                        or selection[0].status
                        == PurchaseOrder.ORDER_CONFIRMED)
            can_finish = (selection[0].status == PurchaseOrder.ORDER_CONFIRMED
                          and selection[0].received_quantity > 0)

        self.set_sensitive([self.Cancel], can_cancel)
        self.set_sensitive([self.Edit], can_edit)
        self.set_sensitive([self.Confirm], can_send_supplier)
        self.set_sensitive([self.Details], one_selected)
        self.set_sensitive([self.Finish], can_finish)

    def _new_order(self, order=None, edit_mode=False):
        with api.new_store() as store:
            order = store.fetch(order)
            self.run_dialog(PurchaseWizard, store, order, edit_mode)

        if store.committed:
            self.refresh()
            res = self.store.find(PurchaseOrderView, id=store.retval.id).one()
            self.select_result(res)

    def _edit_order(self):
        selected = self.results.get_selected_rows()
        qty = len(selected)
        if qty != 1:
            raise ValueError('You should have only one order selected, '
                             'got %d instead' % qty)
        purchase = selected[0].purchase
        if purchase.status == PurchaseOrder.ORDER_QUOTING:
            self._quote_order(purchase)
        else:
            self._new_order(purchase, edit_mode=False)

    def _run_details_dialog(self):
        order_views = self.results.get_selected_rows()
        qty = len(order_views)
        if qty != 1:
            raise ValueError('You should have only one order selected '
                             'at this point, got %d' % qty)
        self.run_dialog(PurchaseDetailsDialog,
                        self.store,
                        model=order_views[0].purchase)

    def _send_selected_items_to_supplier(self):
        orders = self.results.get_selected_rows()
        valid_order_views = [
            order for order in orders
            if order.status == PurchaseOrder.ORDER_PENDING
        ]

        if not valid_order_views:
            warning(_("There are no pending orders selected."))
            return

        msg = stoqlib_ngettext(
            _("The selected order will be marked as sent."),
            _("The %d selected orders will be marked as sent.") %
            len(valid_order_views), len(valid_order_views))
        confirm_label = stoqlib_ngettext(_("Confirm order"),
                                         _("Confirm orders"),
                                         len(valid_order_views))
        if not yesno(msg, Gtk.ResponseType.YES, confirm_label,
                     _("Don't confirm")):
            return

        with api.new_store() as store:
            for order_view in valid_order_views:
                order = store.fetch(order_view.purchase)
                order.confirm()
        self.refresh()
        self.select_result(orders)

    def _finish_order(self):
        order_views = self.results.get_selected_rows()
        qty = len(order_views)
        if qty != 1:
            raise ValueError('You should have only one order selected '
                             'at this point, got %d' % qty)

        with api.new_store() as store:
            order = store.fetch(order_views[0].purchase)
            self.run_dialog(PurchaseFinishWizard, store, order)

        self.refresh()
        self.select_result(order_views)

    def _cancel_order(self):
        order_views = self.results.get_selected_rows()
        assert all(ov.purchase.can_cancel() for ov in order_views)
        cancel_label = stoqlib_ngettext(_("Cancel order"), _("Cancel orders"),
                                        len(order_views))
        select_label = stoqlib_ngettext(
            _('The selected order will be cancelled.'),
            _('The selected orders will be cancelled.'), len(order_views))
        if not yesno(select_label, Gtk.ResponseType.YES, cancel_label,
                     _("Don't cancel")):
            return
        with api.new_store() as store:
            for order_view in order_views:
                order = store.fetch(order_view.purchase)
                order.cancel()
        self._update_totals()
        self.refresh()
        self.select_result(order_views)

    def _get_status_values(self):
        items = [(text, value)
                 for value, text in PurchaseOrder.statuses.items()]
        items.insert(0, (_('Any'), None))
        return items

    def _quote_order(self, quote=None):
        with api.new_store() as store:
            quote = store.fetch(quote)
            self.run_dialog(QuotePurchaseWizard, store, quote)

        if store.committed:
            self.refresh()
            res = self.store.find(PurchaseOrderView, id=store.retval.id).one()
            self.select_result(res)

    def _new_product(self):
        ProductCreateWizard.run_wizard(self)

    def _new_consignment(self):
        with api.new_store() as store:
            self.run_dialog(ConsignmentWizard, store, model=None)

        if store.committed:
            self.refresh()
            res = self.store.find(PurchaseOrderView, id=store.retval.id).one()
            self.select_result(res)

    #
    # Kiwi Callbacks
    #

    def on_results__row_activated(self, klist, purchase_order_view):
        self._run_details_dialog()

    def on_results__selection_changed(self, results, selected):
        self._update_view()

    def on_results__activate_link(self, results, uri):
        if uri == 'new_order':
            self._new_order()

    def _on_results__double_click(self, results, order):
        self._run_details_dialog()

    def _on_results__has_rows(self, results, has_items):
        self._update_list_aware_widgets(has_items)

    def on_Details__activate(self, action):
        self._run_details_dialog()

    def on_Edit__activate(self, action):
        self._edit_order()

    def on_Cancel__activate(self, action):
        self._cancel_order()

    def on_Confirm__activate(self, button):
        self._send_selected_items_to_supplier()

    def on_Finish__activate(self, action):
        self._finish_order()

    def on_ExportFilizola__activate(self, action):
        dest = generate_filizola_file(self.store)
        info(_('File saved in %s') % dest)

    # Order

    def on_StockCost__activate(self, action):
        self.run_dialog(StockCostDialog, self.store, None)

    # Consignment

    def on_CloseInConsignment__activate(self, action):
        with api.new_store() as store:
            self.run_dialog(CloseInConsignmentWizard, store)

    def on_SearchInConsignmentItems__activate(self, action):
        self.run_dialog(ConsignmentItemSearch, self.store)

    def on_Categories__activate(self, action):
        self.run_dialog(SellableCategorySearch, self.store)

    def on_SearchQuotes__activate(self, action):
        with api.new_store() as store:
            self.run_dialog(ReceiveQuoteWizard, store)
        self.refresh()

    def on_SearchPurchasedItems__activate(self, action):
        self.run_dialog(PurchasedItemsSearch, self.store)

    def on_SearchStockItems__activate(self, action):
        self.run_dialog(ProductStockSearch, self.store)

    def on_SearchClosedStockItems__activate(self, action):
        self.run_dialog(ProductClosedStockSearch, self.store)

    def on_Suppliers__activate(self, action):
        self.run_dialog(SupplierSearch, self.store, hide_footer=True)

    def on_Products__activate(self, action):
        self.run_dialog(ProductSearch, self.store)

    def on_ProductUnits__activate(self, action):
        self.run_dialog(SellableUnitSearch, self.store)

    def on_ProductManufacturers__activate(self, action):
        self.run_dialog(ProductManufacturerDialog, self.store)

    def on_Services__activate(self, action):
        self.run_dialog(ServiceSearch, self.store, hide_price_column=True)

    def on_Transporter__activate(self, action):
        self.run_dialog(TransporterSearch, self.store, hide_footer=True)

    def on_ProductsSoldSearch__activate(self, action):
        self.run_dialog(ProductsSoldSearch, self.store)

    def on_ProductsPriceSearch__activate(self, action):
        with api.new_store() as store:
            self.run_dialog(SellableMassEditorDialog, store)

    # Toolitem

    def on_NewOrder__activate(self, action):
        self._new_order()

    def on_NewQuote__activate(self, action):
        self._quote_order()

    def on_NewProduct__activate(self, action):
        self._new_product()

    def on_NewConsignment__activate(self, action):
        self._new_consignment()

    def on_Reconciliation__activate(self, button):
        with api.new_store() as store:
            self.run_dialog(PurchaseReconciliationWizard, store)
Exemple #49
0
 def get_columns(self):
     return [
         IdentifierColumn('identifier', title=_('Purchase #')),
         SearchColumn('status_str',
                      title=_('Status'),
                      width=100,
                      data_type=str,
                      search_attribute='status',
                      valid_values=self._get_status_values(),
                      visible=False),
         SearchColumn('invoice_numbers',
                      title=_('Invoice'),
                      width=100,
                      data_type=str,
                      visible=False),
         SearchColumn('open_date',
                      title=_('Opened'),
                      long_title=_('Date Opened'),
                      width=90,
                      data_type=datetime.date,
                      sorted=True,
                      order=Gtk.SortType.DESCENDING),
         SearchColumn('receival_date',
                      title=_('Receival'),
                      long_title=_('Received date'),
                      width=90,
                      data_type=datetime.date,
                      order=Gtk.SortType.DESCENDING),
         SearchColumn('supplier_name',
                      title=_('Supplier'),
                      data_type=str,
                      searchable=True,
                      expand=True,
                      ellipsize=Pango.EllipsizeMode.END),
         SearchColumn('ordered_quantity',
                      title=_('Ordered'),
                      data_type=Decimal,
                      width=90,
                      format_func=format_quantity),
         SearchColumn('received_quantity',
                      title=_('Received'),
                      data_type=Decimal,
                      width=90,
                      format_func=format_quantity),
         SearchColumn('total',
                      title=_('Total'),
                      data_type=currency,
                      width=120)
     ]
Exemple #50
0
 def _get_status_values(self):
     items = [(text, value)
              for value, text in PurchaseOrder.statuses.items()]
     items.insert(0, (_('Any'), None))
     return items
Exemple #51
0
    def get_columns(self):
        self._status_col = SearchColumn('status_name', title=_('Status'),
                                        data_type=str, width=80, visible=True,
                                        search_attribute='status',
                                        valid_values=self._get_status_values())

        cols = [IdentifierColumn('identifier', title=_('Sale #'),
                                 sorted=True),
                SearchColumn('coupon_id', title=_('Coupon #'), width=100,
                             data_type=int, visible=False),
                SearchColumn('paid', title=_('Paid'), width=120,
                             data_type=bool, visible=False),
                SearchColumn('open_date', title=_('Open date'), width=120,
                             data_type=date, justify=Gtk.Justification.RIGHT,
                             visible=False),
                SearchColumn('close_date', title=_('Close date'), width=120,
                             data_type=date, justify=Gtk.Justification.RIGHT,
                             visible=False),
                SearchColumn('confirm_date', title=_('Confirm date'),
                             data_type=date, justify=Gtk.Justification.RIGHT,
                             visible=False, width=120),
                SearchColumn('cancel_date', title=_('Cancel date'), width=120,
                             data_type=date, justify=Gtk.Justification.RIGHT,
                             visible=False),
                SearchColumn('return_date', title=_('Return date'), width=120,
                             data_type=date, justify=Gtk.Justification.RIGHT,
                             visible=False),
                SearchColumn('expire_date', title=_('Expire date'), width=120,
                             data_type=date, justify=Gtk.Justification.RIGHT,
                             visible=False),
                self._status_col]

        if api.sysparam.get_bool('USE_SALE_TOKEN'):
            cols.append(Column('token_str', title=_(u'Sale token'),
                               data_type=str, visible=True))

        cols.extend([
            SearchColumn('client_name', title=_('Client'),
                         data_type=str, width=140, expand=True,
                         ellipsize=Pango.EllipsizeMode.END),
            SearchColumn('client_fancy_name', title=_('Client fancy name'),
                         data_type=str, width=140, expand=True,
                         visible=False, ellipsize=Pango.EllipsizeMode.END),
            SearchColumn('salesperson_name', title=_('Salesperson'),
                         data_type=str, width=130,
                         ellipsize=Pango.EllipsizeMode.END),
            SearchColumn('total_quantity', title=_('Items'),
                         data_type=decimal.Decimal, width=60,
                         format_func=format_quantity),
            SearchColumn('total', title=_('Gross total'), data_type=currency,
                         width=120, search_attribute='_total'),
            SearchColumn('net_total', title=_('Net total'), data_type=currency,
                         width=120, search_attribute='_net_total')])
        return cols
Exemple #52
0
 def _get_status_values(self):
     items = [(value, key) for key, value in Sale.statuses.items()]
     items.insert(0, (_('Any'), None))
     return items
Exemple #53
0
class SalesApp(ShellApp):

    app_title = _('Sales')
    gladefile = 'sales_app'
    search_spec = SaleView
    search_label = _('matching:')
    report_table = SalesReport

    cols_info = {Sale.STATUS_INITIAL: 'open_date',
                 Sale.STATUS_CONFIRMED: 'confirm_date',
                 Sale.STATUS_ORDERED: 'open_date',
                 Sale.STATUS_CANCELLED: 'cancel_date',
                 Sale.STATUS_QUOTE: 'open_date',
                 Sale.STATUS_RETURNED: 'return_date',
                 Sale.STATUS_RENEGOTIATED: 'close_date'}

    action_permissions = {
        "SalesPrintInvoice": ('app.sales.print_invoice', PermissionManager.PERM_SEARCH),
    }

    def __init__(self, window, store=None):
        self.summary_label = None
        self._visible_date_col = None
        self._extra_summary = None
        ShellApp.__init__(self, window, store=store)
        self.search.connect('search-completed', self._on_search_completed)

    #
    # Application
    #

    def create_actions(self):
        group = get_accels('app.sales')
        actions = [
            # File
            ("SaleQuote", None, _("Sale quote..."), '',
             _('Create a new quote for a sale')),
            ("WorkOrderQuote", None, _("Sale with work order..."), '',
             _('Create a new quote for a sale with work orders')),
            ("LoanNew", None, _("Loan...")),
            ("LoanClose", None, _("Close loan...")),

            # Search
            ("SearchSoldItemsByBranch", None, _("Sold items by branch..."),
             group.get("search_sold_items_by_branch"),
             _("Search for sold items by branch")),
            ("SearchSalesByPaymentMethod", None,
             _("Sales by payment method..."),
             group.get("search_sales_by_payment")),
            ("SearchSalesPersonSales", None,
             _("Total sales made by salesperson..."),
             group.get("search_salesperson_sales"),
             _("Search for sales by payment method")),
            ("SearchProduct", None, _("Products..."),
             group.get("search_products"),
             _("Search for products")),
            ("SearchService", None, _("Services..."),
             group.get("search_services"),
             _("Search for services")),
            ("SearchDelivery", None, _("Deliveries..."),
             group.get("search_deliveries"),
             _("Search for deliveries")),
            ("SearchClient", None, _("Clients..."),
             group.get("search_clients"),
             _("Search for clients")),
            ("SearchClientCalls", None, _("Client Calls..."),
             group.get("search_client_calls"),
             _("Search for client calls")),
            ("SearchCreditCheckHistory", None,
             _("Client credit check history..."),
             group.get("search_credit_check_history"),
             _("Search for client check history")),
            ("SearchCommission", None, _("Commissions..."),
             group.get("search_commissions"),
             _("Search for salespersons commissions")),
            ("LoanSearch", None, _("Loans..."),
             group.get("search_loans")),
            ("LoanSearchItems", None, _("Loan items..."),
             group.get("search_loan_items")),
            ("ReturnedSaleSearch", None, _("Returned sales..."),
             group.get("returned_sales")),
            ("SearchUnconfirmedSaleItems", None, _("Unconfirmed sale items..."),
             group.get("search_reserved_product"),
             _("Search for unconfirmed sale items")),
            ("SearchClientsWithSale", None, _("Clients with sales..."),
             None,
             _("Search for regular clients")),
            ("SearchClientsWithCredit", None, _("Clients with credit..."),
             None,
             _("Search for clients that have credit")),
            ("SearchSoldItemsByClient", None, _("Sold items by client..."),
             None,
             _("Search for products sold by client")),


            # Sale
            ("SaleMenu", None, _("Sale")),

            ("SalesCancel", None, _("Cancel...")),
            ("ChangeClient", Gtk.STOCK_EDIT, _("Change client...")),
            ("ChangeSalesperson", Gtk.STOCK_EDIT, _("Change salesperson...")),
            ("SalesPrintInvoice", Gtk.STOCK_PRINT, _("_Print invoice...")),
            ("Return", Gtk.STOCK_CANCEL, _("Return..."), '',
             _("Return the selected sale, canceling it's payments")),
            ("Edit", Gtk.STOCK_EDIT, _("Edit..."), '',
             _("Edit the selected sale, allowing you to change the details "
               "of it")),
            ("Details", Gtk.STOCK_INFO, _("Details..."), '',
             _("Show details of the selected sale"))
        ]

        self.sales_ui = self.add_ui_actions(actions)
        self.set_help_section(_("Sales help"), 'app-sales')

    def get_domain_options(self):
        options = [
            ('fa-info-circle-symbolic', _('Details'), 'sales.Details', True),
            ('fa-edit-symbolic', _('Edit'), 'sales.Edit', True),
            ('fa-undo-symbolic', _('Return'), 'sales.Return', True),
            ('fa-ban-symbolic', _('Cancel'), 'sales.SalesCancel', True),
        ]

        if api.sysparam.get_bool('CHANGE_CLIENT_AFTER_CONFIRMED'):
            options.append(('', _('Change client'), 'sales.ChangeClient', False))
        if api.sysparam.get_bool('CHANGE_SALESPERSON_AFTER_CONFIRMED'):
            options.append(('', _('Change salesperson'), 'sales.ChangeSalespeson', False))

        return options

    def create_ui(self):
        if api.sysparam.get_bool('SMART_LIST_LOADING'):
            self.search.enable_lazy_search()

        self._setup_columns()
        self._setup_widgets()

        self.window.add_print_items()
        self.window.add_export_items()
        self.window.add_extra_items([self.LoanClose])
        self.window.add_new_items([self.SaleQuote, self.WorkOrderQuote,
                                   self.LoanNew])
        self.window.add_search_items(
            [self.SearchProduct, self.SearchService, self.SearchDelivery])
        self.window.add_search_items(
            [self.SearchClient, self.SearchClientCalls,
             self.SearchCreditCheckHistory, self.SearchClientsWithSale,
             self.SearchClientsWithCredit])
        self.window.add_search_items(
            [self.SearchSalesByPaymentMethod, self.ReturnedSaleSearch,
             self.SearchCommission, self.SearchSalesPersonSales])
        self.window.add_search_items(
            [self.SearchUnconfirmedSaleItems, self.SearchSoldItemsByBranch,
             self.SearchSoldItemsByClient])
        self.window.add_search_items(
            [self.LoanSearch, self.LoanSearchItems])

    def activate(self, refresh=True):
        if refresh:
            self.refresh()

        self.check_open_inventory()
        self._update_toolbar()

        self.search.focus_search_entry()

    def set_open_inventory(self):
        self.set_sensitive(self._inventory_widgets, False)

    def create_filters(self):
        self.set_text_field_columns(['client_name', 'salesperson_name',
                                     'identifier_str', 'token_code',
                                     'token_name'])

        status_filter = ComboSearchFilter(_('Show sales'),
                                          self._get_filter_options())
        status_filter.combo.set_row_separator_func(
            lambda model, titer: model[titer][0] == 'sep')

        executer = self.search.get_query_executer()
        executer.add_filter_query_callback(
            status_filter, self._get_status_query)
        self.add_filter(status_filter, position=SearchFilterPosition.TOP)

        self.create_branch_filter(column=Sale.branch_id)

    def get_columns(self):
        self._status_col = SearchColumn('status_name', title=_('Status'),
                                        data_type=str, width=80, visible=True,
                                        search_attribute='status',
                                        valid_values=self._get_status_values())

        cols = [IdentifierColumn('identifier', title=_('Sale #'),
                                 sorted=True),
                SearchColumn('coupon_id', title=_('Coupon #'), width=100,
                             data_type=int, visible=False),
                SearchColumn('paid', title=_('Paid'), width=120,
                             data_type=bool, visible=False),
                SearchColumn('open_date', title=_('Open date'), width=120,
                             data_type=date, justify=Gtk.Justification.RIGHT,
                             visible=False),
                SearchColumn('close_date', title=_('Close date'), width=120,
                             data_type=date, justify=Gtk.Justification.RIGHT,
                             visible=False),
                SearchColumn('confirm_date', title=_('Confirm date'),
                             data_type=date, justify=Gtk.Justification.RIGHT,
                             visible=False, width=120),
                SearchColumn('cancel_date', title=_('Cancel date'), width=120,
                             data_type=date, justify=Gtk.Justification.RIGHT,
                             visible=False),
                SearchColumn('return_date', title=_('Return date'), width=120,
                             data_type=date, justify=Gtk.Justification.RIGHT,
                             visible=False),
                SearchColumn('expire_date', title=_('Expire date'), width=120,
                             data_type=date, justify=Gtk.Justification.RIGHT,
                             visible=False),
                self._status_col]

        if api.sysparam.get_bool('USE_SALE_TOKEN'):
            cols.append(Column('token_str', title=_(u'Sale token'),
                               data_type=str, visible=True))

        cols.extend([
            SearchColumn('client_name', title=_('Client'),
                         data_type=str, width=140, expand=True,
                         ellipsize=Pango.EllipsizeMode.END),
            SearchColumn('client_fancy_name', title=_('Client fancy name'),
                         data_type=str, width=140, expand=True,
                         visible=False, ellipsize=Pango.EllipsizeMode.END),
            SearchColumn('salesperson_name', title=_('Salesperson'),
                         data_type=str, width=130,
                         ellipsize=Pango.EllipsizeMode.END),
            SearchColumn('total_quantity', title=_('Items'),
                         data_type=decimal.Decimal, width=60,
                         format_func=format_quantity),
            SearchColumn('total', title=_('Gross total'), data_type=currency,
                         width=120, search_attribute='_total'),
            SearchColumn('net_total', title=_('Net total'), data_type=currency,
                         width=120, search_attribute='_net_total')])
        return cols

    #
    # Private
    #

    def _create_summary_labels(self):
        parent = self.get_statusbar_message_area()
        self.search.set_summary_label(column='net_total',
                                      label=('<b>%s</b>' %
                                             api.escape(_('Gross total:'))),
                                      format='<b>%s</b>',
                                      parent=parent)
        # Add an extra summary beyond the main one
        # XXX: Should we modify the summary api to make it support more than one
        # summary value? This is kind of ugly
        if self._extra_summary:
            parent.remove(self._extra_summary)

        self._extra_summary = LazySummaryLabel(klist=self.search.result_view,
                                               column='net_total',
                                               label=('<b>%s</b>' %
                                                      api.escape(_('Net total:'))),
                                               value_format='<b>%s</b>')
        parent.pack_start(self._extra_summary, False, False, 0)
        self._extra_summary.show()

    def _setup_widgets(self):
        self._setup_slaves()
        self._inventory_widgets = [self.sale_toolbar.return_sale_button,
                                   self.Return, self.LoanNew, self.LoanClose]
        self.register_sensitive_group(self._inventory_widgets,
                                      lambda: not self.has_open_inventory())

    def _setup_slaves(self):
        # This is only here to reuse the logic in it.
        self.sale_toolbar = SaleListToolbar(self.store, self.results,
                                            parent=self)

    def _update_toolbar(self, *args):
        sale_view = self.results.get_selected()
        # FIXME: Disable invoice printing if the sale was returned. Remove this
        #       when we add proper support for returned sales invoice.
        can_print_invoice = bool(sale_view and
                                 sale_view.client_name is not None and
                                 sale_view.status != Sale.STATUS_RETURNED)
        self.set_sensitive([self.SalesPrintInvoice], can_print_invoice)
        self.set_sensitive([self.SalesCancel],
                           bool(sale_view and sale_view.can_cancel()))
        self.set_sensitive([self.sale_toolbar.return_sale_button, self.Return],
                           bool(sale_view and (sale_view.can_return() or
                                               sale_view.can_cancel())))
        self.set_sensitive([self.sale_toolbar.return_sale_button, self.Details],
                           bool(sale_view))
        self.set_sensitive([self.sale_toolbar.edit_button, self.Edit],
                           bool(sale_view and sale_view.can_edit()))
        # If the sale cannot be edit anymore, we only allow to change the client
        self.set_sensitive([self.ChangeClient],
                           bool(sale_view and not sale_view.can_edit()))
        self.set_sensitive([self.ChangeSalesperson],
                           bool(sale_view and not sale_view.can_edit()))
        self.sale_toolbar.set_report_filters(self.search.get_search_filters())

    def _print_invoice(self):
        sale_view = self.results.get_selected()
        assert sale_view
        sale = sale_view.sale
        station = api.get_current_station(self.store)
        printer = InvoicePrinter.get_by_station(station, self.store)
        if printer is None:
            info(_("There are no invoice printer configured for this station"))
            return
        assert printer.layout

        invoice = SaleInvoice(sale, printer.layout)
        if not invoice.has_invoice_number() or sale.invoice.invoice_number:
            print_sale_invoice(invoice, printer)
        else:
            store = api.new_store()
            retval = self.run_dialog(SaleInvoicePrinterDialog, store,
                                     store.fetch(sale), printer)
            store.confirm(retval)
            store.close()

    def _setup_columns(self, sale_status=Sale.STATUS_CONFIRMED):
        self._status_col.visible = False

        if sale_status is None:
            # When there is no filter for sale status, show the
            # 'date started' column by default
            sale_status = Sale.STATUS_INITIAL
            self._status_col.visible = True

        if self._visible_date_col:
            self._visible_date_col.visible = False

        col = self.search.get_column_by_attribute(self.cols_info[sale_status])
        if col is not None:
            self._visible_date_col = col
            col.visible = True

        self.results.set_columns(self.search.columns)
        # Adding summary label again and make it properly aligned with the
        # new columns setup
        self._create_summary_labels()

    def _get_status_values(self):
        items = [(value, key) for key, value in Sale.statuses.items()]
        items.insert(0, (_('Any'), None))
        return items

    def _get_filter_options(self):
        options = [
            (_('All Sales'), None),
            (_('Confirmed today'), FilterItem('custom', 'sold-today')),
            (_('Confirmed in the last 7 days'), FilterItem('custom', 'sold-7days')),
            (_('Confirmed in the last 28 days'), FilterItem('custom', 'sold-28days')),
            (_('Expired quotes'), FilterItem('custom', 'expired-quotes')),
            ('sep', None),
        ]

        for key, value in Sale.statuses.items():
            options.append((value, FilterItem('status', key)))
        return options

    def _get_status_query(self, state):
        if state.value is None:
            # FIXME; We cannot return None here, otherwise, the query will have
            # a 'AND NULL', which will return nothing.
            return True

        if state.value.name == 'custom':
            self._setup_columns(None)
            return SALES_FILTERS[state.value.value]

        elif state.value.name == 'status':
            self._setup_columns(state.value.value)
            return SaleView.status == state.value.value

        raise AssertionError(state.value.name, state.value.value)

    def _new_sale_quote(self, wizard):
        if self.check_open_inventory():
            warning(_("You cannot create a quote with an open inventory."))
            return

        store = api.new_store()
        model = self.run_dialog(wizard, store)
        store.confirm(model)
        store.close()

        if model:
            self.refresh()

    def _search_product(self):
        hide_cost_column = not api.sysparam.get_bool('SHOW_COST_COLUMN_IN_SALES')
        self.run_dialog(ProductSearch, self.store, hide_footer=True,
                        hide_toolbar=True, hide_cost_column=hide_cost_column)

    def _change_sale_client(self):
        sale_view = self.results.get_selected()
        with api.new_store() as store:
            sale = store.fetch(sale_view.sale)
            self.run_dialog(SaleClientEditor, store=store, model=sale)

        if store.committed:
            self.refresh()

    def _change_salesperson(self):
        sale_view = self.results.get_selected()
        with api.new_store() as store:
            sale = store.fetch(sale_view.sale)
            self.run_dialog(SalesPersonEditor, store=store, model=sale)

        if store.committed:
            self.refresh()

    #
    # Kiwi callbacks
    #

    def _on_sale_toolbar__sale_returned(self, toolbar, sale):
        self.refresh()

    def _on_sale_toolbar__sale_edited(self, toolbar, sale):
        self.refresh()

    def on_results__selection_changed(self, results, sale):
        self._update_toolbar()

    def on_results__has_rows(self, results, has_rows):
        self._update_toolbar()

    # Sales

    def on_SaleQuote__activate(self, action):
        self._new_sale_quote(wizard=SaleQuoteWizard)

    def on_WorkOrderQuote__activate(self, action):
        self._new_sale_quote(wizard=WorkOrderQuoteWizard)

    def on_SalesCancel__activate(self, action):
        sale_view = self.results.get_selected()
        # A plugin (e.g. ECF) can avoid the cancelation of a sale
        # because it wants it to be cancelled using another way
        if SaleAvoidCancelEvent.emit(sale_view.sale, Sale.STATUS_CANCELLED):
            return

        store = api.new_store()
        sale = store.fetch(sale_view.sale)
        msg_text = _(u"This will cancel the sale, Are you sure?")

        # nfce plugin cancellation event requires a minimum length for the
        # cancellation reason note. We can't set this in the plugin because it's
        # not possible to identify unically this NoteEditor.
        if get_plugin_manager().is_active('nfce'):
            note_min_length = 15
        else:
            note_min_length = 0

        retval = self.run_dialog(
            NoteEditor, store, model=None, message_text=msg_text,
            label_text=_(u"Reason"), mandatory=True,
            ok_button_label=_(u"Cancel sale"),
            cancel_button_label=_(u"Don't cancel"),
            min_length=note_min_length)

        if not retval:
            store.rollback()
            return

        # Try to cancel the sale with sefaz. Don't cancel the sale if sefaz
        # reject it.
        if StockOperationTryFiscalCancelEvent.emit(sale, retval.notes) is False:
            warning(_("The cancellation was not authorized by SEFAZ. You should "
                      "do a sale return."))
            return

        sale.cancel(retval.notes)
        store.commit(close=True)
        self.refresh()

    def on_ChangeClient__activate(self, action):
        self._change_sale_client()

    def on_ChangeSalesperson__activate(self, action):
        self._change_salesperson()

    def on_SalesPrintInvoice__activate(self, action):
        return self._print_invoice()

    # Loan

    def on_LoanNew__activate(self, action):
        if self.check_open_inventory():
            return
        store = api.new_store()
        model = self.run_dialog(NewLoanWizard, store)
        store.confirm(model)
        store.close()

    def on_LoanClose__activate(self, action):
        if self.check_open_inventory():
            return
        store = api.new_store()
        model = self.run_dialog(CloseLoanWizard, store)
        store.confirm(model)
        store.close()

    def on_LoanSearch__activate(self, action):
        self.run_dialog(LoanSearch, self.store)

    def on_LoanSearchItems__activate(self, action):
        self.run_dialog(LoanItemSearch, self.store)

    def on_ReturnedSaleSearch__activate(self, action):
        self.run_dialog(ReturnedSaleSearch, self.store)

    def on_SearchUnconfirmedSaleItems__activate(self, action):
        self.run_dialog(UnconfirmedSaleItemsSearch, self.store)

    # Search

    def on_SearchClient__activate(self, button):
        self.run_dialog(ClientSearch, self.store, hide_footer=True)

    def on_SearchProduct__activate(self, button):
        self._search_product()

    def on_SearchCommission__activate(self, button):
        self.run_dialog(CommissionSearch, self.store)

    def on_SearchClientCalls__activate(self, action):
        self.run_dialog(ClientCallsSearch, self.store)

    def on_SearchCreditCheckHistory__activate(self, action):
        self.run_dialog(CreditCheckHistorySearch, self.store)

    def on_SearchService__activate(self, button):
        self.run_dialog(ServiceSearch, self.store, hide_toolbar=True)

    def on_SearchSoldItemsByBranch__activate(self, button):
        self.run_dialog(SoldItemsByBranchSearch, self.store)

    def on_SearchSalesByPaymentMethod__activate(self, button):
        self.run_dialog(SalesByPaymentMethodSearch, self.store)

    def on_SearchDelivery__activate(self, action):
        self.run_dialog(DeliverySearch, self.store)

    def on_SearchSalesPersonSales__activate(self, action):
        self.run_dialog(SalesPersonSalesSearch, self.store)

    def on_SearchClientsWithSale__activate(self, action):
        self.run_dialog(ClientsWithSaleSearch, self.store)

    def on_SearchClientsWithCredit__activate(self, action):
        self.run_dialog(ClientsWithCreditSearch, self.store)

    def on_SearchSoldItemsByClient__activate(self, action):
        self.run_dialog(SoldItemsByClientSearch, self.store)

    # Toolbar

    def on_Edit__activate(self, action):
        self.sale_toolbar.edit()

    def on_Details__activate(self, action):
        self.sale_toolbar.show_details()

    def on_Return__activate(self, action):
        if self.check_open_inventory():
            return
        self.sale_toolbar.return_sale()

    # Sale toobar

    def on_sale_toolbar__sale_edited(self, widget, sale):
        self.refresh()

    def on_sale_toolbar__sale_returned(self, widget, sale):
        self.refresh()

    # Search

    def _on_search_completed(self, *args):
        if not (self.search.result_view.lazy_search_enabled()
                and len(self.search.result_view)):
            return

        post = self.search.result_view.get_model().get_post_data()
        if post is not None:
            self._extra_summary.update_total(post.net_sum)
Exemple #54
0
    def create_actions(self):
        group = get_accels('app.sales')
        actions = [
            # File
            ("SaleQuote", None, _("Sale quote..."), '',
             _('Create a new quote for a sale')),
            ("WorkOrderQuote", None, _("Sale with work order..."), '',
             _('Create a new quote for a sale with work orders')),
            ("LoanNew", None, _("Loan...")),
            ("LoanClose", None, _("Close loan...")),

            # Search
            ("SearchSoldItemsByBranch", None, _("Sold items by branch..."),
             group.get("search_sold_items_by_branch"),
             _("Search for sold items by branch")),
            ("SearchSalesByPaymentMethod", None,
             _("Sales by payment method..."),
             group.get("search_sales_by_payment")),
            ("SearchSalesPersonSales", None,
             _("Total sales made by salesperson..."),
             group.get("search_salesperson_sales"),
             _("Search for sales by payment method")),
            ("SearchProduct", None, _("Products..."),
             group.get("search_products"),
             _("Search for products")),
            ("SearchService", None, _("Services..."),
             group.get("search_services"),
             _("Search for services")),
            ("SearchDelivery", None, _("Deliveries..."),
             group.get("search_deliveries"),
             _("Search for deliveries")),
            ("SearchClient", None, _("Clients..."),
             group.get("search_clients"),
             _("Search for clients")),
            ("SearchClientCalls", None, _("Client Calls..."),
             group.get("search_client_calls"),
             _("Search for client calls")),
            ("SearchCreditCheckHistory", None,
             _("Client credit check history..."),
             group.get("search_credit_check_history"),
             _("Search for client check history")),
            ("SearchCommission", None, _("Commissions..."),
             group.get("search_commissions"),
             _("Search for salespersons commissions")),
            ("LoanSearch", None, _("Loans..."),
             group.get("search_loans")),
            ("LoanSearchItems", None, _("Loan items..."),
             group.get("search_loan_items")),
            ("ReturnedSaleSearch", None, _("Returned sales..."),
             group.get("returned_sales")),
            ("SearchUnconfirmedSaleItems", None, _("Unconfirmed sale items..."),
             group.get("search_reserved_product"),
             _("Search for unconfirmed sale items")),
            ("SearchClientsWithSale", None, _("Clients with sales..."),
             None,
             _("Search for regular clients")),
            ("SearchClientsWithCredit", None, _("Clients with credit..."),
             None,
             _("Search for clients that have credit")),
            ("SearchSoldItemsByClient", None, _("Sold items by client..."),
             None,
             _("Search for products sold by client")),


            # Sale
            ("SaleMenu", None, _("Sale")),

            ("SalesCancel", None, _("Cancel...")),
            ("ChangeClient", Gtk.STOCK_EDIT, _("Change client...")),
            ("ChangeSalesperson", Gtk.STOCK_EDIT, _("Change salesperson...")),
            ("SalesPrintInvoice", Gtk.STOCK_PRINT, _("_Print invoice...")),
            ("Return", Gtk.STOCK_CANCEL, _("Return..."), '',
             _("Return the selected sale, canceling it's payments")),
            ("Edit", Gtk.STOCK_EDIT, _("Edit..."), '',
             _("Edit the selected sale, allowing you to change the details "
               "of it")),
            ("Details", Gtk.STOCK_INFO, _("Details..."), '',
             _("Show details of the selected sale"))
        ]

        self.sales_ui = self.add_ui_actions(actions)
        self.set_help_section(_("Sales help"), 'app-sales')
Exemple #55
0
class CalendarApp(ShellApp):

    app_title = _('Calendar')
    gladefile = 'calendar'

    def __init__(self, window, store=None):
        # Create this here because CalendarView will update it.
        # It will only be shown on create_ui though
        self.date_label = gtk.Label('')
        self._calendar = CalendarView(self)
        ShellApp.__init__(self, window, store=store)
        threadit(self._setup_daemon)

    def _setup_daemon(self):
        daemon = start_daemon()
        assert daemon.running
        self._calendar.set_daemon_uri(daemon.server_uri)
        schedule_in_main_thread(self._calendar.load)

    #
    # ShellApp overrides
    #

    def create_actions(self):
        group = get_accels('app.calendar')
        actions = [
            # File
            ('NewClientCall', None, _("Client call"),
             group.get('new_client_call'), _("Add a new client call")),
            ('NewPayable', None, _("Account payable"),
             group.get('new_payable'), _("Add a new account payable")),
            ('NewReceivable', None, _("Account receivable"),
             group.get('new_receivable'), _("Add a new account receivable")),
            ('NewWorkOrder', None, _("Work order"),
             group.get('new_work_order'), _("Add a new work order")),
            # View
            ('Back', gtk.STOCK_GO_BACK, _("Back"), group.get('go_back'),
             _("Go back")),
            ('Forward', gtk.STOCK_GO_FORWARD, _("Forward"),
             group.get('go_forward'), _("Go forward")),
            ('Today', STOQ_CALENDAR_TODAY, _("Show today"),
             group.get('show_today'), _("Show today")),
            ('CalendarEvents', None, _("Calendar events")),
            ('CurrentView', None, _("Display view as")),
        ]
        self.calendar_ui = self.add_ui_actions('',
                                               actions,
                                               filename='calendar.xml')
        self.set_help_section(_("Calendar help"), 'app-calendar')

        toggle_actions = [
            ('AccountsPayableEvents', None, _("Accounts payable"), None,
             _("Show accounts payable in the list")),
            ('AccountsReceivableEvents', None, _("Accounts receivable"), None,
             _("Show accounts receivable in the list")),
            ('PurchaseEvents', None, _("Purchases"), None,
             _("Show purchases in the list")),
            ('ClientCallEvents', None, _("Client Calls"), None,
             _("Show client calls in the list")),
            ('ClientBirthdaysEvents', None, _("Client Birthdays"), None,
             _("Show client birthdays in the list")),
            ('WorkOrderEvents', None, _("Work orders"), None,
             _("Show work orders in the list")),
        ]
        self.add_ui_actions('', toggle_actions, 'ToggleActions', 'toggle')

        events_info = dict(
            in_payments=(self.AccountsReceivableEvents, self.NewReceivable,
                         u'receivable'),
            out_payments=(self.AccountsPayableEvents, self.NewPayable,
                          u'payable'),
            purchase_orders=(self.PurchaseEvents, None, u'stock'),
            client_calls=(self.ClientCallEvents, self.NewClientCall, u'sales'),
            client_birthdays=(self.ClientBirthdaysEvents, None, u'sales'),
            work_orders=(self.WorkOrderEvents, self.NewWorkOrder, u'services'),
        )

        user = api.get_current_user(self.store)
        events = self._calendar.get_events()
        for event_name, value in events_info.items():
            view_action, new_action, app = value
            view_action.props.active = events[event_name]
            # Disable feature if user does not have acces to required
            # application
            if not user.profile.check_app_permission(app):
                view_action.props.active = False
                view_action.set_sensitive(False)
                if new_action:
                    new_action.set_sensitive(False)

            view_action.connect('notify::active', self._update_events)
        self._update_events()

        radio_actions = [
            ('ViewMonth', STOQ_CALENDAR_MONTH, _("View as month"), '',
             _("Show one month")),
            ('ViewWeek', STOQ_CALENDAR_WEEK, _("View as week"), '',
             _("Show one week")),
            ('ViewDay', STOQ_CALENDAR_LIST, _("View as day"), '',
             _("Show one day")),
        ]
        self.add_ui_actions('', radio_actions, 'RadioActions', 'radio')
        self.ViewMonth.set_short_label(_("Month"))
        self.ViewWeek.set_short_label(_("Week"))
        self.ViewDay.set_short_label(_("Day"))
        self.ViewMonth.props.is_important = True
        self.ViewWeek.props.is_important = True
        self.ViewDay.props.is_important = True

        view = api.user_settings.get('calendar-view', 'month')
        if view == 'month':
            self.ViewMonth.props.active = True
        elif view == 'basicWeek':
            self.ViewWeek.props.active = True
        else:
            self.ViewDay.props.active = True

    def create_ui(self):
        self.window.add_new_items(
            [self.NewClientCall, self.NewPayable, self.NewReceivable])

        # Reparent the toolbar, to show the date next to it.
        self.hbox = gtk.HBox()
        toolbar = self.uimanager.get_widget('/toolbar')
        toolbar.reparent(self.hbox)

        # A label to show the current calendar date.
        self.date_label.show()
        self.hbox.pack_start(self.date_label, False, False, 6)
        self.hbox.show()

        self.main_vbox.pack_start(self.hbox, False, False)

        self.main_vbox.pack_start(self._calendar)
        self._calendar.show()
        self.window.Print.set_tooltip(_("Print this calendar"))

    def activate(self, refresh=True):
        self.window.SearchToolItem.set_sensitive(False)
        # FIXME: Are we 100% sure we can always print something?
        # self.window.Print.set_sensitive(True)

    def deactivate(self):
        # Put the toolbar back at where it was
        main_vbox = self.window.main_vbox
        toolbar = self.uimanager.get_widget('/toolbar')
        self.hbox.remove(toolbar)
        main_vbox.pack_start(toolbar, False, False)
        main_vbox.reorder_child(toolbar, 1)

        self.uimanager.remove_ui(self.calendar_ui)
        self.window.SearchToolItem.set_sensitive(True)

    # Private

    def _update_events(self, *args):
        self._calendar.update_events(
            out_payments=self.AccountsPayableEvents.get_active(),
            in_payments=self.AccountsReceivableEvents.get_active(),
            purchase_orders=self.PurchaseEvents.get_active(),
            client_calls=self.ClientCallEvents.get_active(),
            client_birthdays=self.ClientBirthdaysEvents.get_active(),
            work_orders=self.WorkOrderEvents.get_active(),
        )

    def _new_client_call(self):
        with api.new_store() as store:
            self.run_dialog(CallsEditor, store, None, None, Client)

        if store.committed:
            self._update_events()

    def _new_work_order(self):
        with api.new_store() as store:
            self.run_dialog(WorkOrderEditor, store)

        if store.committed:
            self._update_events()

    def _new_payment(self, editor):
        with api.new_store() as store:
            self.run_dialog(editor, store)

        if store.committed:
            self._update_events()

    #
    # Kiwi callbacks
    #

    # Toolbar

    def new_activate(self):
        if not self.NewClientCall.get_sensitive():
            return
        self._new_client_call()

    def print_activate(self):
        self._calendar.print_()

    def export_spreadsheet_activate(self):
        pass

    def on_NewClientCall__activate(self, action):
        self._new_client_call()

    def on_NewPayable__activate(self, action):
        self._new_payment(OutPaymentEditor)

    def on_NewReceivable__activate(self, action):
        self._new_payment(InPaymentEditor)

    def on_NewWorkOrder__activate(self, action):
        self._new_work_order()

    def on_Back__activate(self, action):
        self._calendar.go_prev()

    def on_Today__activate(self, action):
        self._calendar.show_today()

    def on_Forward__activate(self, action):
        self._calendar.go_next()

    def on_ViewMonth__activate(self, action):
        self._calendar.change_view('month')

    def on_ViewWeek__activate(self, action):
        self._calendar.change_view('basicWeek')

    def on_ViewDay__activate(self, action):
        self._calendar.change_view('basicDay')
Exemple #56
0
class ResourceStatus(GObject.GObject):
    """The status of a given resource"""

    gsignal('status-changed', int, str)

    (STATUS_NA,
     STATUS_OK,
     STATUS_WARNING,
     STATUS_ERROR) = range(4)

    status_label = {
        STATUS_NA: _("N/A"),
        STATUS_OK: _("OK"),
        STATUS_WARNING: _("WARNING"),
        STATUS_ERROR: _("ERROR"),
    }

    name = None
    label = None
    priority = 0
    refresh_timeout = int(os.environ.get('STOQ_STATUS_REFRESH_TIMEOUT', 60))

    def __init__(self):
        super(ResourceStatus, self).__init__()

        assert self.name is not None
        self.status = self.STATUS_NA
        self.reason = None
        self.reason_long = None
        GLib.timeout_add_seconds(self.refresh_timeout, self.refresh_and_notify)

        # Schedule first update for right after now. Dont call refresh_and_notify()
        # directly as it will block the interface briefly
        GLib.timeout_add_seconds(1, lambda: self.refresh_and_notify() and False)

    __hash__ = GObject.GObject.__hash__

    def __eq__(self, other):
        if type(self) != type(other):
            return False
        return self.name == other.name

    @property
    def status_str(self):
        return self.status_label[self.status]

    def refresh(self):
        """Refresh the resource status

        Subclasses should override this and update
        :obj:`.status` and :obj:`.reason`

        Note that this will not be running on the main thread,
        so be cautelous with non thread-safe operations.
        """
        raise NotImplementedError

    def get_actions(self):
        """Get the actions that can be run for this resource"""
        return []

    def refresh_and_notify(self):
        """Refresh the resource status and notify for changes"""
        old_status, old_reason = self.status, self.reason
        self.refresh()

        if (self.status, self.reason) != (old_status, old_reason):
            self.emit('status-changed', self.status, self.reason)
Exemple #57
0
    def create_actions(self):
        group = get_accels('app.calendar')
        actions = [
            # File
            ('NewClientCall', None, _("Client call"),
             group.get('new_client_call'), _("Add a new client call")),
            ('NewPayable', None, _("Account payable"),
             group.get('new_payable'), _("Add a new account payable")),
            ('NewReceivable', None, _("Account receivable"),
             group.get('new_receivable'), _("Add a new account receivable")),
            ('NewWorkOrder', None, _("Work order"),
             group.get('new_work_order'), _("Add a new work order")),
            # View
            ('Back', gtk.STOCK_GO_BACK, _("Back"), group.get('go_back'),
             _("Go back")),
            ('Forward', gtk.STOCK_GO_FORWARD, _("Forward"),
             group.get('go_forward'), _("Go forward")),
            ('Today', STOQ_CALENDAR_TODAY, _("Show today"),
             group.get('show_today'), _("Show today")),
            ('CalendarEvents', None, _("Calendar events")),
            ('CurrentView', None, _("Display view as")),
        ]
        self.calendar_ui = self.add_ui_actions('',
                                               actions,
                                               filename='calendar.xml')
        self.set_help_section(_("Calendar help"), 'app-calendar')

        toggle_actions = [
            ('AccountsPayableEvents', None, _("Accounts payable"), None,
             _("Show accounts payable in the list")),
            ('AccountsReceivableEvents', None, _("Accounts receivable"), None,
             _("Show accounts receivable in the list")),
            ('PurchaseEvents', None, _("Purchases"), None,
             _("Show purchases in the list")),
            ('ClientCallEvents', None, _("Client Calls"), None,
             _("Show client calls in the list")),
            ('ClientBirthdaysEvents', None, _("Client Birthdays"), None,
             _("Show client birthdays in the list")),
            ('WorkOrderEvents', None, _("Work orders"), None,
             _("Show work orders in the list")),
        ]
        self.add_ui_actions('', toggle_actions, 'ToggleActions', 'toggle')

        events_info = dict(
            in_payments=(self.AccountsReceivableEvents, self.NewReceivable,
                         u'receivable'),
            out_payments=(self.AccountsPayableEvents, self.NewPayable,
                          u'payable'),
            purchase_orders=(self.PurchaseEvents, None, u'stock'),
            client_calls=(self.ClientCallEvents, self.NewClientCall, u'sales'),
            client_birthdays=(self.ClientBirthdaysEvents, None, u'sales'),
            work_orders=(self.WorkOrderEvents, self.NewWorkOrder, u'services'),
        )

        user = api.get_current_user(self.store)
        events = self._calendar.get_events()
        for event_name, value in events_info.items():
            view_action, new_action, app = value
            view_action.props.active = events[event_name]
            # Disable feature if user does not have acces to required
            # application
            if not user.profile.check_app_permission(app):
                view_action.props.active = False
                view_action.set_sensitive(False)
                if new_action:
                    new_action.set_sensitive(False)

            view_action.connect('notify::active', self._update_events)
        self._update_events()

        radio_actions = [
            ('ViewMonth', STOQ_CALENDAR_MONTH, _("View as month"), '',
             _("Show one month")),
            ('ViewWeek', STOQ_CALENDAR_WEEK, _("View as week"), '',
             _("Show one week")),
            ('ViewDay', STOQ_CALENDAR_LIST, _("View as day"), '',
             _("Show one day")),
        ]
        self.add_ui_actions('', radio_actions, 'RadioActions', 'radio')
        self.ViewMonth.set_short_label(_("Month"))
        self.ViewWeek.set_short_label(_("Week"))
        self.ViewDay.set_short_label(_("Day"))
        self.ViewMonth.props.is_important = True
        self.ViewWeek.props.is_important = True
        self.ViewDay.props.is_important = True

        view = api.user_settings.get('calendar-view', 'month')
        if view == 'month':
            self.ViewMonth.props.active = True
        elif view == 'basicWeek':
            self.ViewWeek.props.active = True
        else:
            self.ViewDay.props.active = True
Exemple #58
0
    def _startup(self):
        options = {}
        options['monthNames'] = dateutils.get_month_names()
        options['monthNamesShort'] = dateutils.get_short_month_names()
        options['dayNames'] = dateutils.get_day_names()
        options['dayNamesShort'] = dateutils.get_short_day_names()
        options['buttonText'] = {
            "today": _('today'),
            "month": _('month'),
            "week": _('week'),
            "day": _('day')
        }
        options['defaultView'] = api.user_settings.get('calendar-view',
                                                       'month')

        # FIXME: This should not be tied to the language, rather be
        #        picked up from libc, but it's a bit of work to translate
        #        one into another so just take a shortcut
        options['columnFormat'] = {
            # month column format, eg "Mon", see:
            # http://arshaw.com/fullcalendar/docs/text/columnFormat/
            'month': _('ddd'),
            # week column format: eg, "Mon 9/7", see:
            # http://arshaw.com/fullcalendar/docs/text/columnFormat/
            'week': _('ddd M/d'),
            # day column format : eg "Monday 9/7", see:
            # http://arshaw.com/fullcalendar/docs/text/columnFormat/
            'day': _('dddd M/d'),
        }

        options['timeFormat'] = {
            # for agendaWeek and agendaDay, eg "5:00 - 6:30", see:
            # http://arshaw.com/fullcalendar/docs/text/timeFormat/
            'agenda': _('h:mm{ - h:mm}'),
            # for all other views, eg "7p", see:
            # http://arshaw.com/fullcalendar/docs/text/timeFormat/
            '': _('h(:mm)t'),
        }

        options['titleFormat'] = {
            # month title, eg "September 2009", see:
            # http://arshaw.com/fullcalendar/docs/text/titleFormat/
            'month': _('MMMM yyyy'),
            # week title, eg "Sep 7 - 13 2009" see:
            # http://arshaw.com/fullcalendar/docs/text/titleFormat/
            'week': _("MMM d[ yyyy]{ '&#8212;'[ MMM] d yyyy}"),
            # day time, eg "Tuesday, Sep 8, 2009" see:
            # http://arshaw.com/fullcalendar/docs/text/titleFormat/
            'day': _('dddd, MMM d, yyyy'),
        }

        if get_weekday_start() == MO:
            firstday = 1
        else:
            firstday = 0

        options['firstDay'] = firstday
        options['isRTL'] = (
            gtk.widget_get_default_direction() == gtk.TEXT_DIR_RTL)
        options['data'] = self._show_events
        options['loading_msg'] = _('Loading calendar content, please wait...')
        self.js_function_call('startup', options)
        self._update_title()
Exemple #59
0
    def _generate_dailymovement_data(self, store):
        query = And(Payment.status.is_in([Payment.STATUS_PENDING,
                                          Payment.STATUS_PAID]),
                    self._get_query(Payment.open_date, Payment.branch))

        # Keys are the sale objects, and values are lists with all payments
        self.sales = collections.OrderedDict()

        # Keys are the returned sale objects, and values are lists with all payments
        self.return_sales = collections.OrderedDict()
        self.purchases = collections.OrderedDict()

        # lonely input and output payments
        self.lonely_in_payments = []
        self.lonely_out_payments = []

        # values are lists with the first element the summary of the input, and
        # the second the summary of the output
        method_summary = {}
        self.card_summary = {}

        result = store.find(DailyInPaymentView, query)
        for p in result.order_by(Sale.identifier, Payment.identifier):
            if p.sale:
                subtotal = p.sale_subtotal
                total = p.sale.get_total_sale_amount(subtotal)
                salesperson = p.salesperson_name or _('Not Specified')
                client = p.client_name or _('Not Specified')
                sale = DailyMovementSale(identifier=p.sale.identifier,
                                         salesperson=salesperson,
                                         client=client,
                                         branch=p.branch_name,
                                         value=get_formatted_price(total))
                sale_payments = self.sales.setdefault(sale, {})
                details = ''
                method_desc = p.method.get_description()
                if p.check_data:
                    account = p.check_data.bank_account
                    numbers = [payment.payment_number for payment in p.sale.payments
                               if bool(payment.payment_number)]
                    # Ensure that the check numbers are ordered
                    numbers.sort()
                    parts = []
                    if account.bank_number:
                        parts.append(_(u'Bank: %s') % account.bank_number)
                    if account.bank_branch:
                        parts.append(_(u'Agency: %s') % account.bank_branch)
                    if account.bank_account:
                        parts.append(_(u'Account: %s') % account.bank_account)
                    if numbers:
                        parts.append(_(u'Numbers: %s') % ', '.join(numbers))
                    details = ' / '.join(parts)

                if p.card_data:
                    if p.card_data.card_type == CreditCardData.TYPE_DEBIT:
                        method_desc += ' ' + _('Debit')
                    else:
                        method_desc += ' ' + _(u'Credit')
                    details = '%s - %s - %s' % (p.card_data.auth,
                                                p.card_data.provider.short_name or '',
                                                p.card_data.device.description or '')

                key = (method_desc, details)
                item = sale_payments.setdefault(key, [0, 0])
                item[0] += p.value
                item[1] += 1

            else:
                self.lonely_in_payments.append(p)

            method_summary.setdefault(p.method, [0, 0])
            method_summary[p.method][0] += p.value
            if p.card_data:
                type_desc = p.card_data.short_desc[p.card_data.card_type]
                key = (p.card_data.provider.short_name, type_desc)
                self.card_summary.setdefault(key, 0)
                self.card_summary[key] += p.value

        result = store.find(DailyOutPaymentView, query)
        for p in result.order_by(Payment.identifier):
            if p.purchase:
                purchase_payments = self.purchases.setdefault(p.purchase, [])
                purchase_payments.append(p)
            elif p.sale:
                subtotal = p.sale_subtotal
                value = p.sale.get_total_sale_amount(subtotal)
                salesperson = p.salesperson_name or _('Not Specified')
                client = p.client_name or _('Not Specified')
                sale = DailyMovementSale(identifier=p.sale.identifier,
                                         salesperson=salesperson,
                                         client=client,
                                         return_date=p.sale.return_date,
                                         branch=p.branch_name,
                                         value=value)
                return_sales_payment = self.return_sales.setdefault(sale, [])
                return_sales_payment.append(p)
            else:
                self.lonely_out_payments.append(p)

            method_summary.setdefault(p.method, [0, 0])
            method_summary[p.method][1] += p.value

        self.method_summary = []
        for method, (in_value, out_value) in method_summary.items():
            self.method_summary.append((method,
                                        in_value,
                                        out_value))
        self.method_summary.sort(key=lambda m: _(m[0].description))

        # Till removals
        query = And(Eq(TillEntry.payment_id, None),
                    self._get_query(TillEntry.date, TillEntry.branch),
                    TillEntry.value < 0)
        self.till_removals = store.find(TillEntry, query)

        # Till supply
        query = And(Eq(TillEntry.payment_id, None),
                    self._get_query(TillEntry.date, TillEntry.branch),
                    TillEntry.value > 0)
        self.till_supplies = store.find(TillEntry, query)
Exemple #60
0
class _BackupStatus(ResourceStatus):

    name = "backup"
    label = _("Backup")
    priority = 98

    def __init__(self):
        ResourceStatus.__init__(self)
        self._webservice = WebService()
        self._server = ServerProxy()

    @threaded
    def _get_key(self):
        return self._server.call('get_backup_key')

    @threaded
    def _get_server_status(self):
        request = self._webservice.status()
        return request.get_response()

    def refresh(self):
        if stoq.trial_mode:
            self.status = ResourceStatus.STATUS_NA
            self.reason = (_('Online features are not available in trial mode'))
            self.reason_long = _('Online features require a subscription of Stoq.link')
            return

        if not api.sysparam.get_bool('ONLINE_SERVICES'):
            self.status = ResourceStatus.STATUS_NA
            self.reason = _('Backup service not running because '
                            '"Online Services" is disabled')
            self.reason_long = _('Enable the parameter "Online Services" '
                                 'on the "Admin" app to solve this issue')
            return

        try:
            key = self._get_key()
        except ServerError:
            pass
        else:
            if not key:
                self.status = self.STATUS_WARNING
                self.reason = _("Backup key not configured")
                self.reason_long = _('Click on "Configure" button to '
                                     'configure the backup key')
                return

        try:
            response = self._get_server_status()
        except Exception as e:
            self.status = self.STATUS_WARNING
            self.reason = _("Could not communicate with Stoq.link")
            self.reason_long = str(e)
            return

        if response.status_code != 200:
            self.status = self.STATUS_WARNING
            self.reason = _("Could not communicate with Stoq.link")
            self.reason_long = None
            return

        data = response.json()
        if data['latest_backup_date']:
            backup_date = dateutil.parser.parse(data['latest_backup_date'])
            delta = datetime.datetime.today() - backup_date

            if delta.days > 3:
                self.status = self.STATUS_WARNING
                self.reason = _("Backup is late. Last backup date is %s") % (
                    backup_date.strftime('%x'))
                self.reason_long = _("Check your Stoq Server logs to see if "
                                     "there's any problem with it")
            else:
                self.status = self.STATUS_OK
                self.reason = _("Backup is up-to-date. Last backup date is %s") % (
                    backup_date.strftime('%x'))
                self.reason_long = None
        else:
            self.status = self.STATUS_WARNING
            self.reason = _("There's no backup data yet")
            self.reason_long = None

    def get_actions(self):
        if self.status != ResourceStatus.STATUS_NA:
            yield ResourceStatusAction(
                self, 'backup-now',
                _("Backup now"), self._on_backup_now, threaded=True)
            yield ResourceStatusAction(
                self, 'configure',
                _("Configure"), self._on_configure, threaded=False)

    def _on_configure(self):
        key = self._server.call('get_backup_key')

        with api.new_store() as store:
            rv = run_dialog(BackupSettingsEditor, None,
                            store, Settable(key=key))

        if rv:
            key = self._server.call('set_backup_key', rv.key)

    def _on_backup_now(self):
        self._server.call('backup_database')