예제 #1
0
 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)
예제 #2
0
 def create_filters(self):
     statuses = [(value, key) for key, value in Branch.statuses.items()]
     statuses.insert(0, (_('Any'), None))
     status_filter = ComboSearchFilter(_('Show branches with status'),
                                       statuses)
     status_filter.select(None)
     executer = self.search.get_query_executer()
     executer.add_filter_query_callback(status_filter,
                                        self._get_status_query)
     self.search.add_filter(status_filter, SearchFilterPosition.TOP)
예제 #3
0
파일: delivery.py 프로젝트: Felipebros/stoq
    def create_filters(self):
        self.set_text_field_columns(['recipient_name', 'identifier_str'])

        self.main_filter = ComboSearchFilter(_('Show'), [])
        self.add_filter(self.main_filter,
                        SearchFilterPosition.TOP,
                        callback=self._get_main_query)

        self.create_branch_filter(column=[Invoice.branch_id])
        self._update_filters()
예제 #4
0
파일: stock.py 프로젝트: Felipebros/stoq
 def create_filters(self):
     self.search.set_query(self._query)
     self.set_text_field_columns([
         'description', 'code', 'barcode', 'category_description',
         'manufacturer'
     ])
     branches = Branch.get_active_branches(self.store)
     self.branch_filter = ComboSearchFilter(
         _('Show by:'), api.for_combo(branches, empty=_("All branches")))
     self.branch_filter.select(api.get_current_branch(self.store))
     self.add_filter(self.branch_filter, position=SearchFilterPosition.TOP)
예제 #5
0
    def create_sellable_filter(self, label=None):
        from stoqlib.domain.sellable import Sellable
        items = [(desc, status) for status, desc in Sellable.statuses.items()]
        items.insert(0, (_(u"Any"), None))

        if label is None:
            label = _('With status:')
        sellable_filter = ComboSearchFilter(label, items)
        # Select status available by default
        sellable_filter.select(Sellable.STATUS_AVAILABLE)

        return sellable_filter
예제 #6
0
    def create_main_filter(self):
        self.main_filter = ComboSearchFilter(_('Show'), [])

        combo = self.main_filter.combo
        combo.color_attribute = 'color'
        combo.set_row_separator_func(self._on_main_filter__row_separator_func)
        self._update_filter_items()
        executer = self.search.get_query_executer()
        executer.add_filter_query_callback(self.main_filter,
                                           self._create_main_query)
        self.add_filter(self.main_filter, SearchFilterPosition.TOP)

        self.create_branch_filter(column=self.search_spec.branch_id)
예제 #7
0
    def create_filters(self):
        # Disable string search right now, until we use a proper Viewable
        # for this application
        self.search.disable_search_entry()
        self.branch_filter = ComboSearchFilter(_('Show inventories at:'),
                                               self._get_branches_for_filter())

        current = api.get_current_branch(self.store)
        self.branch_filter.select(current.id)

        self.add_filter(self.branch_filter,
                        SearchFilterPosition.TOP,
                        columns=["branch_id"])
예제 #8
0
    def create_filters(self):
        self.set_text_field_columns([
            'tracking_code', 'transporter_name', 'recipient_name',
            'identifier_str'
        ])

        # Status
        statuses = [(desc, st) for st, desc in Delivery.statuses.items()]
        statuses.insert(0, (_('Any'), None))
        self.status_filter = ComboSearchFilter(_('With status:'), statuses)
        self.status_filter.select(None)
        self.add_filter(self.status_filter,
                        columns=['status'],
                        position=SearchFilterPosition.TOP)
예제 #9
0
    def create_filters(self):
        self.set_text_field_columns(['description', 'identifier_str'])
        executer = self.search.get_query_executer()
        executer.add_query_callback(self._get_query)

        # Status
        items = [(v, k) for k, v in Till.statuses.items()
                 if k != Till.STATUS_PENDING]
        items.insert(0, (_(u'Any'), None))
        status_filter = ComboSearchFilter(_(u'Show entries of type'), items)
        status_filter.select(Till.STATUS_OPEN)
        self.add_filter(status_filter,
                        position=SearchFilterPosition.TOP,
                        columns=['status'])
예제 #10
0
    def create_payment_filter(self, label=None):
        from stoqlib.domain.payment.method import PaymentMethod
        methods = PaymentMethod.get_active_methods(self.store)
        items = [(_('Any'), None)]
        for method in methods:
            if method.method_name == 'multiple':
                continue
            items.append((method.description, method))

        if not label:
            label = _('Method:')
        payment_filter = ComboSearchFilter(label, items)
        payment_filter.select(None)

        return payment_filter
예제 #11
0
 def create_filters(self):
     # Category
     categories = self.store.find(SellableCategory)
     items = stoq_api.for_combo(categories, attr='full_description')
     items.insert(0, (_('Any'), None))
     category_filter = ComboSearchFilter(_('Category'), items)
     self.add_filter(category_filter, columns=[Sellable.category])
예제 #12
0
    def add_filter_by_attribute(self, attr, title, data_type, valid_values=None,
                                callback=None, use_having=False,
                                multiple_selection=False):
        """Add a filter accordingly to the attributes specified

        :param attr: attribute that will be filtered. This can be either the
          name of the attribute or the attribute itself.
        :param title: the title of the filter that will be visible in the
                      interface
        :param data_type: the attribute type (str, bool, decimal, etc)
        :param callback: the callback function that will be triggered
        :param use_having: use having expression in the query
        """
        if data_type is not bool:
            title += ':'

        if data_type == datetime.date:
            filter = DateSearchFilter(title)
            if valid_values:
                filter.clear_options()
                filter.add_custom_options()
                for opt in valid_values:
                    filter.add_option(opt)
                filter.select(valid_values[0])

        elif (data_type == decimal.Decimal or
              data_type == int or
              data_type == currency):
            filter = NumberSearchFilter(title)
            if data_type != int:
                filter.set_digits(2)
        elif data_type == str:
            if multiple_selection:
                filter = MultiSearchFilter(title, valid_values)
            elif valid_values:
                filter = ComboSearchFilter(title, valid_values)
                filter.enable_advanced()
            else:
                filter = StringSearchFilter(title)
                filter.enable_advanced()
        elif data_type == bool:
            filter = BoolSearchFilter(title)
        else:
            raise NotImplementedError(title, data_type)

        filter.set_removable()
        self.add_filter(filter, columns=[attr],
                        callback=callback,
                        use_having=use_having)

        if data_type is not bool:
            label = filter.get_title_label()
            label.set_alignment(1.0, 0.5)
            self.label_group.add_widget(label)
        combo = filter.get_mode_combo()
        if combo:
            self.combo_group.add_widget(combo)

        return filter
예제 #13
0
    def create_filters(self):
        self.set_text_field_columns([
            'source_branch_name', 'destination_branch_name', 'identifier_str'
        ])

        # Date
        self.date_filter = DateSearchFilter(_('Date:'))
        self.add_filter(self.date_filter, columns=['open_date', 'finish_date'])

        # Status
        self.status_filter = ComboSearchFilter(_('With status:'),
                                               self._get_status_options())
        self.status_filter.select('pending')
        executer = self.search.get_query_executer()
        executer.add_filter_query_callback(self.status_filter,
                                           self._get_status_query)
        self.add_filter(self.status_filter, position=SearchFilterPosition.TOP)
예제 #14
0
    def create_filters(self):
        self.set_text_field_columns([
            'sellable', 'description', 'client_name', 'identifier_str',
            'sale_identifier_str'
        ])

        self.main_filter = ComboSearchFilter(_('Show'), [])
        combo = self.main_filter.combo
        combo.color_attribute = 'color'
        combo.set_row_separator_func(self._on_main_filter__row_separator_func)

        self.add_filter(self.main_filter,
                        SearchFilterPosition.TOP,
                        callback=self._get_main_query)

        self.create_branch_filter(
            column=[WorkOrder.branch_id, WorkOrder.current_branch_id])
        self._update_filters()
