コード例 #1
0
class ProductionItemsSearch(ProductSearch):
    title = _(u"Production Items")
    search_spec = ProductionItemView
    report_class = ProductionItemReport
    csv_data = None
    has_print_price_button = False

    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):
        self.set_text_field_columns(["description", "order_identifier_str"])

        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),
        ]
コード例 #2
0
 def create_filters(self):
     self.set_text_field_columns(["name", "cpf", "rg_number", "phone_number", "mobile_number"])
     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"])
コード例 #3
0
ファイル: personsearch.py プロジェクト: coletivoEITA/stoq
    def create_filters(self):
        items = [(_("Active"), True), (_("Inactive"), False)]
        items.insert(0, (_("Any"), None))

        status_filter = ComboSearchFilter(_("Show transporters with status"), items)
        status_filter.select(None)
        self.add_filter(status_filter, SearchFilterPosition.TOP, ["is_active"])
コード例 #4
0
    def create_filters(self):
        self.set_text_field_columns(["name", "phone_number"])
        items = [(_("Active"), True), (_("Inactive"), False)]
        items.insert(0, (_("Any"), None))

        status_filter = ComboSearchFilter(_("Show transporters with status"), items)
        status_filter.select(None)
        self.add_filter(status_filter, SearchFilterPosition.TOP, ["is_active"])
コード例 #5
0
ファイル: loansearch.py プロジェクト: hackedbellini/stoq
 def create_filters(self):
     # status filter
     statuses = [(desc, i) for i, desc in Loan.statuses.items()]
     statuses.insert(0, (_(u'Any'), None))
     status_filter = ComboSearchFilter(_(u'with status:'), statuses)
     status_filter.select(None)
     self.add_filter(status_filter, columns=['loan_status'],
                     position=SearchFilterPosition.TOP)
コード例 #6
0
ファイル: personsearch.py プロジェクト: coletivoEITA/stoq
 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)
コード例 #7
0
ファイル: servicesearch.py プロジェクト: tmaxter/stoq
 def create_filters(self):
     self.set_text_field_columns(['description', 'barcode'])
     items = [(v, k) for k, v in Sellable.statuses.items()]
     items.insert(0, (_('Any'), None))
     service_filter = ComboSearchFilter(_('Show services'),
                                        items)
     service_filter.select(None)
     self.executer.add_query_callback(self._get_query)
     self.add_filter(service_filter, SearchFilterPosition.TOP, ['status'])
コード例 #8
0
ファイル: loansearch.py プロジェクト: LeonamSilva/stoq
 def create_filters(self):
     self.set_text_field_columns(['description', 'identifier_str'])
     # status filter
     statuses = [(desc, i) for i, desc in Loan.statuses.items()]
     statuses.insert(0, (_(u'Any'), None))
     status_filter = ComboSearchFilter(_(u'with status:'), statuses)
     status_filter.select(None)
     self.add_filter(status_filter, columns=['loan_status'],
                     position=SearchFilterPosition.TOP)
コード例 #9
0
ファイル: personsearch.py プロジェクト: LeonamSilva/stoq
 def create_filters(self):
     self.set_text_field_columns(['name', 'cpf', 'rg_number',
                                  'phone_number', 'mobile_number'])
     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'])
コード例 #10
0
ファイル: personsearch.py プロジェクト: LeonamSilva/stoq
    def create_filters(self):
        self.set_text_field_columns(['name', 'phone_number'])
        items = [(_('Active'), True),
                 (_('Inactive'), False)]
        items.insert(0, (_('Any'), None))

        status_filter = ComboSearchFilter(_('Show transporters with status'),
                                          items)
        status_filter.select(None)
        self.add_filter(status_filter, SearchFilterPosition.TOP, ['is_active'])
コード例 #11
0
ファイル: personsearch.py プロジェクト: tmaxter/stoq
 def create_filters(self):
     self.set_text_field_columns(['name', 'acronym', 'phone_number'])
     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)
     self.executer.add_filter_query_callback(
         status_filter, self._get_status_query)
     self.search.add_filter(status_filter, SearchFilterPosition.TOP)
コード例 #12
0
ファイル: deliverysearch.py プロジェクト: Guillon88/stoq
class DeliverySearch(SearchEditor):
    """Delivery search implementation"""

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

    #
    #  SearchEditor hooks
    #

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

    def create_filters(self):
        self.set_text_field_columns(['tracking_code', 'transporter_name',
                                     'client_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('sale_identifier', title=_('Sale #'),
                                 order=gtk.SORT_DESCENDING),
                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.ELLIPSIZE_END),
                SearchColumn('tracking_code', title=_('Tracking code'),
                             data_type=str),
                SearchColumn('transporter_name', title=_('Transporter'),
                             data_type=str),
                SearchColumn('client_name', title=_('Client'),
                             data_type=str),
                SearchColumn('open_date', title=_('Open date'),
                             data_type=datetime.date, visible=False),
                SearchColumn('deliver_date', title=_('Sent date'),
                             data_type=datetime.date, visible=False),
                SearchColumn('receive_date', title=_('Received date'),
                             data_type=datetime.date, visible=False),
                ]
コード例 #13
0
ファイル: searchdialog.py プロジェクト: Guillon88/stoq
    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
コード例 #14
0
ファイル: productionsearch.py プロジェクト: tmaxter/stoq
class ProductionItemsSearch(SearchDialog):
    title = _(u'Production Items')
    search_table = ProductionItemView
    size = (750, 450)

    #
    # SearchDialog
    #

    def create_filters(self):
        self.set_text_field_columns(['description'])

        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)
        ]

    #
    # Callbacks
    #

    def on_print_button_clicked(self, widget):
        print_report(
            ProductionItemReport,
            self.results,
            list(self.results),
            filters=self.search.get_search_filters(),
        )
コード例 #15
0
ファイル: searchdialog.py プロジェクト: n3zsistemas-bkp/stoq
    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
コード例 #16
0
    def create_filters(self):
        self.set_text_field_columns(['description'])
        self.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'])
コード例 #17
0
ファイル: tillsearch.py プロジェクト: tmaxter/stoq
    def create_filters(self):
        self.set_text_field_columns(['description'])
        self.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'])
コード例 #18
0
ファイル: productionsearch.py プロジェクト: 5l1v3r1/stoq-1
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)
        ]
コード例 #19
0
ファイル: searchdialog.py プロジェクト: rg3915/stoq
    def create_branch_filter(self, label=None):
        from stoqlib.domain.person import Branch

        branches = Branch.get_active_branches(self.store)
        items = [(b.get_description(), b.id) for b in branches]
        items.insert(0, (_("Any"), None))

        if not label:
            label = _("Branch:")
        branch_filter = ComboSearchFilter(label, items)
        current = api.get_current_branch(self.store)
        if current:
            branch_filter.select(current.id)

        return branch_filter
コード例 #20
0
ファイル: searchdialog.py プロジェクト: leandrorchaves/stoq
    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
コード例 #21
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
コード例 #22
0
ファイル: searchdialog.py プロジェクト: pkaislan/stoq
    def create_branch_filter(self, label=None):
        from stoqlib.domain.person import Branch
        current = api.get_current_branch(self.store)
        if api.sysparam.get_bool('SYNCHRONIZED_MODE'):
            items = [(current.get_description(), current.id)]
        else:
            branches = Branch.get_active_branches(self.store)
            items = [(b.get_description(), b.id) for b in branches]
            items.insert(0, (_("Any"), None))

        if not label:
            label = _('Branch:')
        branch_filter = ComboSearchFilter(label, items)
        if current:
            branch_filter.select(current.id)

        return branch_filter
コード例 #23
0
ファイル: searchdialog.py プロジェクト: leandrorchaves/stoq
    def create_branch_filter(self, label=None):
        from stoqlib.domain.person import Branch
        branches = Branch.get_active_branches(self.store)
        items = [(b.person.name, b.id) for b in branches]
        # if not items:
        #    raise ValueError('You should have at least one branch at '
        #                      'this point')
        items.insert(0, (_("Any"), None))

        if not label:
            label = _('Branch:')
        branch_filter = ComboSearchFilter(label, items)
        current = api.get_current_branch(self.store)
        if current:
            branch_filter.select(current.id)

        return branch_filter
コード例 #24
0
    def create_branch_filter(self, label=None):
        from stoqlib.domain.person import Branch
        branches = Branch.get_active_branches(self.store)
        items = [(b.person.name, b.id) for b in branches]
        # if not items:
        #    raise ValueError('You should have at least one branch at '
        #                      'this point')
        items.insert(0, (_("Any"), None))

        if not label:
            label = _('Branch:')
        branch_filter = ComboSearchFilter(label, items)
        current = api.get_current_branch(self.store)
        if current:
            branch_filter.select(current.id)

        return branch_filter
コード例 #25
0
ファイル: personsearch.py プロジェクト: 5l1v3r1/stoq-1
    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()
コード例 #26
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()
コード例 #27
0
ファイル: productsearch.py プロジェクト: leandrorchaves/stoq
    def create_filters(self):
        self.set_text_field_columns(['description', 'barcode',
                                     'category_description'])
        self.search.set_query(self.executer_query)

        # Branch
        branch_filter = self.create_branch_filter(_('In branch:'))
        self.add_filter(branch_filter, columns=[])
        self.branch_filter = branch_filter

        # Status
        statuses = [(desc, id) for id, desc in Sellable.statuses.items()]
        statuses.insert(0, (_('Any'), None))
        status_filter = ComboSearchFilter(_('with status:'), statuses)
        status_filter.select(None)
        self.add_filter(status_filter, columns=['status'],
                        position=SearchFilterPosition.TOP)
        self.status_filter = status_filter