예제 #15
0
    def create_salesperson_filter(self, label=None):
        from stoqlib.domain.person import SalesPerson
        items = SalesPerson.get_active_items(
            self.store, api.get_current_branch(self.store))
        items.insert(0, (_("Any"), None))

        if not label:
            label = _('Salesperson:')
        return ComboSearchFilter(label, items)
예제 #16
0
 def create_filters(self):
     self.search.set_query(self._query_executer)
     self.set_text_field_columns(['client_name', 'salesperson_name',
                                  'identifier_str', 'token_code',
                                  'token_name'])
     self.status_filter = ComboSearchFilter(_(u"Show orders"),
                                            self._get_status_values())
     self.add_filter(self.status_filter, position=SearchFilterPosition.TOP,
                     columns=['status'])
예제 #17
0
    def create_filters(self):
        self.search.set_query(self.executer_query)

        # Category
        categories = self.store.find(SellableCategory)
        items = stoq_api.for_combo(categories, attr='full_description')
        items.insert(0, (_('Any'), None))
        category_filter = ComboSearchFilter(_('Category'), items)
        self.add_filter(category_filter, position=SearchFilterPosition.TOP)
        self.category_filter = category_filter
예제 #18
0
    def create_filters(self):
        statuses = [(v, k) for k, v in Client.statuses.items()]
        statuses.insert(0, (_('Any'), None))
        status_filter = ComboSearchFilter(_('Show clients with status'),
                                          statuses)
        status_filter.select(None)
        self.add_filter(status_filter, SearchFilterPosition.TOP, ['status'])

        if self._birth_date:
            birthday_filter = self.search.add_filter_by_attribute(
                'birth_date',
                _('Birthday'),
                datetime.date,
                callback=self.birthday_search)
            # FIXME: The fifth position is a search by day. This is done
            # elsewhere too but we should not hardcode it. Try to
            # find a better solution in the future and fix everything
            birthday_filter.mode.select_item_by_position(5)
            birthday_filter.start_date.set_date(self._birth_date)
            self.search.refresh()
예제 #19
0
    def create_provider_filter(self, label=None):
        from stoqlib.domain.payment.card import CreditProvider
        providers = CreditProvider.get_card_providers(self.store).order_by(
            CreditProvider.short_name)
        items = [(p.short_name, p) for p in providers]
        items.insert(0, (_("Any"), None))

        if not label:
            label = _('Provider:')
        provider_filter = ComboSearchFilter(label, items)

        return provider_filter
예제 #20
0
class ProductionItemsSearch(ProductSearch):
    title = _(u'Production Items')
    search_spec = ProductionItemView
    report_class = ProductionItemReport
    csv_data = None
    has_print_price_button = False
    text_field_columns = [ProductionItemView.description,
                          ProductionItemView.order_identifier_str]

    def __init__(self, store, hide_footer=True, hide_toolbar=True):
        ProductSearch.__init__(self, store, hide_footer=hide_footer,
                               hide_toolbar=hide_toolbar)

    #
    # SearchDialog
    #

    def create_filters(self):
        statuses = [(desc, i) for i, desc in ProductionOrder.statuses.items()]
        statuses.insert(0, (_(u'Any'), None))
        self.status_filter = ComboSearchFilter(_('order status:'), statuses)
        self.status_filter.select(ProductionOrder.ORDER_PRODUCING)
        self.add_filter(self.status_filter, columns=['order_status'],
                        position=SearchFilterPosition.TOP)

    def get_columns(self):
        return [IdentifierColumn('order_identifier', title=_(u"Order #"),
                                 sorted=True),
                SearchColumn('category_description', title=_(u'Category'),
                             data_type=str),
                SearchColumn('description', title=_(u'Description'),
                             data_type=str, expand=True),
                SearchColumn('unit_description', title=_(u'Unit'),
                             data_type=str),
                SearchColumn('quantity', title=_(u'To Produce'),
                             data_type=Decimal),
                SearchColumn('produced', title=_(u'Produced'),
                             data_type=Decimal),
                SearchColumn('lost', title=_(u'Lost'), data_type=Decimal,
                             visible=False)]
예제 #21
0
    def create_branch_filter(self, label=None, column=None):
        """Returns a new branch filter.

        :param label: The label to be used for the filter
        :param column: When provided, besides creating the filter, we will also
          add it to the interface, filtering by the informed column.
        """
        items = api.get_branches_for_filter(self.store, use_id=True)
        if not label:
            label = _('Branch:')

        if column and not isinstance(column, list):
            column = [column]

        branch_filter = ComboSearchFilter(label, items)
        current = api.get_current_branch(self.store)
        branch_filter.select(current.id)
        if column:
            self.add_filter(branch_filter, columns=column,
                            position=SearchFilterPosition.TOP)

        return branch_filter
예제 #22
0
파일: sales.py 프로젝트: Felipebros/stoq
    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)
예제 #23
0
파일: delivery.py 프로젝트: Felipebros/stoq
class DeliveryApp(ShellApp):
    """Delivery app"""

    app_title = _(u'Deliveries')
    gladefile = 'delivery'
    search_spec = DeliveryView
    search_label = _(u'matching:')

    # TODO: Create a report for the view here
    #report_table = DeliveriesReport

    #
    # Application
    #

    def create_actions(self):
        #group = get_accels('app.delivery')
        actions = [
            # Search
            (
                "Transporters",
                None,
                _("Transporters..."),
                # group.get("search_transporters")),
                None),
            (
                "Clients",
                None,
                _("Clients..."),
                # group.get("search_clients")),
                None),
            (
                "Products",
                None,
                _("Products..."),
                # group.get("search_products")),
                None),
            (
                "Services",
                None,
                _("Services..."),
                # group.get("search_services")),
                None),

            # Delivery
            (
                "Edit",
                Gtk.STOCK_EDIT,
                _("Edit..."),
                # group.get('delivery_pick'),
                None,
                _("Edit the selected delivery")),
            (
                "Pick",
                None,
                _("Pick..."),
                # group.get('delivery_pick'),
                None,
                _("Pick the selected delivery")),
            (
                "Pack",
                None,
                _("Pack..."),
                # group.get('delivery_pack'),
                None,
                _("Pack the selected delivery")),
            (
                "Send",
                None,
                _("Send..."),
                # group.get('delivery_send'),
                None,
                _("Send the selected delivery to deliver")),
            (
                "Receive",
                Gtk.STOCK_APPLY,
                _("Mark as received..."),
                # group.get('delivery_receive'),
                None,
                _("Mark the selected delivery as received by the recipient")),
            (
                "Cancel",
                Gtk.STOCK_CANCEL,
                _("Cancel..."),
                # group.get('delivery_cancel'),
                None,
                _("Cancel the selected delivery")),
        ]
        self.delivery_ui = self.add_ui_actions(actions)
        self.set_help_section(_(u"Delivery help"), 'app-delivery')

    def get_domain_options(self):
        options = [
            ('fa-edit-symbolic', _('Edit'), 'delivery.Edit', True),
            ('', _('Pick'), 'delivery.Cancel', False),
            ('', _('Pack'), 'delivery.Cancel', False),
            ('fa-share-square-symbolic', _('Send'), 'delivery.Send', True),
            ('fa-check-square-symbolic', _('Mark as received'),
             'delivery.Receive', True),
            ('fa-ban-symbolic', _('Cancel'), 'delivery.Cancel', True),
        ]

        return options

    def create_ui(self):
        self.search.enable_lazy_search()

        # XXX: What should we put on new items?
        self.window.add_new_items([])

        self.window.add_search_items([
            self.Products,
            self.Services,
            self.Transporters,
            self.Clients,
        ])

        # FIXME: identifier is here because it needs an integer column.
        # The lazy summary will actually be taken from the view's
        # post_search_callback
        self.search.set_summary_label(
            column='identifier',
            label=('<b>%s</b>' % api.escape(_('Number of deliveries:'))),
            format='<b>%s</b>',
            parent=self.get_statusbar_message_area())

        self.results.set_cell_data_func(self._on_results__cell_data_func)

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

        self.search.focus_search_entry()

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

        state = states[1]
        if state is None:
            return

        if state.value is None:
            # Base search with no filters
            base_msg = _("No deliveries could be found.")
            url_msg = ''
        elif state:
            base_msg = {
                Delivery.STATUS_INITIAL:
                _("No pending deliveries could be found"),
                Delivery.STATUS_CANCELLED:
                _("No cancelled deliveries could be found"),
                Delivery.STATUS_PICKED:
                _("No picked deliveries could be found"),
                Delivery.STATUS_PACKED:
                _("No packed deliveries could be found"),
                Delivery.STATUS_SENT:
                _("No sent deliveries could be found"),
                Delivery.STATUS_RECEIVED:
                _("No received deliveries could be found"),
            }[state.value]
            url_msg = ''

        msg = '\n\n'.join([base_msg, url_msg])
        self.search.set_message(msg)

    def create_filters(self):
        self.set_text_field_columns(['recipient_name', 'identifier_str'])

        self.main_filter = ComboSearchFilter(_('Show'), [])
        self.add_filter(self.main_filter,
                        SearchFilterPosition.TOP,
                        callback=self._get_main_query)

        self.create_branch_filter(column=[Invoice.branch_id])
        self._update_filters()

    def get_columns(self):
        return [
            IdentifierColumn('identifier',
                             title=_("Operation #"),
                             sorted=True,
                             width=110,
                             format_func=self._format_identifier),
            SearchColumn('operation_nature',
                         title=_('Operation Nature'),
                         data_type=str),
            SearchColumn('status_str',
                         title=_(u'Status'),
                         search_attribute='status',
                         data_type=str,
                         valid_values=self._get_status_values()),
            SearchColumn('recipient_name',
                         title=_(u'Recipient'),
                         data_type=str,
                         expand=True),
            Column('flag_icon',
                   title=_(u'Status (Description)'),
                   column='recipient_name',
                   data_type=GdkPixbuf.Pixbuf,
                   format_func=self._format_state_icon,
                   format_func_data=True),
            SearchColumn('branch_name',
                         title=_(u'Branch'),
                         data_type=str,
                         visible=False),
            SearchColumn('transporter_name',
                         title=_(u'Transporter'),
                         data_type=str),
            SearchColumn('open_date',
                         title=_(u'Open date'),
                         data_type=datetime.date),
            SearchColumn('cancel_date',
                         title=_(u'Cancel date'),
                         data_type=datetime.date,
                         visible=False),
            SearchColumn('pick_date',
                         title=_(u'Pick date'),
                         data_type=datetime.date,
                         visible=False),
            SearchColumn('pack_date',
                         title=_(u'Pack date'),
                         data_type=datetime.date,
                         visible=False),
            SearchColumn('send_date',
                         title=_(u'Send date'),
                         data_type=datetime.date),
            SearchColumn('receive_date',
                         title=_(u'Receive date'),
                         data_type=datetime.date,
                         visible=False),
        ]

    def set_open_inventory(self):
        pass

    #
    # Private
    #

    def _format_identifier(self, value):
        return str(value).zfill(5)

    def _edit(self):
        delivery = self.search.get_selected_item().delivery
        with api.new_store() as store:
            self.run_dialog(DeliveryEditor, store, model=store.fetch(delivery))

        if store.committed:
            self._update_view()

    def _cancel(self):
        if not yesno(_("This will cancel the delivery. Are you sure?"),
                     Gtk.ResponseType.NO, _(u"Cancel"), _(u"Don't cancel")):
            return

        selection = self.search.get_selected_item()
        with api.new_store() as store:
            delivery = store.fetch(selection.delivery)
            delivery.close()

        self._update_view(select_item=selection)

    def _pick(self):
        if not yesno(_("This will mark the delivery as picked. Are you sure?"),
                     Gtk.ResponseType.NO, _(u"Mark as picked"),
                     _(u"Don't mark")):
            return

        selection = self.search.get_selected_item()
        with api.new_store() as store:
            delivery = store.fetch(selection.delivery)
            delivery.pick(api.get_current_user(store))

        self._update_view(select_item=selection)

    def _pack(self):
        if not yesno(_("This will mark the delivery as packed. Are you sure?"),
                     Gtk.ResponseType.NO, _(u"Mark as packed"),
                     _(u"Don't mark")):
            return

        selection = self.search.get_selected_item()
        with api.new_store() as store:
            delivery = store.fetch(selection.delivery)
            delivery.pack(api.get_current_user(store))

        self._update_view(select_item=selection)

    def _send(self):
        if not yesno(
                _("This will mark the delivery as sent to the recipient. "
                  "Are you sure?"), Gtk.ResponseType.NO, _(u"Mark as sent"),
                _(u"Don't mark")):
            return

        selection = self.search.get_selected_item()
        with api.new_store() as store:
            delivery = store.fetch(selection.delivery)
            delivery.send(api.get_current_user(store))

        self._update_view(select_item=selection)

    def _receive(self):
        if not yesno(
                _("This will mark the delivery as received by the recipient. "
                  "Are you sure?"), Gtk.ResponseType.NO,
                _(u"Mark as received"), _(u"Don't mark")):
            return

        selection = self.search.get_selected_item()
        with api.new_store() as store:
            delivery = store.fetch(selection.delivery)
            delivery.receive()

        self._update_view(select_item=selection)

    def _format_state_icon(self, item, data):
        # This happens with lazy object lists. Sometimes it calls this function
        # without actually having the real object.
        if not isinstance(item, DeliveryView):
            return

        stock_id, tooltip = get_delivery_state_icon(item.delivery)
        if stock_id is not None:
            return render_icon(stock_id, Gtk.IconSize.MENU)

    def _get_main_query(self, state):
        if state.value is None:
            return True

        return Delivery.status == state.value

    def _get_status_values(self):
        return ([(_('Any'), None)] + [(v, k)
                                      for k, v in Delivery.statuses.items()])

    def _update_view(self, select_item=None):
        self.refresh()
        if select_item is not None:
            item = self.store.find(DeliveryView, id=select_item.id).one()
            self.select_result(item)
        self._update_list_aware_view()

    def _update_list_aware_view(self):
        selection = self.search.get_selected_item()
        has_selected = bool(selection)
        delivery = has_selected and selection.delivery

        self.set_sensitive([self.Edit], has_selected)
        self.set_sensitive([self.Pick], has_selected and delivery.can_pick())
        self.set_sensitive([self.Pack], has_selected and delivery.can_pack())
        self.set_sensitive([self.Send], has_selected and delivery.can_send())
        self.set_sensitive([self.Receive], has_selected
                           and delivery.can_receive())
        self.set_sensitive([self.Cancel], has_selected
                           and delivery.can_cancel())

    def _update_filters(self):
        items = [(_("All Deliveries"), None)]
        items.extend((status_str, status)
                     for status, status_str in Delivery.statuses.items())
        self.main_filter.update_values(items)

    #
    #  Callbacks
    #

    def _on_results__cell_data_func(self, column, renderer, item, text):
        if not isinstance(renderer, Gtk.CellRendererText):
            return text

        delivery = item.delivery
        is_finished = delivery.status in [
            Delivery.STATUS_SENT, Delivery.STATUS_RECEIVED
        ]
        is_waiting = delivery.status == Delivery.STATUS_INITIAL
        is_picked = delivery.status == Delivery.STATUS_PICKED

        for prop, is_set, value in [('strikethrough', is_finished, True),
                                    ('style', is_picked, Pango.Style.ITALIC),
                                    ('weight', is_waiting, Pango.Weight.BOLD)]:
            renderer.set_property(prop + '-set', is_set)
            if is_set:
                renderer.set_property(prop, value)

        return text

    def on_search__result_selection_changed(self, search):
        self._update_list_aware_view()

    def on_search__result_item_activated(self, search, item):
        self._edit()

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

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

    def on_Pick__activate(self, action):
        self._pick()

    def on_Pack__activate(self, action):
        self._pack()

    def on_Send__activate(self, action):
        self._send()

    def on_Receive__activate(self, action):
        self._receive()

    def on_Products__activate(self, action):
        self.run_dialog(ProductSearch,
                        self.store,
                        hide_footer=True,
                        hide_toolbar=True)

    def on_Transporters__activate(self, action):
        self.run_dialog(TransporterSearch, self.store)

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

    def on_Clients__activate(self, button):
        self.run_dialog(ClientSearch, self.store, hide_footer=True)