コード例 #28
0
ファイル: productionsearch.py プロジェクト: tmaxter/stoq
class ProductionItemsSearch(SearchDialog):
    title = _(u'Production Items')
    search_table = ProductionItemView
    size = (750, 450)

    #
    # SearchDialog
    #

    def create_filters(self):
        self.set_text_field_columns(['description'])

        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)]

    #
    # Callbacks
    #

    def on_print_button_clicked(self, widget):
        print_report(ProductionItemReport, self.results, list(self.results),
                     filters=self.search.get_search_filters(), )
コード例 #29
0
    def create_filters(self):
        self.set_text_field_columns(
            ['description', 'barcode', 'category_description'])
        self.search.set_query(self.executer_query)

        # Branch
        branch_filter = self.create_branch_filter(_('In branch:'))
        branch_filter.select(None)
        self.add_filter(branch_filter, columns=[])
        self.branch_filter = branch_filter

        # Status
        statuses = [(desc, id) for id, desc in Sellable.statuses.items()]
        statuses.insert(0, (_('Any'), None))
        status_filter = ComboSearchFilter(_('with status:'), statuses)
        status_filter.select(None)
        self.add_filter(status_filter,
                        columns=['status'],
                        position=SearchFilterPosition.TOP)
        self.status_filter = status_filter
コード例 #30
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)]
コード例 #31
0
ファイル: searchslave.py プロジェクト: Guillon88/stoq
    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
コード例 #32
0
ファイル: searchslave.py プロジェクト: n3zsistemas-bkp/stoq
    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
コード例 #33
0
ファイル: inventory.py プロジェクト: 5l1v3r1/stoq-1
class InventoryApp(ShellApp):

    # TODO: Change all widget.set_sensitive to self.set_sensitive([widget])
    app_title = _('Inventory')
    gladefile = "inventory"
    search_spec = Inventory
    search_labels = _('Matching:')
    report_table = InventoryReport

    #
    # Application
    #

    def create_actions(self):
        group = get_accels('app.inventory')
        actions = [
            # File
            ('NewInventory', None, _('Inventory...'),
             group.get('new_inventory'),
             _('Create a new inventory for product counting')),

            # Inventory
            ('Details', Gtk.STOCK_INFO, _('Details...'),
             group.get('inventory_details'),
             _('See details about this inventory')),
            ('CountingAction', Gtk.STOCK_INDEX, _('_Count...'),
             group.get('inventory_count'),
             _('Register the actual stock of products in the selected '
               'inventory')),
            ('AdjustAction', Gtk.STOCK_CONVERT, _('_Adjust...'),
             group.get('inventory_adjust'),
             _('Adjust the stock accordingly to the counting in the selected '
               'inventory')),
            ('Cancel', Gtk.STOCK_CANCEL, _('Cancel...'),
             group.get('inventory_cancel'),
             _('Cancel the selected inventory')),
            ('Export', Gtk.STOCK_SAVE, _('Export for external counting...'),
             None, _('Export the list of products for external counting')),
            ('PrintProductListing', Gtk.STOCK_PRINT,
             _('Print product listing...'), group.get('inventory_print'),
             _('Print the product listing for this inventory'))
        ]
        self.inventory_ui = self.add_ui_actions(actions)
        self.set_help_section(_("Inventory help"), 'app-inventory')

    def create_ui(self):
        self.window.add_new_items([self.NewInventory])
        self.window.add_print_items([self.PrintProductListing])
        self.window.add_export_items([self.Export])

    def activate(self, refresh=True):
        if refresh:
            # Avoid letting this sensitive if has-rows is never emitted
            self.refresh()
        self._update_widgets()

        self.search.focus_search_entry()

    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"])

    def get_domain_options(self):
        options = [
            ('fa-info-circle-symbolic', _('Details'), 'inventory.Details',
             True),
            ('fa-list-ol-symbolic', _('Count'), 'inventory.CountingAction',
             True),
            ('fa-tasks-symbolic', _('Adjust'), 'inventory.AdjustAction', True),
            ('fa-ban-symbolic', _('Cancel'), 'inventory.Cancel', True),
        ]

        return options

    def get_columns(self):
        return [
            IdentifierColumn('identifier',
                             title=_('Inventory #'),
                             sorted=True,
                             order=Gtk.SortType.DESCENDING),
            SearchColumn('status_str',
                         title=_('Status'),
                         data_type=str,
                         width=100,
                         valid_values=self._get_status_values(),
                         search_attribute='status'),
            Column('branch.description',
                   title=_('Branch'),
                   data_type=str,
                   expand=True),
            SearchColumn('open_date',
                         title=_('Opened'),
                         long_title=_('Date Opened'),
                         data_type=datetime.date,
                         width=120),
            SearchColumn('close_date',
                         title=_('Closed'),
                         long_title=_('Date Closed'),
                         data_type=datetime.date,
                         width=120)
        ]

    #
    # Private API
    #

    def _get_status_values(self):
        values = [(v, k) for k, v in Inventory.statuses.items()]
        values.insert(0, (_("Any"), None))
        return values

    def _get_branches(self):
        return self.store.find(Branch)

    def _get_branches_for_filter(self):
        items = [(b.get_description(), b.id) for b in self._get_branches()]
        if not items:
            raise DatabaseInconsistency('You should have at least one '
                                        'branch on your database.'
                                        'Found zero')
        items.insert(0, [_('All branches'), None])
        return items

    def _update_widgets(self):
        has_open = False
        all_counted = False
        has_adjusted = False
        selected = self.results.get_selected()
        if selected:
            all_counted = selected.all_items_counted()
            has_open = selected.is_open()
            has_adjusted = selected.has_adjusted_items()

        self.set_sensitive([self.PrintProductListing, self.Details],
                           bool(selected))
        self.set_sensitive([self.Cancel], has_open and not has_adjusted)
        self.set_sensitive([self.NewInventory], self._can_open())
        self.set_sensitive([self.CountingAction], has_open and not all_counted)
        self.set_sensitive([self.AdjustAction], has_open and all_counted)

    def _can_open(self):
        branch = api.get_current_branch(self.store)
        if Inventory.has_open(self.store, branch):
            return False

        # It doesn't make sense to open an inventory if we don't have any stock
        return self.store.find(ProductStockItem, branch=branch).count() > 0

    def _open_inventory(self):
        with api.new_store() as store:
            rv = self.run_dialog(InventoryOpenEditor, store)

        if rv:
            self.refresh()
            self._update_widgets()

    def _cancel_inventory(self):
        if not yesno(_('Are you sure you want to cancel this inventory ?'),
                     Gtk.ResponseType.NO, _("Cancel inventory"),
                     _("Don't cancel")):
            return

        store = api.new_store()
        inventory = store.fetch(self.results.get_selected())
        inventory.cancel()
        store.commit()
        store.close()
        self.refresh()
        self._update_widgets()

    def _export_product_list(self):
        filename = save(_("Save products file"), self.get_toplevel(),
                        "%s.txt" % (_('products')))
        if not filename:
            return

        tables = [
            Sellable,
            Join(Storable, Storable.id == Sellable.id),
        ]
        sellables = self.store.using(*tables).find(Sellable)
        with open(filename, 'w') as fh:
            for sellable in sellables:
                # TODO: Add a dialog for the user to choose the format for
                # exporting
                fh.write('%-20s%s\n' %
                         (sellable.barcode, sellable.description))

    def _register_product_counting(self):
        store = api.new_store()
        inventory = store.fetch(self.results.get_selected())
        model = self.run_dialog(InventoryCountWizard, store, inventory)
        store.confirm(model)
        store.close()
        self.refresh()
        self._update_widgets()

    def _adjust_product_quantities(self):
        store = api.new_store()
        inventory = store.fetch(self.results.get_selected())
        model = self.run_dialog(InventoryAdjustmentEditor, store, inventory)
        store.confirm(model)
        store.close()
        self.refresh()
        self._update_widgets()

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

    def _get_sellables_by_inventory(self, inventory):
        for item in inventory.get_items():
            yield item.product.sellable

    def _show_inventory_details(self):
        store = api.new_store()
        inventory = store.fetch(self.results.get_selected())
        self.run_dialog(InventoryDetailsDialog, store, inventory)
        store.close()

    #
    # Callbacks
    #

    def on_NewInventory__activate(self, action):
        self._open_inventory()

    def on_CountingAction__activate(self, action):
        self._register_product_counting()

    def on_AdjustAction__activate(self, action):
        self._adjust_product_quantities()

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

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

    def on_results__double_click(self, results, inventory):
        self._show_inventory_details()

    def on_Cancel__activate(self, widget):
        self._cancel_inventory()

    def on_Export__activate(self, widget):
        self._export_product_list()

    def on_PrintProductListing__activate(self, button):
        selected = self.results.get_selected()
        sellables = list(self._get_sellables_by_inventory(selected))
        if not sellables:
            warning(_("No products found in the inventory."))
            return
        self.print_report(ProductCountingReport, sellables)