예제 #24
0
 def create_filters(self):
     items = [(v, k) for k, v in fiscal_book_entries.items()]
     self.entry_type = ComboSearchFilter(_('Show entries of type'), items)
     self.add_filter(self.entry_type,
                     callback=self._get_entry_type_query,
                     position=SearchFilterPosition.TOP)
예제 #25
0
class DeliverySearch(SearchEditor):
    """Delivery search implementation"""

    title = _('Delivery Search')
    search_spec = DeliveryView
    editor_class = DeliveryEditor
    has_new_button = False
    size = (750, 450)

    #
    #  Private
    #

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

    #
    #  SearchEditor
    #

    def create_filters(self):
        self.set_text_field_columns([
            'tracking_code', 'transporter_name', 'recipient_name',
            'identifier_str'
        ])

        # Status
        statuses = [(desc, st) for st, desc in Delivery.statuses.items()]
        statuses.insert(0, (_('Any'), None))
        self.status_filter = ComboSearchFilter(_('With status:'), statuses)
        self.status_filter.select(None)
        self.add_filter(self.status_filter,
                        columns=['status'],
                        position=SearchFilterPosition.TOP)

    def get_editor_model(self, viewable):
        return viewable.delivery

    def get_columns(self):
        return [
            IdentifierColumn('identifier',
                             title=_('Sale #'),
                             order=Gtk.SortType.DESCENDING,
                             sorted=True),
            SearchColumn('status_str',
                         title=_('Status'),
                         data_type=str,
                         search_attribute='status',
                         valid_values=self._get_status_values()),
            Column('address_str',
                   title=_('Address'),
                   data_type=str,
                   expand=True,
                   ellipsize=Pango.EllipsizeMode.END),
            SearchColumn('tracking_code',
                         title=_('Tracking code'),
                         data_type=str),
            SearchColumn('transporter_name',
                         title=_('Transporter'),
                         data_type=str),
            SearchColumn('recipient_name', title=_('Recipient'),
                         data_type=str),
            SearchColumn('open_date',
                         title=_('Open date'),
                         data_type=datetime.date,
                         visible=False),
            SearchColumn('send_date',
                         title=_('Sent date'),
                         data_type=datetime.date,
                         visible=False),
            SearchColumn('receive_date',
                         title=_('Received date'),
                         data_type=datetime.date,
                         visible=False),
        ]
예제 #26
0
class TransferOrderSearch(SearchDialog):
    title = _(u"Transfer Order Search")
    size = (750, 500)
    search_spec = TransferOrderView
    report_class = TransferOrderReport
    selection_mode = Gtk.SelectionMode.MULTIPLE

    def __init__(self, store):
        SearchDialog.__init__(self, store)
        self._setup_widgets()

    def _show_transfer_order_details(self, order_view):
        transfer_order = order_view.transfer_order
        with api.new_store() as store:
            model = store.fetch(transfer_order)
            run_dialog(TransferOrderDetailsDialog, self, store, model)
            store.retval = store.get_pending_count() > 0

    def _setup_widgets(self):
        self.results.connect('row_activated', self.on_row_activated)
        self.update_widgets()

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

    #
    # SearchDialog Hooks
    #

    def update_widgets(self):
        orders = self.results.get_selected_rows()
        has_one_selected = len(orders) == 1
        self.set_details_button_sensitive(has_one_selected)
        self.set_print_button_sensitive(has_one_selected)

    def create_filters(self):
        self.set_text_field_columns([
            'source_branch_name', 'destination_branch_name', 'identifier_str'
        ])

        # Date
        self.date_filter = DateSearchFilter(_('Date:'))
        self.add_filter(self.date_filter, columns=['open_date', 'finish_date'])

        # Status
        self.status_filter = ComboSearchFilter(_('With status:'),
                                               self._get_status_options())
        self.status_filter.select('pending')
        executer = self.search.get_query_executer()
        executer.add_filter_query_callback(self.status_filter,
                                           self._get_status_query)
        self.add_filter(self.status_filter, position=SearchFilterPosition.TOP)

    def _get_status_options(self):
        return [
            (_('All transfers'), None),
            (_('Pending receive'), 'pending'),
            (_('Received'), 'received'),
            (_('Sent'), 'sent'),
            (_('Cancelled'), 'cancelled'),
        ]

    def _get_status_query(self, state):
        current_branch = api.get_current_branch(self.store)
        if state.value == 'pending':
            return And(
                TransferOrder.status == TransferOrder.STATUS_SENT,
                TransferOrder.destination_branch_id == current_branch.id)
        elif state.value == 'received':
            return And(
                TransferOrder.status == TransferOrder.STATUS_RECEIVED,
                TransferOrder.destination_branch_id == current_branch.id)
        elif state.value == 'sent':
            return And(
                TransferOrder.source_branch_id == current_branch.id,
                Not(TransferOrder.status == TransferOrder.STATUS_CANCELLED))
        elif state.value == 'cancelled':
            return And(TransferOrder.status == TransferOrder.STATUS_CANCELLED,
                       TransferOrder.source_branch_id == current_branch.id)
        else:
            return Or(TransferOrder.source_branch_id == current_branch.id,
                      TransferOrder.destination_branch_id == current_branch.id)

    def get_columns(self):
        return [
            IdentifierColumn('identifier', title=_('Transfer #')),
            SearchColumn('transfer_order.status_str',
                         _('Status'),
                         data_type=str,
                         valid_values=self._get_status_values(),
                         search_attribute='status',
                         width=100),
            SearchColumn('open_date',
                         _('Open date'),
                         data_type=datetime.date,
                         sorted=True,
                         width=100),
            SearchColumn('finish_date',
                         _('Finish Date'),
                         data_type=datetime.date,
                         width=100,
                         visible=False),
            SearchColumn('source_branch_name',
                         _('Source'),
                         data_type=str,
                         expand=True),
            SearchColumn('destination_branch_name',
                         _('Destination'),
                         data_type=str,
                         width=220),
            Column('total_items',
                   _('Items'),
                   data_type=Decimal,
                   format_func=format_quantity,
                   width=110)
        ]

    #
    # Callbacks
    #

    def on_row_activated(self, klist, view):
        self._show_transfer_order_details(view)

    def on_details_button_clicked(self, button):
        self._show_transfer_order_details(self.results.get_selected_rows()[0])
예제 #27
0
    def create_filters(self):
        items = [(value, key) for key, value in Sale.statuses.items()]
        items.insert(0, (_('Any'), None))

        status_filter = ComboSearchFilter(_('Show sales with status'), items)
        self.add_filter(status_filter, columns=[SaleView.status])