コード例 #34
0
class TransferOrderSearch(SearchDialog):
    title = _(u"Transfer Order Search")
    size = (750, 500)
    search_spec = TransferOrderView
    selection_mode = gtk.SELECTION_MULTIPLE
    search_by_date = True
    advanced_search = False

    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
        run_dialog(TransferOrderDetailsDialog, self, self.store,
                   transfer_order)

    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 _has_rows(self, results, obj):
        pass

    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', 'receival_date'])

        # Branch
        self.branch_filter = self.create_branch_filter(_('To branch:'))
        self.add_filter(self.branch_filter, columns=['destination_branch_id'])

        # Status
        statuses = self._get_status_values()
        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_columns(self):
        return [
            IdentifierColumn('identifier'),
            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('receival_date',
                         _('Receival Date'),
                         data_type=datetime.date,
                         width=100,
                         visible=False),
            SearchColumn('source_branch_name',
                         _('Source'),
                         data_type=unicode,
                         expand=True),
            SearchColumn('destination_branch_name',
                         _('Destination'),
                         data_type=unicode,
                         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_print_button_clicked(self, button):
        view = self.results.get_selected_rows()[0]
        print_report(TransferOrderReceipt, view.transfer_order)

    def on_details_button_clicked(self, button):
        self._show_transfer_order_details(self.results.get_selected_rows()[0])
コード例 #35
0
ファイル: transfersearch.py プロジェクト: sarkis89/stoq
class TransferOrderSearch(SearchDialog):
    title = _(u"Transfer Order Search")
    size = (750, 500)
    search_spec = TransferOrderView
    report_class = TransferOrderReport
    selection_mode = gtk.SELECTION_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])
コード例 #36
0
ファイル: inventory.py プロジェクト: hackedbellini/stoq
class InventoryApp(ShellApp):

    # TODO: Change all widget.set_sensitive to self.set_sensitive([widget])
    app_title = _('Inventory')
    gladefile = "inventory"
    search_spec = Inventory
    search_labels = _('Matching:')
    report_table = InventoryReport

    #
    # Application
    #

    def create_actions(self):
        group = get_accels('app.inventory')
        actions = [
            # File
            ('NewInventory', None, _('Inventory...'),
             group.get('new_inventory'),
             _('Create a new inventory for product counting')),

            # Inventory
            ('Details', Gtk.STOCK_INFO, _('Details...'),
             group.get('inventory_details'),
             _('See details about this inventory')),
            ('CountingAction', Gtk.STOCK_INDEX, _('_Count...'),
             group.get('inventory_count'),
             _('Register the actual stock of products in the selected '
               'inventory')),
            ('AdjustAction', Gtk.STOCK_CONVERT, _('_Adjust...'),
             group.get('inventory_adjust'),
             _('Adjust the stock accordingly to the counting in the selected '
               'inventory')),
            ('Cancel', Gtk.STOCK_CANCEL, _('Cancel...'),
             group.get('inventory_cancel'),
             _('Cancel the selected inventory')),
            ('Export', Gtk.STOCK_SAVE, _('Export for external counting...'),
             None,
             _('Export the list of products for external counting')),
            ('PrintProductListing', Gtk.STOCK_PRINT,
             _('Print product listing...'),
             group.get('inventory_print'),
             _('Print the product listing for this inventory'))
        ]
        self.inventory_ui = self.add_ui_actions(actions)
        self.set_help_section(_("Inventory help"), 'app-inventory')

    def create_ui(self):
        self.window.add_new_items([self.NewInventory])
        self.window.add_print_items([self.PrintProductListing])
        self.window.add_export_items([self.Export])

    def activate(self, refresh=True):
        if refresh:
            # Avoid letting this sensitive if has-rows is never emitted
            self.refresh()
        self._update_widgets()

        self.search.focus_search_entry()

    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"])

    def get_domain_options(self):
        options = [
            ('fa-info-circle-symbolic', _('Details'), 'inventory.Details', True),
            ('fa-list-ol-symbolic', _('Count'), 'inventory.CountingAction', True),
            ('fa-tasks-symbolic', _('Adjust'), 'inventory.AdjustAction', True),
            ('fa-ban-symbolic', _('Cancel'), 'inventory.Cancel', True),
        ]

        return options

    def get_columns(self):
        return [IdentifierColumn('identifier', title=_('Inventory #'),
                                 sorted=True, order=Gtk.SortType.DESCENDING),
                SearchColumn('status_str', title=_('Status'),
                             data_type=str, width=100,
                             valid_values=self._get_status_values(),
                             search_attribute='status'),
                Column('branch.description', title=_('Branch'),
                       data_type=str, expand=True),
                SearchColumn('open_date', title=_('Opened'),
                             long_title=_('Date Opened'),
                             data_type=datetime.date, width=120),
                SearchColumn('close_date', title=_('Closed'),
                             long_title=_('Date Closed'),
                             data_type=datetime.date, width=120)]

    #
    # Private API
    #

    def _get_status_values(self):
        values = [(v, k) for k, v in Inventory.statuses.items()]
        values.insert(0, (_("Any"), None))
        return values

    def _get_branches(self):
        return self.store.find(Branch)

    def _get_branches_for_filter(self):
        items = [(b.get_description(), b.id) for b in self._get_branches()]
        if not items:
            raise DatabaseInconsistency('You should have at least one '
                                        'branch on your database.'
                                        'Found zero')
        items.insert(0, [_('All branches'), None])
        return items

    def _update_widgets(self):
        has_open = False
        all_counted = False
        has_adjusted = False
        selected = self.results.get_selected()
        if selected:
            all_counted = selected.all_items_counted()
            has_open = selected.is_open()
            has_adjusted = selected.has_adjusted_items()

        self.set_sensitive([self.PrintProductListing, self.Details],
                           bool(selected))
        self.set_sensitive([self.Cancel], has_open and not has_adjusted)
        self.set_sensitive([self.NewInventory], self._can_open())
        self.set_sensitive([self.CountingAction], has_open and not all_counted)
        self.set_sensitive([self.AdjustAction], has_open and all_counted)

    def _can_open(self):
        branch = api.get_current_branch(self.store)
        if Inventory.has_open(self.store, branch):
            return False

        # It doesn't make sense to open an inventory if we don't have any stock
        return self.store.find(ProductStockItem, branch=branch).count() > 0

    def _open_inventory(self):
        with api.new_store() as store:
            rv = self.run_dialog(InventoryOpenEditor, store)

        if rv:
            self.refresh()
            self._update_widgets()

    def _cancel_inventory(self):
        if not yesno(_('Are you sure you want to cancel this inventory ?'),
                     Gtk.ResponseType.NO, _("Cancel inventory"), _("Don't cancel")):
            return

        store = api.new_store()
        inventory = store.fetch(self.results.get_selected())
        inventory.cancel()
        store.commit()
        store.close()
        self.refresh()
        self._update_widgets()

    def _export_product_list(self):
        filename = save(_("Save products file"), self.get_toplevel(),
                        "%s.txt" % (_('products')))
        if not filename:
            return

        tables = [
            Sellable,
            Join(Storable, Storable.id == Sellable.id),
        ]
        sellables = self.store.using(*tables).find(Sellable)
        with open(filename, 'w') as fh:
            for sellable in sellables:
                # TODO: Add a dialog for the user to choose the format for
                # exporting
                fh.write('%-20s%s\n' % (sellable.barcode, sellable.description))

    def _register_product_counting(self):
        store = api.new_store()
        inventory = store.fetch(self.results.get_selected())
        model = self.run_dialog(InventoryCountWizard, store, inventory)
        store.confirm(model)
        store.close()
        self.refresh()
        self._update_widgets()

    def _adjust_product_quantities(self):
        store = api.new_store()
        inventory = store.fetch(self.results.get_selected())
        model = self.run_dialog(InventoryAdjustmentEditor, store, inventory)
        store.confirm(model)
        store.close()
        self.refresh()
        self._update_widgets()

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

    def _get_sellables_by_inventory(self, inventory):
        for item in inventory.get_items():
            yield item.product.sellable

    def _show_inventory_details(self):
        store = api.new_store()
        inventory = store.fetch(self.results.get_selected())
        self.run_dialog(InventoryDetailsDialog, store, inventory)
        store.close()

    #
    # Callbacks
    #

    def on_NewInventory__activate(self, action):
        self._open_inventory()

    def on_CountingAction__activate(self, action):
        self._register_product_counting()

    def on_AdjustAction__activate(self, action):
        self._adjust_product_quantities()

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

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

    def on_results__double_click(self, results, inventory):
        self._show_inventory_details()

    def on_Cancel__activate(self, widget):
        self._cancel_inventory()

    def on_Export__activate(self, widget):
        self._export_product_list()

    def on_PrintProductListing__activate(self, button):
        selected = self.results.get_selected()
        sellables = list(self._get_sellables_by_inventory(selected))
        if not sellables:
            warning(_("No products found in the inventory."))
            return
        self.print_report(ProductCountingReport, sellables)
コード例 #37
0
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", STOQ_RECEIVING, _("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,
                                            filename='stock.xml')

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

        self.NewReceiving.set_short_label(_("Receive"))
        self.NewTransfer.set_short_label(_("Transfer"))
        self.EditProduct.set_short_label(_("Edit"))
        self.ProductStockHistory.set_short_label(_("History"))
        self.EditProduct.props.is_important = True
        self.ProductStockHistory.props.is_important = True

    def create_ui(self):
        if api.sysparam.get_bool('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.SearchBrandItems,
            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())

    def activate(self, refresh=True):
        self.window.NewToolItem.set_tooltip(
            _("Create a new receiving order"))
        self.window.SearchToolItem.set_tooltip(
            _("Search for stock items"))

        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.uimanager.remove_ui(self.stock_ui)
        self._close_image_viewer()

    def new_activate(self):
        if not self.NewReceiving.get_sensitive():
            warning(_("You cannot receive a purchase with an open inventory."))
            return
        self._receive_purchase()

    def search_activate(self):
        self.run_dialog(ProductStockSearch, self.store)

    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_sensitive()
        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_active(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_results__right_click(self, results, result, event):
        self.popup.popup(None, None, None, None,
                         event.button.button, event.time)

    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__toggled(self, button):
        if button.get_active():
            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)