예제 #28
0
class ServicesApp(ShellApp):
    """Services app"""

    app_title = _(u'Services')
    gladefile = 'services'
    search_spec = WorkOrderView
    search_label = _(u'matching:')
    report_table = WorkOrdersReport

    _status_query_mapper = {
        'pending':
        Or(WorkOrder.status == WorkOrder.STATUS_OPENED,
           WorkOrder.status == WorkOrder.STATUS_WORK_WAITING),
        'in-progress':
        WorkOrder.status == WorkOrder.STATUS_WORK_IN_PROGRESS,
        'finished':
        WorkOrder.status == WorkOrder.STATUS_WORK_FINISHED,
        'delivered':
        WorkOrder.status == WorkOrder.STATUS_DELIVERED,
        'cancelled':
        WorkOrder.status == WorkOrder.STATUS_CANCELLED,
        'all-orders':
        None,
        'not-delivered':
        And(WorkOrder.status != WorkOrder.STATUS_CANCELLED,
            WorkOrder.status != WorkOrder.STATUS_DELIVERED),
    }
    _flags_query_mapper = {
        'approved':
        And(WorkOrder.status != WorkOrder.STATUS_OPENED,
            WorkOrder.status != WorkOrder.STATUS_CANCELLED),
        'in-transport':
        Eq(WorkOrder.current_branch_id, None),
        'rejected':
        Eq(WorkOrder.is_rejected, True),
    }

    def __init__(self, *args, **kwargs):
        self._other_kinds = {}
        self.actions = WorkOrderActions.get_instance()
        super(ServicesApp, self).__init__(*args, **kwargs)

    #
    # Application
    #

    def create_actions(self):
        group = get_accels('app.services')
        actions = [
            # Search
            ("Products", None, _(u"Products..."), group.get("search_products")
             ),
            ("Services", None, _(u"Services..."),
             group.get("search_services")),
            ("Categories", None, _(u"Categories..."),
             group.get("search_categories")),
            ("Clients", None, _(u"Clients..."), group.get("search_clients")),
        ]
        self.services_ui = self.add_ui_actions(actions)
        radio_actions = [
            ('ViewKanban', '', _("View as Kanban"), '',
             _("Show in Kanban mode")),
            ('ViewList', '', _("View as List"), '', _("Show in list mode")),
        ]
        self.add_ui_actions(radio_actions, 'RadioActions')
        self.set_help_section(_(u"Services help"), 'app-services')

    def get_domain_options(self):
        options = [
            ('fa-info-circle-symbolic', _('Details'), 'work_order.Details',
             True),
            ('fa-edit-symbolic', _('Edit'), 'work_order.Edit', True),
            ('fa-check-symbolic', _('Finish'), 'work_order.FinishOrClose',
             True),
            ('fa-ban-symbolic', _('Cancel'), 'work_order.Cancel', True),
            ('', _('Deliver'), 'work_order.Close', False),
            # Separator
            ('', _('Approve'), 'work_order.Approve', False),
            ('', _('Pause the work'), 'work_order.Pause', False),
            ('', _('Start the work'), 'work_order.Work', False),
            ('', _('Reject order'), 'work_order.Reject', False),
            ('', _('Check order'), 'work_order.CheckOrder', False),
            ('', _('Inform client'), 'work_order.InformClient', False),
            ('', _('Undo order rejection'), 'work_order.UndoRejection', False),
            ('', _('Repoen order'), 'work_order.Reopen', False),
            # Separator
            ('', _('Print quote'), 'work_order.PrintQuote', False),
            ('', _('Print receipt'), 'work_order.PrintReceipt', False),
        ]
        return options

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

        self.window.add_print_items2([
            (_("Print quote..."), 'work_order.PrintQuote'),
            (_("Print receipt..."), 'work_order.PrintReceipt'),
        ])
        self.window.add_export_items()
        self.window.add_extra_items2([
            (_("Send orders..."), 'work_order.SendOrders'),
            (_("Receive orders..."), 'work_order.ReceiveOrders'),
        ])
        self.window.add_extra_items([self.ViewKanban, self.ViewList])
        self.window.add_new_items2([
            (_("Work order..."), 'work_order.NewOrder'),
        ])

        self.window.add_search_items([
            self.Products,
            self.Services,
            self.Categories,
            self.Clients,
        ])

        self.search.set_summary_label(column='total',
                                      label=('<b>%s</b>' %
                                             api.escape(_('Total:'))),
                                      format='<b>%s</b>',
                                      parent=self.get_statusbar_message_area())

        self.results.set_cell_data_func(self._on_results__cell_data_func)

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

        is_kanban = self.window._current_app_settings.get('show-kanban', False)
        if is_kanban:
            self.ViewKanban.set_state(GLib.Variant.new_boolean(True))
            self.search.set_result_view(WorkOrderResultKanbanView,
                                        refresh=refresh)

        if refresh:
            self._update_view()

        self.search.focus_search_entry()

    def deactivate(self):
        # Reset actions to clean up connections
        self.actions = None

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

        base_msg = ''
        url_msg = ''
        state = states[1]
        if state and state.value is None:
            # Base search with no filters
            base_msg = _(u"No work orders could be found.")
            url = u"<a href='new_order'>%s</a>" % (api.escape(
                _(u"create a new work order")), )
            url_msg = _(u"Would you like to %s ?") % (url, )
        else:
            kind, value = state.value.value.split(':')
            # Search filtering by status
            if kind == 'status':
                if value == 'pending':
                    base_msg = _(u"No pending work orders could be found.")
                elif value == 'in-progress':
                    base_msg = _(u"No work orders in progress could be found.")
                elif value == 'finished':
                    base_msg = _(u"No finished work orders could be found.")
                elif value == 'delivered':
                    base_msg = _(u"No delivered or cancelled work "
                                 u"orders could be found.")
            # Search filtering by category
            elif kind == 'category':
                base_msg = _(u"No work orders in the category %s "
                             u"could be found.") % ('<b>%s</b>' % (value, ), )
                url = u"<a href='new_order?%s'>%s</a>" % (
                    urllib.parse.quote(value),
                    api.escape(_(u"create a new work order")),
                )
                url_msg = _(u"Would you like to %s ?") % (url, )

        if not base_msg:
            return

        msg = '\n\n'.join([base_msg, url_msg])
        self.search.set_message(msg)

    def create_filters(self):
        self.set_text_field_columns([
            'sellable', 'description', 'client_name', 'identifier_str',
            'sale_identifier_str'
        ])

        self.main_filter = ComboSearchFilter(_('Show'), [])
        combo = self.main_filter.combo
        combo.color_attribute = 'color'
        combo.set_row_separator_func(self._on_main_filter__row_separator_func)

        self.add_filter(self.main_filter,
                        SearchFilterPosition.TOP,
                        callback=self._get_main_query)

        self.create_branch_filter(
            column=[WorkOrder.branch_id, WorkOrder.current_branch_id])
        self._update_filters()

    def get_columns(self):
        return [
            IdentifierColumn('identifier', title=_('WO #'), sorted=True),
            IdentifierColumn('sale_identifier',
                             title=_("Sale #"),
                             visible=False),
            SearchColumn('status_str',
                         title=_(u'Status'),
                         search_attribute='status',
                         data_type=str,
                         valid_values=self._get_status_values(),
                         visible=False),
            SearchColumn('category_name',
                         title=_(u'Category'),
                         data_type=str,
                         visible=False,
                         multiple_selection=True,
                         search_attribute='category_id',
                         valid_values=self._get_category_values()),
            Column('equipment',
                   title=_(u'Equipment (Description)'),
                   data_type=str,
                   expand=True,
                   pack_end=True),
            Column('category_color',
                   title=_(u'Equipment (Description)'),
                   column='equipment',
                   data_type=GdkPixbuf.Pixbuf,
                   format_func=render_pixbuf),
            Column('flag_icon',
                   title=_(u'Equipment (Description)'),
                   column='equipment',
                   data_type=GdkPixbuf.Pixbuf,
                   format_func=self._format_state_icon,
                   format_func_data=True),
            SearchColumn('client_name', title=_(u'Client'), data_type=str),
            SearchColumn('branch_name',
                         title=_(u'Branch'),
                         data_type=str,
                         visible=False),
            SearchColumn('current_branch_name',
                         title=_(u'Current branch'),
                         data_type=str,
                         visible=False),
            SearchColumn('execution_branch_name',
                         title=_(u'Execution branch'),
                         data_type=str,
                         visible=False),
            SearchColumn('supplier_order',
                         title=_("Supplier Order #"),
                         visible=False,
                         data_type=str),
            SearchColumn('open_date',
                         title=_(u'Open date'),
                         data_type=datetime.date),
            SearchColumn('approve_date',
                         title=_(u'Approval date'),
                         data_type=datetime.date,
                         visible=False),
            SearchColumn('estimated_start',
                         title=_(u'Estimated start'),
                         data_type=datetime.date,
                         visible=False),
            SearchColumn('estimated_finish',
                         title=_(u'Estimated finish'),
                         data_type=datetime.date,
                         visible=False),
            SearchColumn('finish_date',
                         title=_(u'Finish date'),
                         data_type=datetime.date,
                         visible=False),
            SearchColumn('total', title=_(u'Total'), data_type=currency),
        ]

    def set_open_inventory(self):
        # This needs to be implemented because we are calling check_open_inventory.
        # We won't do anything here tough, WorkOrderEditor will, but we call
        # check_open_inventory to display the open inventory bar and make
        # it explicit for the user that there's an open inventory
        pass

    def search_for_date(self, date):
        self.main_filter.combo.select(self._not_delivered_filter_item)
        dfilter = DateSearchFilter(_("Estimated finish"))
        dfilter.set_removable()
        dfilter.select(data=DateSearchFilter.Type.USER_DAY)
        self.add_filter(dfilter, columns=["estimated_finish"])
        dfilter.start_date.set_date(date)
        self.refresh()

    def add_filters(self, filter_items, kind, mapper):
        """Add additional filter option.

        :param filter_items: list of tuple (name, value, color)
        :param kind:the kind of filter
        :param mapper: a dictionary containing the query for each option
        """
        for item in filter_items:
            option = _FilterItem(item[0], item[1], color=item[2])
            self.main_filter.combo.append_item(option.name, option)
        self._other_kinds[kind] = mapper

    #
    # Private
    #

    def _format_state_icon(self, item, data):
        # This happens with lazy object lists. Sometimes it calls this function
        # without actually having the real object.
        if not isinstance(item, WorkOrderView):
            return

        stock_id, tooltip = get_workorder_state_icon(item.work_order)
        if stock_id is not None:
            return render_icon(stock_id, 16)

    def _get_main_query(self, state):
        item = state.value
        kind, value = item.value.split(':')
        if kind in self._other_kinds:
            return self._other_kinds[kind][value]
        elif kind == 'category':
            return WorkOrder.category_id == item.id
        elif kind == 'status':
            return self._status_query_mapper[value]
        elif kind == 'flag':
            return self._flags_query_mapper[value]
        else:
            raise AssertionError(kind, value)

    def _get_status_values(self):
        return ([(_('Any'), None)] + [(v, k)
                                      for k, v in WorkOrder.statuses.items()])

    def _get_category_values(self):
        return [(category.name, category.id, render_pixbuf(category.color))
                for category in self.store.find(WorkOrderCategory)]

    def _update_view(self, select_item=None):
        self.refresh()
        if select_item is not None:
            item = self.store.find(WorkOrderView, id=select_item.id).one()
            self.select_result(item)
        self._update_list_aware_view()

    def _update_list_aware_view(self):
        selection = self.search.get_selected_item()
        wo = selection and selection.work_order
        self.actions.set_model(wo)

        finish_btn = self.window.domain_header.get_children()[2]
        finish_btn.set_tooltip_text(_(u"Finish"))
        # If the selected work order is already finished, we change the finish
        # button's label.
        if wo and wo.status == WorkOrder.STATUS_WORK_FINISHED:
            finish_btn.set_tooltip_text(_(u"Deliver"))

    def _update_filters(self):
        self._not_delivered_filter_item = _FilterItem(_(u'Not delivered'),
                                                      'status:not-delivered')
        options = [
            self._not_delivered_filter_item,
            _FilterItem(_(u'Pending'), 'status:pending'),
            _FilterItem(_(u'In progress'), 'status:in-progress'),
            _FilterItem(_(u'Finished'), 'status:finished'),
            _FilterItem(_(u'Delivered'), 'status:delivered'),
            _FilterItem(_(u'Cancelled'), 'status:cancelled'),
            _FilterItem(_(u'All work orders'), 'status:all-orders'),
            _FilterItem('sep', 'sep'),
            _FilterItem(_(u'Approved'), 'flag:approved'),
            _FilterItem(_(u'In transport'), 'flag:in-transport'),
            _FilterItem(_(u'Rejected'), 'flag:rejected'),
        ]

        categories = list(self.store.find(WorkOrderCategory))
        if len(categories):
            options.append(_FilterItem('sep', 'sep'))
        for category in categories:
            value = 'category:%s' % (category.name, )
            options.append(
                _FilterItem(category.name,
                            value,
                            color=category.color,
                            obj_id=category.id))

        self.main_filter.update_values([(item.name, item) for item in options])

    def _run_order_category_dialog(self):
        with api.new_store() as store:
            self.run_dialog(WorkOrderCategoryDialog, store)
        self._update_view()
        self._update_filters()

    #
    # Kiwi Callbacks
    #

    def _on_main_filter__row_separator_func(self, model, titer):
        obj = model[titer][1]
        if obj and obj.value == 'sep':
            return True
        return False

    def _on_results__cell_data_func(self, column, renderer, wov, text):
        if not isinstance(renderer, Gtk.CellRendererText):
            return text

        work_order = wov.work_order
        is_finished = work_order.status == WorkOrder.STATUS_WORK_FINISHED
        is_delivered = work_order.status in [
            WorkOrder.STATUS_CANCELLED, WorkOrder.STATUS_DELIVERED
        ]
        is_late = work_order.is_late()

        for prop, is_set, value in [('strikethrough', is_delivered, True),
                                    ('style', is_finished, Pango.Style.ITALIC),
                                    ('weight', is_late, Pango.Weight.BOLD)]:
            renderer.set_property(prop + '-set', is_set)
            if is_set:
                renderer.set_property(prop, value)

        return text

    def on_search__result_item_popup_menu(self, search, objectlist, item,
                                          event):
        self._popover.set_relative_to(objectlist)
        self.show_popover(event)

    def on_search__result_item_activated(self, search, item):
        self.actions.edit_or_details(item.work_order)

    def on_search__result_selection_changed(self, search):
        self._update_list_aware_view()

    def on_results__activate_link(self, results, uri):
        if not uri.startswith('new_order'):
            return

        if '?' in uri:
            category_name = str(urllib.parse.unquote(uri.split('?', 1)[1]))
            category = self.store.find(WorkOrderCategory,
                                       name=category_name).one()
        else:
            category = None

        self.actions.new_order(category=category)

    def on_actions__model_created(self, actions, order):
        self._update_view(select_item=order)
        # A category may have been created on the editor
        self._update_filters()

    def on_actions__model_edited(self, actions, order):
        self._update_view()
        # A category may have been created on the editor
        self._update_filters()

    def on_Products__activate(self, action):
        self.run_dialog(ProductSearch,
                        self.store,
                        hide_footer=True,
                        hide_toolbar=True)

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

    def on_Categories__activate(self, action):
        self._run_order_category_dialog()

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

    def on_ViewList__change_state(self, action, value):
        action.set_state(value)
        if not value.get_boolean():
            return
        self.ViewKanban.set_state(
            GLib.Variant.new_boolean(not value.get_boolean()))
        self.search.set_result_view(SearchResultListView, refresh=True)
        self._update_list_aware_view()

    def on_ViewKanban__change_state(self, action, value):
        action.set_state(value)
        self.ViewList.set_state(
            GLib.Variant.new_boolean(not value.get_boolean()))
        self.window._current_app_settings['show-kanban'] = value.get_boolean()
        if not value.get_boolean():
            return
        self.search.set_result_view(WorkOrderResultKanbanView, refresh=True)
        self._update_list_aware_view()