コード例 #38
0
ファイル: stock.py プロジェクト: tmaxter/stoq
class StockApp(ShellApp):
    app_title = _('Stock')
    gladefile = "stock"
    search_table = ProductFullStockView
    search_labels = _('Matching:')
    report_table = SimpleProductReport
    pixbuf_converter = converter.get_converter(gtk.gdk.Pixbuf)

    #
    # Application
    #

    def create_actions(self):
        group = get_accels('app.stock')
        actions = [
            ("NewReceiving", STOQ_RECEIVING, _("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")),
            ("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...")),
            ("ProductMenu", None, _("Product")),
            ("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,
                                            filename='stock.xml')

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

        self.NewReceiving.set_short_label(_("Receive"))
        self.NewTransfer.set_short_label(_("Transfer"))
        self.EditProduct.set_short_label(_("Edit"))
        self.ProductStockHistory.set_short_label(_("History"))
        self.EditProduct.props.is_important = True
        self.ProductStockHistory.props.is_important = True

    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())

    def activate(self, params):
        self.window.NewToolItem.set_tooltip(
            _("Create a new receiving order"))
        self.window.SearchToolItem.set_tooltip(
            _("Search for stock items"))

        self.check_open_inventory()
        self._update_widgets()

    def setup_focus(self):
        self.search.refresh()

    def deactivate(self):
        self.uimanager.remove_ui(self.stock_ui)

    def new_activate(self):
        if not self.NewReceiving.get_sensitive():
            warning(_("You cannot receive a purchase with an open inventory."))
            return
        self._receive_purchase()

    def search_activate(self):
        self.run_dialog(ProductStockSearch, self.store)

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

    def create_filters(self):
        self.executer.set_query(self._query)
        self.set_text_field_columns(['description'])
        self.branch_filter = ComboSearchFilter(
            _('Show by:'), self._get_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.ELLIPSIZE_END),
                SearchColumn('manufacturer', title=_("Manufacturer"),
                             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),
                SearchColumn('stock', title=_('Quantity'),
                             data_type=decimal.Decimal, width=100),
                SearchColumn('unit', title=_("Unit"), data_type=str,
                             width=40, visible=False),
                Column('has_image', title=_('Picture'),
                       data_type=bool, width=80),
                ]

    #
    # Private API
    #

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

    def _get_branches(self):
        items = [(b.person.name, b)
                 for b in self.store.find(Branch)]
        if not items:
            raise DatabaseInconsistency('You should have at least one '
                                        'branch on your database.'
                                        'Found zero')
        items.insert(0, [_('All branches'), None])
        return items

    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 sellable.has_image:
                thumbnail = sellable.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()

        self.set_sensitive([self.EditProduct], 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_sensitive()
        self.set_sensitive([self.NewTransfer],
                           transfer_active and has_branches)
        self.set_sensitive([self.SearchTransfer], has_branches)

    def _update_edit_image(self, pixbuf=None):
        if not pixbuf:
            self.image.set_from_stock(gtk.STOCK_EDIT,
                                      gtk.ICON_SIZE_LARGE_TOOLBAR)
            return

        # FIXME: get this icon size from settings
        icon_size = 24
        pixbuf = pixbuf.scale_simple(icon_size, icon_size,
                                     gtk.gdk.INTERP_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.search.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.search.refresh()

    #
    # Callbacks
    #

    def on_image_viewer_closed(self, window, event):
        self.StockPictureViewer.props.active = False
        self.image_viewer = None

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

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

    def on_results__right_click(self, results, result, event):
        self.popup.popup(None, None, None, event.button, event.time)

    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_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()

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

    def on_StockInitial__activate(self, action):
        if self.check_open_inventory():
            return
        branch = self.branch_filter.get_state().value
        store = api.new_store()
        retval = self.run_dialog(InitialStockDialog, store, branch)
        store.confirm(retval)
        store.close()
        self.search.refresh()

    def on_StockPictureViewer__activate(self, button):
        if self.image_viewer:
            self.StockPictureViewer.props.active = False
            self.image_viewer.destroy()
            self.image_viewer = None
        else:
            self.StockPictureViewer.props.active = True
            self.image_viewer = SellableImageViewer()
            selected = self.results.get_selected()
            if selected:
                self.image_viewer.set_sellable(selected.product.sellable)
            self.image_viewer.toplevel.connect(
                "delete-event", self.on_image_viewer_closed)
            self.image_viewer.toplevel.set_property("visible", True)

    # 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.search.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.search.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.run_dialog(TransferOrderSearch, 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_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)
コード例 #39
0
class TillApp(ShellApp):

    app_title = _(u'Till')
    gladefile = 'till'
    search_spec = SaleView
    search_labels = _(u'matching:')
    report_table = SalesReport

    #
    # Application
    #

    def create_actions(self):
        group = get_accels('app.till')
        actions = [
            ('SaleMenu', None, _('Sale')),
            ('TillOpen', None, _('Open till...'), group.get('open_till')),
            ('TillClose', None, _('Close till...'), group.get('close_till')),
            ('TillVerify', None, _('Verify till...'),
             group.get('verify_till')),
            ("TillDailyMovement", None, _("Till daily movement..."),
             group.get('daily_movement')),
            ('TillAddCash', None, _('Cash addition...'), ''),
            ('TillRemoveCash', None, _('Cash removal...'), ''),
            ("PaymentReceive", None, _("Payment receival..."),
             group.get('payment_receive'), _("Receive payments")),
            ("SearchClient", None, _("Clients..."),
             group.get('search_clients'), _("Search for clients")),
            ("SearchSale", None, _("Sales..."), group.get('search_sale'),
             _("Search for sales")),
            ("SearchCardPayment", None, _("Card payments..."),
             _("Search for card payments")),
            ("SearchSoldItemsByBranch", None, _("Sold items by branch..."),
             group.get('search_sold_items_by_branch'),
             _("Search for items sold by branch")),
            ("SearchTillHistory", None, _("Till history..."),
             group.get('search_till_history'), _("Search for till history")),
            ("SearchFiscalTillOperations", None,
             _("Fiscal till operations..."),
             group.get('search_fiscal_till_operations'),
             _("Search for fiscal till operations")),
            ("Confirm", gtk.STOCK_APPLY, _("Confirm..."),
             group.get('confirm_sale'),
             _("Confirm the selected sale, decreasing stock and making it "
               "possible to receive it's payments")),
            ("Return", gtk.STOCK_CANCEL, _("Return..."),
             group.get('return_sale'),
             _("Return the selected sale, returning stock and the client's "
               "payments")),
            ("Details", gtk.STOCK_INFO, _("Details..."),
             group.get('sale_details'),
             _("Show details of the selected sale")),
        ]

        self.till_ui = self.add_ui_actions('', actions, filename="till.xml")
        self.set_help_section(_("Till help"), 'app-till')

        self.Confirm.set_short_label(_('Confirm'))
        self.Return.set_short_label(_('Return'))
        self.Details.set_short_label(_('Details'))
        self.Confirm.props.is_important = True
        self.Return.props.is_important = True
        self.Details.props.is_important = True

    def create_ui(self):
        self.popup = self.uimanager.get_widget('/TillSelection')

        self.current_branch = api.get_current_branch(self.store)
        # Groups
        self.main_vbox.set_focus_chain([self.app_vbox])
        self.app_vbox.set_focus_chain([self.search_holder, self.list_vbox])

        # Setting up the toolbar
        self.list_vbox.set_focus_chain([self.footer_hbox])
        self._setup_printer()
        self._setup_widgets()

    def get_title(self):
        return _('[%s] - Till') % (api.get_current_branch(
            self.store).get_description(), )

    def activate(self, refresh=True):
        self.window.add_new_items([self.TillAddCash, self.TillRemoveCash])
        self.window.add_search_items([
            self.SearchFiscalTillOperations, self.SearchClient, self.SearchSale
        ])
        self.window.Print.set_tooltip(_("Print a report of these sales"))
        self.refresh()
        self._printer.run_initial_checks()
        self.check_open_inventory()

    def deactivate(self):
        self.uimanager.remove_ui(self.till_ui)

    def new_activate(self):
        if not self.TillAddCash.get_sensitive():
            return
        self._run_add_cash_dialog()

    def search_activate(self):
        self._run_search_dialog(TillFiscalOperationsSearch)

    #
    # ShellApp
    #

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

    def create_filters(self):
        self.search.set_query(self._query_executer)
        self.set_text_field_columns(
            ['client_name', 'salesperson_name', 'identifier_str'])
        self.status_filter = ComboSearchFilter(_(u"Show orders"),
                                               self._get_status_values())
        self.status_filter.select(Sale.STATUS_CONFIRMED)
        self.add_filter(self.status_filter,
                        position=SearchFilterPosition.TOP,
                        columns=['status'])

    def get_columns(self):
        return [
            IdentifierColumn('identifier',
                             long_title=_('Order #'),
                             sorted=True),
            Column('status_name',
                   title=_(u'Status'),
                   data_type=str,
                   visible=False),
            SearchColumn('open_date',
                         title=_('Date Started'),
                         width=110,
                         data_type=date,
                         justify=gtk.JUSTIFY_RIGHT),
            SearchColumn('client_name',
                         title=_('Client'),
                         data_type=str,
                         expand=True,
                         ellipsize=pango.ELLIPSIZE_END),
            SearchColumn('salesperson_name',
                         title=_('Salesperson'),
                         data_type=str,
                         width=180,
                         ellipsize=pango.ELLIPSIZE_END),
            SearchColumn('total_quantity',
                         title=_('Quantity'),
                         data_type=decimal.Decimal,
                         width=100,
                         format_func=format_quantity),
            SearchColumn('total',
                         title=_('Total'),
                         data_type=currency,
                         width=100)
        ]

    #
    # Private
    #

    def _query_executer(self, store):
        # We should only show Sales that
        # 1) In the current branch (FIXME: Should be on the same station.
        # See bug 4266)
        # 2) Are in the status QUOTE or ORDERED.
        # 3) For the order statuses, the date should be the same as today

        query = And(
            Sale.branch == self.current_branch,
            Or(Sale.status == Sale.STATUS_QUOTE,
               Sale.status == Sale.STATUS_ORDERED,
               Date(Sale.open_date) == date.today()))

        return store.find(self.search_spec, query)

    def _setup_printer(self):
        self._printer = FiscalPrinterHelper(self.store, parent=self)
        self._printer.connect('till-status-changed',
                              self._on_PrinterHelper__till_status_changed)
        self._printer.connect('ecf-changed',
                              self._on_PrinterHelper__ecf_changed)
        self._printer.setup_midnight_check()

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

    def _create_sale_payments(self, order_view):
        store = api.new_store()
        # TODO: Maybe change the sale status to ORDERED
        sale = store.fetch(order_view.sale)
        retval = run_dialog(SalePaymentsEditor, self, store, sale)

        if store.confirm(retval):
            self.refresh()
        store.close()

    def _confirm_order(self, order_view):
        if self.check_open_inventory():
            return

        store = api.new_store()
        sale = store.fetch(order_view.sale)
        expire_date = sale.expire_date

        if (sale.status == Sale.STATUS_QUOTE and expire_date
                and expire_date.date() < date.today() and not yesno(
                    _("This quote has expired. Confirm it anyway?"),
                    gtk.RESPONSE_YES, _("Confirm quote"), _("Don't confirm"))):
            store.close()
            return

        missing = get_missing_items(sale, store)

        if missing:
            retval = run_dialog(MissingItemsDialog, self, sale, missing)
            if retval:
                self.refresh()
            store.close()
            return

        coupon = self._open_coupon()
        if not coupon:
            store.close()
            return
        subtotal = self._add_sale_items(sale, coupon)
        try:
            if coupon.confirm(sale, store, subtotal=subtotal):
                workorders = WorkOrder.find_by_sale(store, sale)
                for order in workorders:
                    order.close()
                store.commit()
                self.refresh()
            else:
                coupon.cancel()
        except SellError as err:
            warning(str(err))
        except ModelDataError as err:
            warning(str(err))

        store.close()

    def _open_coupon(self):
        coupon = self._printer.create_coupon()

        if coupon:
            while not coupon.open():
                if not yesno(
                        _("Failed to open the fiscal coupon.\n"
                          "Until it is opened, it's not possible to "
                          "confirm the sale. Do you want to try again?"),
                        gtk.RESPONSE_YES, _("Try again"), _("Cancel coupon")):
                    return None

        return coupon

    def _add_sale_items(self, sale, coupon):
        subtotal = 0
        for sale_item in sale.get_items():
            coupon.add_item(sale_item)
            subtotal += sale_item.price * sale_item.quantity
        return subtotal

    def _update_total(self):
        balance = currency(self._get_till_balance())
        text = _(u"Total: %s") % converter.as_string(currency, balance)
        self.total_label.set_text(text)

    def _get_till_balance(self):
        """Returns the balance of till operations"""
        try:
            till = Till.get_current(self.store)
        except TillError:
            till = None

        if till is None:
            return currency(0)

        return till.get_balance()

    def _setup_widgets(self):
        # SearchSale is here because it's possible to return a sale inside it
        self._inventory_widgets = [self.Confirm, self.SearchSale, self.Return]
        self.register_sensitive_group(self._inventory_widgets,
                                      lambda: not self.has_open_inventory())

        self.total_label.set_size('xx-large')
        self.total_label.set_bold(True)

        self.till_status_label.set_size('xx-large')
        self.till_status_label.set_bold(True)

    def _update_toolbar_buttons(self):
        sale_view = self.results.get_selected()
        if sale_view:
            can_confirm = sale_view.can_confirm()
            # when confirming sales in till, we also might want to cancel
            # sales
            can_return = (sale_view.can_return() or sale_view.can_cancel())
        else:
            can_confirm = can_return = False

        self.set_sensitive([self.Details], bool(sale_view))
        self.set_sensitive([self.Confirm], can_confirm)
        self.set_sensitive([self.Return], can_return)

    def _check_selected(self):
        sale_view = self.results.get_selected()
        if not sale_view:
            raise StoqlibError("You should have a selected item at "
                               "this point")
        return sale_view

    def _run_search_dialog(self, dialog_type, **kwargs):
        store = api.new_store()
        self.run_dialog(dialog_type, store, **kwargs)
        store.close()

    def _run_details_dialog(self):
        sale_view = self._check_selected()
        run_dialog(SaleDetailsDialog, self, self.store, sale_view)

    def _run_add_cash_dialog(self):
        with api.trans() as store:
            try:
                run_dialog(CashInEditor, self, store)
            except TillError as err:
                # Inform the error to the user instead of crashing
                warning(str(err))
                return

        if store.committed:
            self._update_total()

    def _return_sale(self):
        if self.check_open_inventory():
            return

        sale_view = self._check_selected()

        with api.trans() as store:
            return_sale(self.get_toplevel(), store.fetch(sale_view.sale),
                        store)

        if store.committed:
            self._update_total()
            self.refresh()

    def _update_ecf(self, has_ecf):
        # If we have an ecf, let the other events decide what to disable.
        if has_ecf:
            return

        # We dont have an ecf. Disable till related operations
        widgets = [
            self.TillOpen, self.TillClose, self.TillVerify, self.TillAddCash,
            self.TillRemoveCash, self.SearchTillHistory, self.app_vbox,
            self.Confirm, self.Return, self.Details
        ]
        self.set_sensitive(widgets, has_ecf)
        text = _(u"Till operations requires a connected fiscal printer")
        self.till_status_label.set_text(text)

    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()

    #
    # Callbacks
    #

    def on_Confirm__activate(self, action):
        selected = self.results.get_selected()
        query = WorkOrder.status != WorkOrder.STATUS_WORK_FINISHED

        # If there are unfinished workorders associated with the sale, we
        # cannot print the coupon yet. Instead lets just create the payments.
        unfinished = WorkOrder.find_by_sale(self.store,
                                            selected.sale).find(query)
        if not unfinished.is_empty():
            self._create_sale_payments(selected)
        else:
            self._confirm_order(selected)
        self._update_total()

    def on_results__double_click(self, results, sale):
        self._run_details_dialog()

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

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

    def on_results__right_click(self, results, result, event):
        self.popup.popup(None, None, None, event.button, event.time)

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

    def on_Return__activate(self, action):
        self._return_sale()

    def _on_PrinterHelper__till_status_changed(self, printer, closed, blocked):
        self._update_till_status(closed, blocked)

    def _on_PrinterHelper__ecf_changed(self, printer, ecf):
        self._update_ecf(ecf)

    def on_PaymentReceive__activate(self, action):
        self.run_dialog(PaymentReceivingSearch, self.store)

    # Till

    def on_TillVerify__activate(self, button):
        self._printer.verify_till()

    def on_TillClose__activate(self, button):
        self._printer.close_till()

    def on_TillOpen__activate(self, button):
        self._printer.open_till()

    def on_TillAddCash__activate(self, action):
        self._run_add_cash_dialog()

    def on_TillRemoveCash__activate(self, action):
        with api.trans() as store:
            run_dialog(CashOutEditor, self, store)
        if store.committed:
            self._update_total()

    def on_TillDailyMovement__activate(self, button):
        today = localtoday()
        print_report(TillDailyMovementReport, self.store, today)

    # Search

    def on_SearchClient__activate(self, action):
        self._run_search_dialog(ClientSearch, hide_footer=True)

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

        self._run_search_dialog(SaleWithToolbarSearch)
        self.refresh()

    def on_SearchCardPayment__activate(self, action):
        self.run_dialog(CardPaymentSearch, self.store)

    def on_SearchSoldItemsByBranch__activate(self, button):
        self._run_search_dialog(SoldItemsByBranchSearch)

    def on_SearchTillHistory__activate(self, button):
        self.run_dialog(TillHistoryDialog, self.store)

    def on_SearchFiscalTillOperations__activate(self, button):
        self._run_search_dialog(TillFiscalOperationsSearch)
コード例 #40
0
ファイル: personsearch.py プロジェクト: coletivoEITA/stoq
 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"])
コード例 #41
0
ファイル: till.py プロジェクト: marianaanselmo/stoq
class TillApp(ShellApp):

    app_title = _(u'Till')
    gladefile = 'till'
    search_spec = SaleView
    search_labels = _(u'matching:')
    report_table = SalesReport

    #
    # Application
    #

    def create_actions(self):
        group = get_accels('app.till')
        actions = [
            ('SaleMenu', None, _('Sale')),
            ('TillOpen', None, _('Open till...'),
             group.get('open_till')),
            ('TillClose', None, _('Close till...'),
             group.get('close_till')),
            ('TillVerify', None, _('Verify till...'),
             group.get('verify_till')),
            ('TillAddCash', None, _('Cash addition...'), ''),
            ('TillRemoveCash', None, _('Cash removal...'), ''),
            ("PaymentReceive", None, _("Payment receival..."),
             group.get('payment_receive'),
             _("Receive payments")),
            ("SearchClient", None, _("Clients..."),
             group.get('search_clients'),
             _("Search for clients")),
            ("SearchSale", None, _("Sales..."),
             group.get('search_sale'),
             _("Search for sales")),
            ("SearchSoldItemsByBranch", None, _("Sold items by branch..."),
             group.get('search_sold_items_by_branch'),
             _("Search for items sold by branch")),
            ("SearchTillHistory", None, _("Till history..."),
             group.get('search_till_history'),
             _("Search for till history")),
            ("SearchFiscalTillOperations", None, _("Fiscal till operations..."),
             group.get('search_fiscal_till_operations'),
             _("Search for fiscal till operations")),
            ("Confirm", gtk.STOCK_APPLY, _("Confirm..."),
             group.get('confirm_sale'),
             _("Confirm the selected sale, decreasing stock and making it "
               "possible to receive it's payments")),
            ("Return", gtk.STOCK_CANCEL, _("Return..."),
             group.get('return_sale'),
             _("Return the selected sale, returning stock and the client's "
               "payments")),
            ("Details", gtk.STOCK_INFO, _("Details..."),
             group.get('sale_details'),
             _("Show details of the selected sale")),
        ]

        self.till_ui = self.add_ui_actions('', actions,
                                           filename="till.xml")
        self.set_help_section(_("Till help"), 'app-till')

        self.Confirm.set_short_label(_('Confirm'))
        self.Return.set_short_label(_('Return'))
        self.Details.set_short_label(_('Details'))
        self.Confirm.props.is_important = True
        self.Return.props.is_important = True
        self.Details.props.is_important = True

    def create_ui(self):
        self.popup = self.uimanager.get_widget('/TillSelection')

        self.current_branch = api.get_current_branch(self.store)
        # Groups
        self.main_vbox.set_focus_chain([self.app_vbox])
        self.app_vbox.set_focus_chain([self.search_holder, self.list_vbox])

        # Setting up the toolbar
        self.list_vbox.set_focus_chain([self.footer_hbox])
        self._setup_printer()
        self._setup_widgets()

    def get_title(self):
        return _('Stoq - Till for Branch %03d') % (
            api.get_current_branch(self.store).id, )

    def activate(self, params):
        self.window.add_new_items([self.TillAddCash,
                                   self.TillRemoveCash])
        self.window.add_search_items([self.SearchFiscalTillOperations,
                                      self.SearchClient,
                                      self.SearchSale])
        self.window.Print.set_tooltip(_("Print a report of these sales"))
        self.refresh()
        self._printer.run_initial_checks()
        self.check_open_inventory()

    def deactivate(self):
        self.uimanager.remove_ui(self.till_ui)

    def new_activate(self):
        if not self.TillAddCash.get_sensitive():
            return
        self._run_add_cash_dialog()

    def search_activate(self):
        self._run_search_dialog(TillFiscalOperationsSearch)

    #
    # ShellApp
    #

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

    def create_filters(self):
        self.search.set_query(self._query_executer)
        self.set_text_field_columns(['client_name', 'salesperson_name'])
        self.status_filter = ComboSearchFilter(_(u"Show orders"),
                                               self._get_status_values())
        self.status_filter.select(Sale.STATUS_CONFIRMED)
        self.add_filter(self.status_filter, position=SearchFilterPosition.TOP,
                        columns=['status'])

    def get_columns(self):
        return [IdentifierColumn('identifier', long_title=_('Order #'),
                                 sorted=True),
                Column('status_name', title=_(u'Status'), data_type=str,
                       visible=False),
                SearchColumn('open_date', title=_('Date Started'), width=110,
                             data_type=date, justify=gtk.JUSTIFY_RIGHT),
                SearchColumn('client_name', title=_('Client'),
                             data_type=str, expand=True,
                             ellipsize=pango.ELLIPSIZE_END),
                SearchColumn('salesperson_name', title=_('Salesperson'),
                             data_type=str, width=180,
                             ellipsize=pango.ELLIPSIZE_END),
                SearchColumn('total_quantity', title=_('Quantity'),
                             data_type=decimal.Decimal, width=100,
                             format_func=format_quantity),
                SearchColumn('total', title=_('Total'), data_type=currency,
                             width=100)]

    #
    # Private
    #

    def _query_executer(self, store):
        # We should only show Sales that
        # 1) In the current branch (FIXME: Should be on the same station.
                                    # See bug 4266)
        # 2) Are in the status QUOTE or ORDERED.
        # 3) For the order statuses, the date should be the same as today

        query = And(Sale.branch == self.current_branch,
                    Or(Sale.status == Sale.STATUS_QUOTE,
                       Sale.status == Sale.STATUS_ORDERED,
                       Date(Sale.open_date) == date.today()))

        return store.find(self.search_spec, query)

    def _setup_printer(self):
        self._printer = FiscalPrinterHelper(self.store,
                                            parent=self)
        self._printer.connect('till-status-changed',
                              self._on_PrinterHelper__till_status_changed)
        self._printer.connect('ecf-changed',
                              self._on_PrinterHelper__ecf_changed)
        self._printer.setup_midnight_check()

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

    def _confirm_order(self):
        if self.check_open_inventory():
            return

        store = api.new_store()
        selected = self.results.get_selected()
        sale = store.fetch(selected.sale)
        expire_date = sale.expire_date

        if (sale.status == Sale.STATUS_QUOTE and
            expire_date and expire_date.date() < date.today() and
            not yesno(_("This quote has expired. Confirm it anyway?"),
                      gtk.RESPONSE_YES,
                      _("Confirm quote"), _("Don't confirm"))):
            store.close()
            return

        missing = get_missing_items(sale, store)

        if missing:
            retval = run_dialog(MissingItemsDialog, self, sale, missing)
            if retval:
                self.refresh()
            store.close()
            return

        coupon = self._open_coupon()
        if not coupon:
            store.close()
            return
        subtotal = self._add_sale_items(sale, coupon)
        try:
            if coupon.confirm(sale, store, subtotal=subtotal):
                store.commit()
                self.refresh()
            else:
                coupon.cancel()
        except SellError as err:
            warning(str(err))
        except ModelDataError as err:
            warning(str(err))

        store.close()

    def _open_coupon(self):
        coupon = self._printer.create_coupon()

        if coupon:
            while not coupon.open():
                if not yesno(_("Failed to open the fiscal coupon.\n"
                               "Until it is opened, it's not possible to "
                               "confirm the sale. Do you want to try again?"),
                             gtk.RESPONSE_YES, _("Try again"), _("Cancel coupon")):
                    return None

        return coupon

    def _add_sale_items(self, sale, coupon):
        subtotal = 0
        for sale_item in sale.get_items():
            coupon.add_item(sale_item)
            subtotal += sale_item.price * sale_item.quantity
        return subtotal

    def _update_total(self):
        balance = currency(self._get_till_balance())
        text = _(u"Total: %s") % converter.as_string(currency, balance)
        self.total_label.set_text(text)

    def _get_till_balance(self):
        """Returns the balance of till operations"""
        try:
            till = Till.get_current(self.store)
        except TillError:
            till = None

        if till is None:
            return currency(0)

        return till.get_balance()

    def _setup_widgets(self):
        # SearchSale is here because it's possible to return a sale inside it
        self._inventory_widgets = [self.Confirm, self.SearchSale,
                                   self.Return]
        self.register_sensitive_group(self._inventory_widgets,
                                      lambda: not self.has_open_inventory())

        self.total_label.set_size('xx-large')
        self.total_label.set_bold(True)

        self.till_status_label.set_size('xx-large')
        self.till_status_label.set_bold(True)

    def _update_toolbar_buttons(self):
        sale_view = self.results.get_selected()
        if sale_view:
            can_confirm = sale_view.can_confirm()
            # when confirming sales in till, we also might want to cancel
            # sales
            can_return = (sale_view.can_return() or
                          sale_view.can_cancel())
        else:
            can_confirm = can_return = False

        self.set_sensitive([self.Details], bool(sale_view))
        self.set_sensitive([self.Confirm], can_confirm)
        self.set_sensitive([self.Return], can_return)

    def _check_selected(self):
        sale_view = self.results.get_selected()
        if not sale_view:
            raise StoqlibError("You should have a selected item at "
                               "this point")
        return sale_view

    def _run_search_dialog(self, dialog_type, **kwargs):
        store = api.new_store()
        self.run_dialog(dialog_type, store, **kwargs)
        store.close()

    def _run_details_dialog(self):
        sale_view = self._check_selected()
        run_dialog(SaleDetailsDialog, self, self.store, sale_view)

    def _run_add_cash_dialog(self):
        with api.trans() as store:
            try:
                run_dialog(CashInEditor, self, store)
            except TillError as err:
                # Inform the error to the user instead of crashing
                warning(str(err))
                return

        if store.committed:
            self._update_total()

    def _return_sale(self):
        if self.check_open_inventory():
            return

        sale_view = self._check_selected()

        with api.trans() as store:
            return_sale(self.get_toplevel(), store.fetch(sale_view.sale), store)

        if store.committed:
            self._update_total()
            self.refresh()

    def _update_ecf(self, has_ecf):
        # If we have an ecf, let the other events decide what to disable.
        if has_ecf:
            return

        # We dont have an ecf. Disable till related operations
        widgets = [self.TillOpen, self.TillClose, self.TillVerify, self.TillAddCash,
                   self.TillRemoveCash, self.SearchTillHistory, self.app_vbox,
                   self.Confirm, self.Return, self.Details]
        self.set_sensitive(widgets, has_ecf)
        text = _(u"Till operations requires a connected fiscal printer")
        self.till_status_label.set_text(text)

    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()

    #
    # Callbacks
    #

    def on_Confirm__activate(self, action):
        self._confirm_order()
        self._update_total()

    def on_results__double_click(self, results, sale):
        self._run_details_dialog()

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

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

    def on_results__right_click(self, results, result, event):
        self.popup.popup(None, None, None, event.button, event.time)

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

    def on_Return__activate(self, action):
        self._return_sale()

    def _on_PrinterHelper__till_status_changed(self, printer, closed, blocked):
        self._update_till_status(closed, blocked)

    def _on_PrinterHelper__ecf_changed(self, printer, ecf):
        self._update_ecf(ecf)

    def on_PaymentReceive__activate(self, action):
        self.run_dialog(PaymentReceivingSearch, self.store)

    # Till

    def on_TillVerify__activate(self, button):
        self._printer.verify_till()

    def on_TillClose__activate(self, button):
        self._printer.close_till()

    def on_TillOpen__activate(self, button):
        self._printer.open_till()

    def on_TillAddCash__activate(self, action):
        self._run_add_cash_dialog()

    def on_TillRemoveCash__activate(self, action):
        with api.trans() as store:
            run_dialog(CashOutEditor, self, store)
        if store.committed:
            self._update_total()

    # Search

    def on_SearchClient__activate(self, action):
        self._run_search_dialog(ClientSearch, hide_footer=True)

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

        self._run_search_dialog(SaleWithToolbarSearch)
        self.refresh()

    def on_SearchSoldItemsByBranch__activate(self, button):
        self._run_search_dialog(SoldItemsByBranchSearch)

    def on_SearchTillHistory__activate(self, button):
        self.run_dialog(TillHistoryDialog, self.store)

    def on_SearchFiscalTillOperations__activate(self, button):
        self._run_search_dialog(TillFiscalOperationsSearch)
コード例 #42
0
class DeliverySearch(SearchEditor):
    """Delivery search implementation"""

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

    #
    #  SearchEditor hooks
    #

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

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

        # 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('sale_identifier',
                             title=_('Sale #'),
                             order=gtk.SORT_DESCENDING),
            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.ELLIPSIZE_END),
            SearchColumn('tracking_code',
                         title=_('Tracking code'),
                         data_type=str),
            SearchColumn('transporter_name',
                         title=_('Transporter'),
                         data_type=str),
            SearchColumn('client_name', title=_('Client'), data_type=str),
            SearchColumn('open_date',
                         title=_('Open date'),
                         data_type=datetime.date,
                         visible=False),
            SearchColumn('deliver_date',
                         title=_('Sent date'),
                         data_type=datetime.date,
                         visible=False),
            SearchColumn('receive_date',
                         title=_('Received date'),
                         data_type=datetime.date,
                         visible=False),
        ]