예제 #29
0
파일: stock.py 프로젝트: Felipebros/stoq
class StockApp(ShellApp):
    app_title = _('Stock')
    gladefile = "stock"
    search_spec = ProductFullStockView
    search_labels = _('Matching:')
    report_table = SimpleProductReport
    pixbuf_converter = converter.get_converter(GdkPixbuf.Pixbuf)

    #
    # Application
    #

    def create_actions(self):
        group = get_accels('app.stock')
        actions = [
            ("NewReceiving", None, _("Order _receival..."),
             group.get('new_receiving')),
            ('NewTransfer', Gtk.STOCK_CONVERT, _('Transfer...'),
             group.get('transfer_product')),
            ('NewStockDecrease', None, _('Stock decrease...'),
             group.get('stock_decrease')),
            ('StockInitial', Gtk.STOCK_GO_UP, _('Register initial stock...')),
            ("LoanNew", None, _("Loan...")),
            ("LoanClose", None, _("Close loan...")),
            ("SearchPurchaseReceiving", None, _("Received purchases..."),
             group.get('search_receiving'),
             _("Search for received purchase orders")),
            ("SearchProductHistory", None, _("Product history..."),
             group.get('search_product_history'),
             _("Search for product history")),
            ("SearchStockDecrease", None, _("Stock decreases..."), '',
             _("Search for manual stock decreases")),
            ("SearchPurchasedStockItems", None, _("Purchased items..."),
             group.get('search_purchased_stock_items'),
             _("Search for purchased items")),
            ("SearchBrandItems", None, _("Brand items..."),
             group.get('search_brand_items'),
             _("Search for brand items on stock")),
            ("SearchBrandItemsByBranch", None, _("Brand item by branch..."),
             group.get('search_brand_by_branch'),
             _("Search for brand items by branch on stock")),
            ("SearchBatchItems", None, _("Batch items..."),
             group.get('search_batch_items'),
             _("Search for batch items on stock")),
            ("SearchStockItems", None, _("Stock items..."),
             group.get('search_stock_items'), _("Search for items on stock")),
            ("SearchTransfer", None, _("Transfers..."),
             group.get('search_transfers'), _("Search for stock transfers")),
            ("SearchClosedStockItems", None, _("Closed stock Items..."),
             group.get('search_closed_stock_items'),
             _("Search for closed stock items")),
            ("LoanSearch", None, _("Loans...")),
            ("LoanSearchItems", None, _("Loan items...")),
            ("SearchTransferItems", None, _("Transfer items...")),
            ("SearchReturnedItems", None, _("Returned items...")),
            ("SearchPendingReturnedSales", None,
             _("Pending returned sales...")),
            ("ProductMenu", None, _("Product")),
            ("PrintLabels", None, _("Print labels...")),
            ("ManageStock", None, _("Manage stock...")),
            ("ProductStockHistory", Gtk.STOCK_INFO, _("History..."),
             group.get('history'),
             _('Show the stock history of the selected product')),
            ("EditProduct", Gtk.STOCK_EDIT, _("Edit..."),
             group.get('edit_product'),
             _("Edit the selected product, allowing you to change it's "
               "details")),
        ]
        self.stock_ui = self.add_ui_actions(actions)

        toggle_actions = [
            ('StockPictureViewer', None, _('Picture viewer'),
             group.get('toggle_picture_viewer')),
        ]
        self.add_ui_actions(toggle_actions, 'ToggleActions')
        self.set_help_section(_("Stock help"), 'app-stock')

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

        self.window.add_print_items([self.PrintLabels])
        self.window.add_export_items()
        self.window.add_extra_items(
            [self.StockInitial, self.LoanClose, self.StockPictureViewer])
        self.window.add_new_items([
            self.NewReceiving, self.NewTransfer, self.NewStockDecrease,
            self.LoanNew
        ])
        self.window.add_search_items([
            self.SearchPurchaseReceiving,
            self.SearchProductHistory,
            self.SearchTransfer,
            self.SearchTransferItems,
            self.SearchStockDecrease,
            self.SearchReturnedItems,
            self.SearchPurchasedStockItems,
            self.SearchStockItems,
            self.SearchBrandItems,
            self.SearchBrandItemsByBranch,
            self.SearchBatchItems,
            self.SearchClosedStockItems,
            self.SearchPendingReturnedSales,
            # Separator
            self.LoanSearch,
            self.LoanSearchItems,
        ])
        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()
        # FIXME: How are we goint to display an image preview?
        #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())

    def get_domain_options(self):
        options = [
            ('fa-info-circle-symbolic', _('History'),
             'stock.ProductStockHistory', True),
            ('fa-edit-symbolic', _('Edit'), 'stock.EditProduct', True),
            ('', _('Print labels'), 'stock.PrintLabels', False),
            ('', _('Manage stock'), 'stock.ManageStock', False),
        ]

        return options

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

        open_inventory = self.check_open_inventory()

        if not open_inventory:
            self.transfers_bar = self._create_pending_info_message()
            self.returned_bar = self._create_pending_returned_sale_message()
        else:
            self.transfers_bar = None
            self.returned_bar = None

        self._update_widgets()

        self.search.focus_search_entry()

    def deactivate(self):
        if self.transfers_bar:
            self.transfers_bar.hide()
        if self.returned_bar:
            self.returned_bar.hide()

        self._close_image_viewer()

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

    def create_filters(self):
        self.search.set_query(self._query)
        self.set_text_field_columns([
            'description', 'code', 'barcode', 'category_description',
            'manufacturer'
        ])
        branches = Branch.get_active_branches(self.store)
        self.branch_filter = ComboSearchFilter(
            _('Show by:'), api.for_combo(branches, empty=_("All branches")))
        self.branch_filter.select(api.get_current_branch(self.store))
        self.add_filter(self.branch_filter, position=SearchFilterPosition.TOP)

    def get_columns(self):
        return [
            SearchColumn('code',
                         title=_('Code'),
                         sorted=True,
                         sort_func=sort_sellable_code,
                         data_type=str,
                         width=130),
            SearchColumn('barcode',
                         title=_("Barcode"),
                         data_type=str,
                         width=130),
            SearchColumn('category_description',
                         title=_("Category"),
                         data_type=str,
                         width=100,
                         visible=False),
            SearchColumn('description',
                         title=_("Description"),
                         data_type=str,
                         expand=True,
                         ellipsize=Pango.EllipsizeMode.END),
            SearchColumn('manufacturer',
                         title=_("Manufacturer"),
                         data_type=str,
                         visible=False),
            SearchColumn('brand',
                         title=_("Brand"),
                         data_type=str,
                         visible=False),
            SearchColumn('model',
                         title=_("Model"),
                         data_type=str,
                         visible=False),
            SearchColumn('location',
                         title=_("Location"),
                         data_type=str,
                         width=100,
                         visible=False),
            QuantityColumn('stock',
                           title=_('Quantity'),
                           width=100,
                           use_having=True),
            SearchColumn('has_image',
                         title=_('Picture'),
                         data_type=bool,
                         width=80),
        ]

    #
    # Private API
    #

    def _open_image_viewer(self):
        assert self.image_viewer is None

        self.image_viewer = SellableImageViewer(size=(325, 325))
        self.image_viewer.toplevel.connect('delete-event',
                                           self.on_image_viewer_closed)
        self.image_viewer.show_all()

        self._update_widgets()

    def _close_image_viewer(self):
        if self.image_viewer is None:
            return

        self.image_viewer.destroy()
        self.image_viewer = None

    def _query(self, store):
        branch = self.branch_filter.get_state().value
        return self.search_spec.find_by_branch(store, branch)

    def _update_widgets(self):
        branch = api.get_current_branch(self.store)

        is_main_branch = self.branch_filter.get_state().value is branch
        item = self.results.get_selected()

        sellable = item and item.product.sellable
        if sellable:
            if item.has_image:
                # XXX:Workaround for a bug caused by the image domain refactoring
                # which left some existent thumbnail as None
                thumbnail = sellable.image.thumbnail
                if thumbnail is None:
                    # Create new store to create the thumbnail
                    with api.new_store() as new_store:
                        image = sellable.image
                        image = new_store.fetch(image)
                        size = (Image.THUMBNAIL_SIZE_WIDTH,
                                Image.THUMBNAIL_SIZE_HEIGHT)
                        image.thumbnail = get_thumbnail(image.image, size)
                        thumbnail = image.thumbnail
                pixbuf = self.pixbuf_converter.from_string(thumbnail)
            else:
                pixbuf = None

            self._update_edit_image(pixbuf)
            if self.image_viewer:
                self.image_viewer.set_sellable(sellable)
        else:
            self._update_edit_image()

        # Always let the user choose the manage stock option and do a proper
        # check there (showing a warning if he can't)
        self.set_sensitive([self.ManageStock], bool(item))
        self.set_sensitive([self.EditProduct, self.PrintLabels], bool(item))
        self.set_sensitive([self.ProductStockHistory],
                           bool(item) and is_main_branch)
        # We need more than one branch to be able to do transfers
        # Note that 'all branches' is not a real branch
        has_branches = len(self.branch_filter.combo) > 2

        transfer_active = self.NewTransfer.get_enabled()
        self.set_sensitive([self.NewTransfer], transfer_active
                           and has_branches)
        # Building a list of searches that we must disable if there is no
        # branches other than the main company
        searches = [
            self.SearchTransfer, self.SearchTransferItems,
            self.SearchPendingReturnedSales
        ]
        self.set_sensitive(searches, has_branches)

    def _update_edit_image(self, pixbuf=None):
        if not pixbuf:
            self.image.set_from_stock(Gtk.STOCK_EDIT,
                                      Gtk.IconSize.LARGE_TOOLBAR)
            return

        # FIXME: get this icon size from settings
        icon_size = 24
        pixbuf = pixbuf.scale_simple(icon_size, icon_size,
                                     GdkPixbuf.InterpType.BILINEAR)
        self.image.set_from_pixbuf(pixbuf)

    def _update_filter_slave(self, slave):
        self.refresh()

    def _transfer_stock(self):
        if self.check_open_inventory():
            return
        store = api.new_store()
        model = self.run_dialog(StockTransferWizard, store)
        store.confirm(model)
        store.close()
        self.refresh()

    def _receive_purchase(self):
        if self.check_open_inventory():
            return
        store = api.new_store()
        model = self.run_dialog(ReceivingOrderWizard, store)
        store.confirm(model)
        store.close()
        self.refresh()

    def _create_pending_info_message(self):
        branch = api.get_current_branch(self.store)
        n_transfers = TransferOrder.get_pending_transfers(self.store,
                                                          branch).count()

        if not n_transfers:
            return None

        msg = stoqlib_ngettext(_(u"You have %s incoming transfer"),
                               _(u"You have %s incoming transfers"),
                               n_transfers) % n_transfers
        info_bar = self.window.add_info_bar(Gtk.MessageType.QUESTION, msg)
        button = info_bar.add_button(_(u"Receive"), Gtk.ResponseType.OK)
        button.connect('clicked', self._on_info_transfers__clicked)

        return info_bar

    def _create_pending_returned_sale_message(self):
        branch = api.get_current_branch(self.store)
        n_returned = ReturnedSale.get_pending_returned_sales(
            self.store, branch).count()

        if not n_returned:
            return None

        msg = stoqlib_ngettext(_(u"You have %s returned sale to receive"),
                               _(u"You have %s returned sales to receive"),
                               n_returned) % n_returned
        info_returned_bar = self.window.add_info_bar(Gtk.MessageType.QUESTION,
                                                     msg)
        button = info_returned_bar.add_button(_(u"Returned sale"),
                                              Gtk.ResponseType.OK)
        button.connect('clicked', self._on_info_returned_sales__clicked)

        return info_returned_bar

    def _search_transfers(self):
        branch = api.get_current_branch(self.store)
        self.run_dialog(TransferOrderSearch, self.store)

        # After the search is closed we may want to update , or even hide the
        # message, if there is no pending transfer to receive
        if self.transfers_bar:
            n_transfers = TransferOrder.get_pending_transfers(
                self.store, branch).count()

            if n_transfers > 0:
                msg = stoqlib_ngettext(_(u"You have %s incoming transfer"),
                                       _(u"You have %s incoming transfers"),
                                       n_transfers) % n_transfers
                self.transfers_bar.set_message(msg)
            else:
                self.transfers_bar.hide()
        self.refresh()

    def _search_pending_returned_sales(self):
        with api.new_store() as store:
            self.run_dialog(PendingReturnedSaleSearch, store)

        branch = api.get_current_branch(self.store)
        # After the search is closed we may want to update , or even hide the
        # message, if there is no pending returned sale to receive
        if self.returned_bar:
            n_returned = ReturnedSale.get_pending_returned_sales(
                self.store, branch).count()

            if n_returned > 0:
                msg = stoqlib_ngettext(
                    _(u"You have %s returned sale to receive"),
                    _(u"You have %s returned sales to receive"),
                    n_returned) % n_returned
                self.returned_bar.set_message(msg)
            else:
                self.returned_bar.hide()
        self.refresh()

    #
    # Callbacks
    #

    def on_image_viewer_closed(self, window, event):
        self.image_viewer = None
        self.StockPictureViewer.set_state(GLib.Variant.new_boolean(False))

    def on_results__has_rows(self, results, product):
        self._update_widgets()

    def on_results__selection_changed(self, results, product):
        self._update_widgets()

    def on_ProductStockHistory__activate(self, button):
        selected = self.results.get_selected()
        sellable = selected.sellable
        self.run_dialog(ProductStockHistoryDialog,
                        self.store,
                        sellable,
                        branch=self.branch_filter.combo.get_selected())

    def on_ManageStock__activate(self, action):
        user = api.get_current_user(self.store)
        if not user.profile.check_app_permission(u'inventory'):
            return warning(
                _('Only users with access to the inventory app can'
                  ' change the stock quantity'))

        product = self.results.get_selected().product
        if product.is_grid:
            return warning(_("Can't change stock quantity of a grid parent"))

        if product.storable and product.storable.is_batch:
            return warning(
                _("It's not possible to change the stock quantity of"
                  " a batch product"))

        branch = self.branch_filter.combo.get_selected()
        if not branch:
            return warning(_('You must select a branch first'))

        with api.new_store() as store:
            self.run_dialog(ProductStockQuantityEditor,
                            store,
                            store.fetch(product),
                            branch=branch)

        if store.committed:
            self.refresh()

    def on_PrintLabels__activate(self, button):
        selected = self.results.get_selected()
        sellable = selected.sellable
        label_data = self.run_dialog(PrintLabelEditor, None, self.store,
                                     sellable)
        if label_data:
            print_labels(label_data, self.store)

    def on_EditProduct__activate(self, button):
        selected = self.results.get_selected()
        assert selected

        store = api.new_store()
        product = store.fetch(selected.product)

        model = self.run_dialog(ProductStockEditor, store, product)
        store.confirm(model)
        store.close()
        if model:
            self.refresh()

    def _on_info_transfers__clicked(self, button):
        self._search_transfers()

    def _on_info_returned_sales__clicked(self, button):
        self._search_pending_returned_sales()

    # Stock

    def on_NewReceiving__activate(self, button):
        self._receive_purchase()

    def on_NewTransfer__activate(self, button):
        self._transfer_stock()

    def on_NewStockDecrease__activate(self, action):
        if self.check_open_inventory():
            return
        store = api.new_store()
        model = self.run_dialog(StockDecreaseWizard, store)
        store.confirm(model)
        store.close()
        self.refresh()

    def on_StockInitial__activate(self, action):
        if self.check_open_inventory():
            return

        with api.new_store() as store:
            self.run_dialog(InitialStockDialog, store)

        if store.committed:
            self.refresh()

    def on_StockPictureViewer__change_state(self, action, value):
        action.set_state(value)
        if value.get_boolean():
            self._open_image_viewer()
        else:
            self._close_image_viewer()

    # 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()
        self.refresh()

    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()
        self.refresh()

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

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

    # Search

    def on_SearchPurchaseReceiving__activate(self, button):
        self.run_dialog(PurchaseReceivingSearch, self.store)

    def on_SearchTransfer__activate(self, action):
        self._search_transfers()

    def on_SearchTransferItems__activate(self, action):
        self.run_dialog(TransferItemSearch, self.store)

    def on_SearchPendingReturnedSales__activate(self, action):
        self._search_pending_returned_sales()

    def on_SearchReturnedItems__activate(self, action):
        self.run_dialog(ReturnedItemSearch, self.store)

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

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

    def on_SearchBrandItems__activate(self, action):
        self.run_dialog(ProductBrandSearch, self.store)

    def on_SearchBrandItemsByBranch__activate(self, action):
        self.run_dialog(ProductBrandByBranchSearch, self.store)

    def on_SearchBatchItems__activate(self, action):
        self.run_dialog(ProductBatchSearch, self.store)

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

    def on_SearchProductHistory__activate(self, action):
        self.run_dialog(ProductSearchQuantity, self.store)

    def on_SearchStockDecrease__activate(self, action):
        self.run_dialog(StockDecreaseSearch, self.store)
예제 #30
0
 def create_filters(self):
     status_filter = ComboSearchFilter(_('Show employees with status'),
                                       self._get_status_values())
     self.add_filter(status_filter, SearchFilterPosition.TOP, ['status'])