コード例 #43
0
ファイル: stock.py プロジェクト: adrianoaguiar/stoq
class StockApp(ShellApp):
    app_title = _('Stock')
    gladefile = "stock"
    search_spec = ProductFullStockView
    search_labels = _('Matching:')
    report_table = SimpleProductReport
    pixbuf_converter = converter.get_converter(gtk.gdk.Pixbuf)

    #
    # Application
    #

    def create_actions(self):
        group = get_accels('app.stock')
        actions = [
            ("NewReceiving", STOQ_RECEIVING, _("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...")),
            ("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,
                                            filename='stock.xml')

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

        self.NewReceiving.set_short_label(_("Receive"))
        self.NewTransfer.set_short_label(_("Transfer"))
        self.EditProduct.set_short_label(_("Edit"))
        self.ProductStockHistory.set_short_label(_("History"))
        self.EditProduct.props.is_important = True
        self.ProductStockHistory.props.is_important = True

    def create_ui(self):
        if api.sysparam.get_bool('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.SearchBrandItems,
            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())

    def activate(self, refresh=True):
        self.window.NewToolItem.set_tooltip(
            _("Create a new receiving order"))
        self.window.SearchToolItem.set_tooltip(
            _("Search for stock items"))

        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.uimanager.remove_ui(self.stock_ui)
        self._close_image_viewer()

    def new_activate(self):
        if not self.NewReceiving.get_sensitive():
            warning(_("You cannot receive a purchase with an open inventory."))
            return
        self._receive_purchase()

    def search_activate(self):
        self.run_dialog(ProductStockSearch, self.store)

    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.ELLIPSIZE_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:
                thumbnail = sellable.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()

        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_sensitive()
        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.ICON_SIZE_LARGE_TOOLBAR)
            return

        # FIXME: get this icon size from settings
        icon_size = 24
        pixbuf = pixbuf.scale_simple(icon_size, icon_size,
                                     gtk.gdk.INTERP_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.MESSAGE_QUESTION, msg)
        button = info_bar.add_button(_(u"Receive"), gtk.RESPONSE_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.MESSAGE_QUESTION, msg)
        button = info_returned_bar.add_button(_(u"Returned sale"), gtk.RESPONSE_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_active(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_results__right_click(self, results, result, event):
        self.popup.popup(None, None, None, event.button, event.time)

    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_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__toggled(self, button):
        if button.get_active():
            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)
コード例 #44
0
ファイル: inventory.py プロジェクト: rosalin/stoq
class InventoryApp(ShellApp):

    # TODO: Change all widget.set_sensitive to self.set_sensitive([widget])
    app_title = _('Inventory')
    gladefile = "inventory"
    search_spec = Inventory
    search_labels = _('Matching:')
    report_table = InventoryReport

    #
    # Application
    #

    def create_actions(self):
        group = get_accels('app.inventory')
        actions = [
            # File
            ('NewInventory', None, _('Inventory...'),
             group.get('new_inventory'),
             _('Create a new inventory for product counting')),

            # Inventory
            ('InventoryMenu', None, _('Inventory')),
            ('Details', gtk.STOCK_INFO, _('Details...'),
             group.get('inventory_details'),
             _('See details about this inventory')),
            ('CountingAction', gtk.STOCK_INDEX, _('_Count...'),
             group.get('inventory_count'),
             _('Register the actual stock of products in the selected '
               'inventory')),
            ('AdjustAction', gtk.STOCK_CONVERT, _('_Adjust...'),
             group.get('inventory_adjust'),
             _('Adjust the stock accordingly to the counting in the selected '
               'inventory')),
            ('Cancel', gtk.STOCK_CANCEL, _('Cancel...'),
             group.get('inventory_cancel'),
             _('Cancel the selected inventory')),
            ('PrintProductListing', gtk.STOCK_PRINT,
             _('Print product listing...'),
             group.get('inventory_print'),
             _('Print the product listing for this inventory'))
        ]
        self.inventory_ui = self.add_ui_actions('', actions,
                                                filename='inventory.xml')
        self.set_help_section(_("Inventory help"), 'app-inventory')

        self.AdjustAction.set_short_label(_("Adjust"))
        self.CountingAction.set_short_label(_("Count"))
        self.Details.set_short_label(_("Details"))
        self.Cancel.set_short_label(_("Cancel"))
        self.AdjustAction.props.is_important = True
        self.CountingAction.props.is_important = True
        self.Details.props.is_important = True
        self.Cancel.props.is_important = True

    def create_ui(self):
        self.popup = self.uimanager.get_widget('/InventorySelection')

        self.window.add_new_items([self.NewInventory])
        self.window.Print.set_tooltip(
            _("Print a report of these inventories"))

    def activate(self, refresh=True):
        # Avoid letting this sensitive if has-rows is never emitted
        self.refresh()
        self._update_widgets()
        self.window.SearchToolItem.set_sensitive(False)

    def deactivate(self):
        self.uimanager.remove_ui(self.inventory_ui)
        self.window.SearchToolItem.set_sensitive(True)

    def new_activate(self):
        if not self.NewInventory.get_sensitive():
            warning(_("You cannot open an inventory without having a "
                      "branch with stock in it."))
            return
        self._open_inventory()

    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"])

    def get_columns(self):
        return [IdentifierColumn('identifier', title=_('Code'),
                                 sorted=True, order=gtk.SORT_DESCENDING),
                SearchColumn('status_str', title=_('Status'),
                             data_type=str, width=100,
                             valid_values=self._get_status_values(),
                             search_attribute='status'),
                Column('branch.person.name', title=_('Branch'),
                       data_type=str, expand=True),
                SearchColumn('open_date', title=_('Opened'),
                             long_title=_('Date Opened'),
                             data_type=datetime.date, width=120),
                SearchColumn('close_date', title=_('Closed'),
                             long_title=_('Date Closed'),
                             data_type=datetime.date, width=120)]

    #
    # Private API
    #

    def _get_status_values(self):
        values = [(v, k) for k, v in Inventory.statuses.items()]
        values.insert(0, (_("Any"), None))
        return values

    def _get_branches(self):
        return self.store.find(Branch)

    def _get_branches_for_filter(self):
        items = [(b.person.name, b.id) for b in self._get_branches()]
        if not items:
            raise DatabaseInconsistency('You should have at least one '
                                        'branch on your database.'
                                        'Found zero')
        items.insert(0, [_('All branches'), None])
        return items

    def _update_widgets(self):
        has_open = False
        all_counted = False
        has_adjusted = False
        selected = self.results.get_selected()
        if selected:
            all_counted = selected.all_items_counted()
            has_open = selected.is_open()
            has_adjusted = selected.has_adjusted_items()

        self.set_sensitive([self.PrintProductListing, self.Details],
                           bool(selected))
        self.set_sensitive([self.Cancel], has_open and not has_adjusted)
        self.set_sensitive([self.NewInventory], self._can_open())
        self.set_sensitive([self.CountingAction], has_open and not all_counted)
        self.set_sensitive([self.AdjustAction], has_open and all_counted)
        self.window.set_new_menu_sensitive(self._can_open())

    def _can_open(self):
        branch = api.get_current_branch(self.store)
        if Inventory.has_open(self.store, branch):
            return False

        # It doesn't make sense to open an inventory if we don't have any stock
        return self.store.find(ProductStockItem, branch=branch).count() > 0

    def _open_inventory(self):
        with api.trans() as store:
            rv = self.run_dialog(InventoryOpenEditor, store)

        if rv:
            self.refresh()
            self._update_widgets()

    def _cancel_inventory(self):
        if not yesno(_('Are you sure you want to cancel this inventory ?'),
                     gtk.RESPONSE_NO, _("Cancel inventory"), _("Don't cancel")):
            return

        store = api.new_store()
        inventory = store.fetch(self.results.get_selected())
        inventory.cancel()
        store.commit()
        store.close()
        self.refresh()
        self._update_widgets()

    def _register_product_counting(self):
        store = api.new_store()
        inventory = store.fetch(self.results.get_selected())
        model = self.run_dialog(InventoryCountWizard, store, inventory)
        store.confirm(model)
        store.close()
        self.refresh()
        self._update_widgets()

    def _adjust_product_quantities(self):
        store = api.new_store()
        inventory = store.fetch(self.results.get_selected())
        model = self.run_dialog(InventoryAdjustmentEditor, store, inventory)
        store.confirm(model)
        store.close()
        self.refresh()
        self._update_widgets()

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

    def _get_sellables_by_inventory(self, inventory):
        for item in inventory.get_items():
            yield item.product.sellable

    def _show_inventory_details(self):
        store = api.new_store()
        inventory = store.fetch(self.results.get_selected())
        self.run_dialog(InventoryDetailsDialog, store, inventory)
        store.close()

    #
    # Callbacks
    #

    def on_NewInventory__activate(self, action):
        self._open_inventory()

    def on_CountingAction__activate(self, action):
        self._register_product_counting()

    def on_AdjustAction__activate(self, action):
        self._adjust_product_quantities()

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

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

    def on_results__right_click(self, results, result, event):
        self.popup.popup(None, None, None, event.button, event.time)

    def on_results__double_click(self, results, inventory):
        self._show_inventory_details()

    def on_Cancel__activate(self, widget):
        self._cancel_inventory()

    def on_PrintProductListing__activate(self, button):
        selected = self.results.get_selected()
        sellables = list(self._get_sellables_by_inventory(selected))
        if not sellables:
            warning(_("No products found in the inventory."))
            return
        self.print_report(ProductCountingReport, sellables)
コード例 #45
0
ファイル: stock.py プロジェクト: rosalin/stoq
class StockApp(ShellApp):
    app_title = _('Stock')
    gladefile = "stock"
    search_spec = ProductFullStockView
    search_labels = _('Matching:')
    report_table = SimpleProductReport
    pixbuf_converter = converter.get_converter(gtk.gdk.Pixbuf)

    #
    # Application
    #

    def create_actions(self):
        group = get_accels('app.stock')
        actions = [
            ("NewReceiving", STOQ_RECEIVING, _("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")),
            ("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...")),
            ("ProductMenu", None, _("Product")),
            ("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, filename='stock.xml')

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

        self.NewReceiving.set_short_label(_("Receive"))
        self.NewTransfer.set_short_label(_("Transfer"))
        self.EditProduct.set_short_label(_("Edit"))
        self.ProductStockHistory.set_short_label(_("History"))
        self.EditProduct.props.is_important = True
        self.ProductStockHistory.props.is_important = True

    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.SearchBrandItems,
            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())

    def activate(self, refresh=True):
        self.window.NewToolItem.set_tooltip(_("Create a new receiving order"))
        self.window.SearchToolItem.set_tooltip(_("Search for stock items"))

        self.check_open_inventory()
        self._update_widgets()

    def setup_focus(self):
        self.refresh()

    def deactivate(self):
        self.uimanager.remove_ui(self.stock_ui)

    def new_activate(self):
        if not self.NewReceiving.get_sensitive():
            warning(_("You cannot receive a purchase with an open inventory."))
            return
        self._receive_purchase()

    def search_activate(self):
        self.run_dialog(ProductStockSearch, self.store)

    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'])
        self.branch_filter = ComboSearchFilter(_('Show by:'),
                                               self._get_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.ELLIPSIZE_END),
            SearchColumn('manufacturer',
                         title=_("Manufacturer"),
                         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),
            SearchColumn('stock',
                         title=_('Quantity'),
                         data_type=decimal.Decimal,
                         width=100),
            SearchColumn('unit',
                         title=_("Unit"),
                         data_type=str,
                         width=40,
                         visible=False),
            Column('has_image', title=_('Picture'), data_type=bool, width=80),
        ]

    #
    # Private API
    #

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

    def _get_branches(self):
        items = [(b.person.name, b) for b in self.store.find(Branch)]
        if not items:
            raise DatabaseInconsistency('You should have at least one '
                                        'branch on your database.'
                                        'Found zero')
        items.insert(0, [_('All branches'), None])
        return items

    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 sellable.has_image:
                thumbnail = sellable.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()

        self.set_sensitive([self.EditProduct], 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_sensitive()
        self.set_sensitive([self.NewTransfer], transfer_active
                           and has_branches)
        self.set_sensitive([self.SearchTransfer], has_branches)

    def _update_edit_image(self, pixbuf=None):
        if not pixbuf:
            self.image.set_from_stock(gtk.STOCK_EDIT,
                                      gtk.ICON_SIZE_LARGE_TOOLBAR)
            return

        # FIXME: get this icon size from settings
        icon_size = 24
        pixbuf = pixbuf.scale_simple(icon_size, icon_size,
                                     gtk.gdk.INTERP_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()

    #
    # Callbacks
    #

    def on_image_viewer_closed(self, window, event):
        self.StockPictureViewer.props.active = False
        self.image_viewer = None

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

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

    def on_results__right_click(self, results, result, event):
        self.popup.popup(None, None, None, event.button, event.time)

    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_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()

    # 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
        branch = self.branch_filter.get_state().value
        store = api.new_store()
        retval = self.run_dialog(InitialStockDialog, store, branch)
        store.confirm(retval)
        store.close()
        self.refresh()

    def on_StockPictureViewer__activate(self, button):
        if self.image_viewer:
            self.StockPictureViewer.props.active = False
            self.image_viewer.destroy()
            self.image_viewer = None
        else:
            self.StockPictureViewer.props.active = True
            self.image_viewer = SellableImageViewer()
            selected = self.results.get_selected()
            if selected:
                self.image_viewer.set_sellable(selected.product.sellable)
            self.image_viewer.toplevel.connect("delete-event",
                                               self.on_image_viewer_closed)
            self.image_viewer.toplevel.set_property("visible", True)

    # 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.run_dialog(TransferOrderSearch, self.store)
        self.refresh()

    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_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)
コード例 #46
0
ファイル: transfersearch.py プロジェクト: barkinet/stoq
class TransferOrderSearch(SearchDialog):
    title = _(u"Transfer Order Search")
    size = (750, 500)
    search_spec = TransferOrderView
    report_class = TransferOrderReport
    selection_mode = gtk.SELECTION_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
        run_dialog(TransferOrderDetailsDialog, self, self.store,
                   transfer_order)

    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',
                                                   'receival_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'),
        ]

    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)
        else:
            return Or(TransferOrder.source_branch_id == current_branch.id,
                      TransferOrder.destination_branch_id == current_branch.id)

    def get_columns(self):
        return [IdentifierColumn('identifier'),
                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('receival_date', _('Receival 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])