コード例 #1
0
class MaintenanceApp(ShellApp):
    """Maintenance app"""

    app_title = _(u'Maintenance')
    gladefile = 'maintenance'
    search_spec = WorkOrderView
    search_label = _(u'matching:')
    report_table = WorkOrdersReport

    _query_mapper = {
        'pending':
        Or(WorkOrder.status == WorkOrder.STATUS_OPENED,
           WorkOrder.status == WorkOrder.STATUS_APPROVED),
        'in-progress':
        WorkOrder.status == WorkOrder.STATUS_WORK_IN_PROGRESS,
        'in-transport':
        Eq(WorkOrder.current_branch_id, None),
        'finished':
        WorkOrder.status == WorkOrder.STATUS_WORK_FINISHED,
        'closed':
        Or(WorkOrder.status == WorkOrder.STATUS_CANCELLED,
           WorkOrder.status == WorkOrder.STATUS_CLOSED),
    }

    #
    # Application
    #

    def create_actions(self):
        group = get_accels('app.maintenance')
        actions = [
            # File
            ("OrderMenu", None, _(u"Order")),
            ("NewOrder", None, _(u"Work order..."), group.get("new_order")),
            ("SendOrders", None, _(u"Send orders...")),
            ("ReceiveOrders", None, _(u"Receive orders...")),

            # Search
            ("Products", None, _(u"Products..."), group.get("search_products")
             ),
            ("Services", None, _(u"Services..."),
             group.get("search_services")),
            ("Categories", None, _(u"Categories..."),
             group.get("search_categories")),
            ("Clients", STOQ_CLIENTS, _(u"Clients..."),
             group.get("search_clients")),

            # Order
            ("Edit", gtk.STOCK_EDIT, _(u"Edit..."), group.get('order_edit'),
             _(u"Edit the selected order")),
            ("Finish", gtk.STOCK_APPLY, _(u"Finish..."),
             group.get('order_finish'), _(u"Finish the selected order")),
            ("Cancel", gtk.STOCK_CANCEL, _(u"Cancel..."),
             group.get('order_cancel'), _(u"Cancel the selected order")),
            ("Details", gtk.STOCK_INFO, _(u"Details..."),
             group.get('order_details'),
             _(u"Show details of the selected order")),
            ("PrintQuote", None, _(u"Print quote..."),
             group.get('order_print_quote'),
             _(u"Print a quote report of the selected order")),
            ("PrintReceipt", None, _(u"Print receipt..."),
             group.get('order_print_receipt'),
             _(u"Print a receipt of the selected order")),
        ]
        self.maintenance_ui = self.add_ui_actions("",
                                                  actions,
                                                  filename="maintenance.xml")

        radio_actions = [
            ('ViewKanban', '', _("View as Kanban"), '',
             _("Show in Kanban mode")),
            ('ViewList', '', _("View as List"), '', _("Show in list mode")),
        ]
        self.add_ui_actions('', radio_actions, 'RadioActions', 'radio')

        if is_developer_mode():
            self.ViewList.props.active = True
        else:
            self.ViewList.props.visible = False
            self.ViewKanban.props.visible = False
        self.Edit.set_short_label(_(u"Edit"))
        self.Finish.set_short_label(_(u"Finish"))
        self.Edit.props.is_important = True
        self.Finish.props.is_important = True

        self.set_help_section(_(u"Maintenance help"), 'app-maintenance')
        self.popup = self.uimanager.get_widget('/MaintenanceSelection')

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

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

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

        self.results.set_cell_data_func(self._on_results__cell_data_func)

    def activate(self, params):
        self.window.NewToolItem.set_tooltip(_(u"Create a new work order"))
        self.window.SearchToolItem.set_tooltip(
            _(u"Search for work order categories"))

        self._update_view()

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

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

    def new_activate(self):
        self._new_order()

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

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

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

        if not base_msg:
            return

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

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

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

        executer = self.search.get_query_executer()
        executer.add_filter_query_callback(
            self.main_filter, self._on_main_filter__query_callback)
        self.add_filter(self.main_filter, SearchFilterPosition.TOP)

        self._update_filters()

    def get_columns(self):
        return [
            IdentifierColumn('identifier'),
            SearchColumn('work_order.status_str',
                         title=_(u'Status'),
                         search_attribute='status',
                         data_type=str,
                         valid_values=self._get_status_values(),
                         visible=False),
            SearchColumn('category_name',
                         title=_(u'Category'),
                         data_type=str,
                         visible=False),
            SearchColumn('equipment',
                         title=_(u'Equipment'),
                         data_type=str,
                         expand=True,
                         pack_end=True),
            Column('category_color',
                   title=_(u'Equipment'),
                   column='equipment',
                   data_type=gtk.gdk.Pixbuf,
                   format_func=render_pixbuf),
            SearchColumn('client_name', title=_(u'Client'), data_type=str),
            SearchColumn('branch_name',
                         title=_(u'Branch'),
                         data_type=str,
                         visible=False),
            SearchColumn('current_branch_name',
                         title=_(u'Current branch'),
                         data_type=str,
                         visible=False),
            SearchColumn('open_date',
                         title=_(u'Open date'),
                         data_type=datetime.date),
            SearchColumn('approve_date',
                         title=_(u'Approval date'),
                         data_type=datetime.date,
                         visible=False),
            SearchColumn('finish_date',
                         title=_(u'Finish date'),
                         data_type=datetime.date,
                         visible=False),
            SearchColumn('total', title=_(u'Total'), data_type=currency),
        ]

    #
    # Private
    #

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

        kind, value = item.value.split(':')
        if kind == 'category':
            return WorkOrder.category_id == item.id
        if kind == 'status':
            return self._query_mapper[value]
        else:
            raise AssertionError(kind, value)

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

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

    def _update_list_aware_view(self):
        selection = self.search.get_selected_item()
        has_selected = bool(selection)
        has_quote = has_selected and bool(selection.work_order.defect_detected)

        can_edit = (has_selected and (selection.work_order.can_approve()
                                      or selection.work_order.can_start()
                                      or selection.work_order.can_finish()))
        self.set_sensitive([self.Edit], can_edit)
        self.set_sensitive([self.Details], has_selected)
        self.set_sensitive([self.Finish], has_selected
                           and selection.work_order.can_finish())
        self.set_sensitive([self.Cancel], has_selected
                           and selection.work_order.can_cancel())
        self.set_sensitive([self.PrintReceipt], has_selected
                           and selection.work_order.is_finished())
        self.set_sensitive([self.PrintQuote], has_quote)

    def _update_filters(self):
        options = [
            _FilterItem(_(u'Pending'), 'status:pending'),
            _FilterItem(_(u'In progress'), 'status:in-progress'),
            _FilterItem(_(u'In transport'), 'status:in-transport'),
            _FilterItem(_(u'Finished'), 'status:finished'),
            _FilterItem(_(u'Closed or cancelled'), 'status:closed'),
        ]

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

        self.main_filter.update_values([(_(u'All work orders'), None)] +
                                       [(item.name, item) for item in options])

    def _new_order(self, category=None):
        with api.trans() as store:
            work_order = self.run_dialog(WorkOrderEditor,
                                         store,
                                         category=store.fetch(category))

        if store.committed:
            self._update_view(select_item=work_order)
            # A category may have been created on the editor
            self._update_filters()

    def _edit_order(self, work_order=None):
        if work_order is None:
            work_order = self.search.get_selected_item().work_order
        with api.trans() as store:
            self.run_dialog(WorkOrderEditor,
                            store,
                            model=store.fetch(work_order))

        if store.committed:
            self._update_view()
            # A category may have been created on the editor
            self._update_filters()

    def _finish_order(self):
        if not yesno(
                _(u"This will finish the selected order, marking the "
                  u"work as done. Are you sure?"), gtk.RESPONSE_NO,
                _(u"Finish order"), _(u"Don't finish")):
            return

        selection = self.search.get_selected_item()
        with api.trans() as store:
            work_order = store.fetch(selection.work_order)
            work_order.finish()

        self._update_view()

    def _cancel_order(self):
        if not yesno(_(u"This will cancel the selected order. Are you sure?"),
                     gtk.RESPONSE_NO, _(u"Cancel order"), _(u"Don't cancel")):
            return

        selection = self.search.get_selected_item()
        with api.trans() as store:
            work_order = store.fetch(selection.work_order)
            work_order.cancel()
        self._update_view()

    def _send_orders(self):
        with api.trans() as store:
            self.run_dialog(WorkOrderPackageSendEditor, store)

        if store.committed:
            self._update_view()

    def _receive_orders(self):
        with api.trans() as store:
            self.run_dialog(WorkOrderPackageReceiveWizard, store)

        if store.committed:
            self._update_view()

    def _run_order_details_dialog(self):
        selection = self.search.get_selected_item()
        self.run_dialog(WorkOrderEditor,
                        self.store,
                        model=selection.work_order,
                        visual_mode=True)

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

    #
    # Kiwi Callbacks
    #

    def _on_main_filter__row_separator_func(self, model, titer):
        if model[titer][0] == 'sep':
            return True
        return False

    def _on_main_filter__query_callback(self, state):
        return self._get_main_query(state)

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

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

        for prop, is_set, value in [('strikethrough', is_closed, True),
                                    ('style', is_finished, pango.STYLE_ITALIC),
                                    ('weight', is_late, pango.WEIGHT_BOLD)]:
            renderer.set_property(prop + '-set', is_set)
            if is_set:
                renderer.set_property(prop, value)

        return text

    def on_search__result_item_popup_menu(self, search, item, event):
        self.popup.popup(None, None, None, event.button, event.time)

    def on_search__result_item_activated(self, search, item):
        if self.Edit.get_sensitive():
            self._edit_order()
        elif self.Details.get_sensitive():
            self._run_order_details_dialog()
        else:
            assert False

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

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

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

        self._new_order(category=category)

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

    def on_SendOrders__activate(self, action):
        self._send_orders()

    def on_ReceiveOrders__activate(self, action):
        self._receive_orders()

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

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

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

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

    def on_PrintQuote__activate(self, action):
        workorderview = self.search.get_selected_item()
        print_report(WorkOrderQuoteReport, workorderview.work_order)

    def on_PrintReceipt__activate(self, action):
        workorderview = self.search.get_selected_item()
        print_report(WorkOrderReceiptReport, workorderview.work_order)

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

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

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

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

    def on_ViewList__toggled(self, action):
        if not action.get_active():
            return
        self.search.set_result_view(SearchResultListView, refresh=True)
        self._update_list_aware_view()

    def on_ViewKanban__toggled(self, action):
        if not action.get_active():
            return
        self.search.set_result_view(WorkOrderResultKanbanView, refresh=True)
        self._update_list_aware_view()
コード例 #2
0
ファイル: maintenance.py プロジェクト: rosalin/stoq
class MaintenanceApp(ShellApp):
    """Maintenance app"""

    app_title = _(u'Maintenance')
    gladefile = 'maintenance'
    search_spec = WorkOrderView
    search_label = _(u'matching:')
    report_table = WorkOrdersReport

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

    #
    # Application
    #

    def create_actions(self):
        group = get_accels('app.maintenance')
        actions = [
            # File
            ("OrderMenu", None, _(u"Order")),
            ("NewOrder", None, _(u"Work order..."),
             group.get("new_order")),
            ("SendOrders", None, _(u"Send orders...")),
            ("ReceiveOrders", None, _(u"Receive orders...")),

            # Search
            ("Products", None, _(u"Products..."),
             group.get("search_products")),
            ("Services", None, _(u"Services..."),
             group.get("search_services")),
            ("Categories", None, _(u"Categories..."),
             group.get("search_categories")),
            ("Clients", STOQ_CLIENTS, _(u"Clients..."),
             group.get("search_clients")),

            # Order
            ("Edit", gtk.STOCK_EDIT, _(u"Edit..."),
             group.get('order_edit'),
             _(u"Edit the selected order")),
            ("Finish", gtk.STOCK_APPLY, _(u"Finish..."),
             group.get('order_finish'),
             _(u"Finish the selected order")),
            ("Cancel", gtk.STOCK_CANCEL, _(u"Cancel..."),
             group.get('order_cancel'),
             _(u"Cancel the selected order")),
            ("DeliverOrder", None, _(u"Delivered...")),
            ("Details", gtk.STOCK_INFO, _(u"Details..."),
             group.get('order_details'),
             _(u"Show details of the selected order")),
            ("PrintQuote", None, _(u"Print quote..."),
             group.get('order_print_quote'),
             _(u"Print a quote report of the selected order")),
            ("PrintReceipt", None, _(u"Print receipt..."),
             group.get('order_print_receipt'),
             _(u"Print a receipt of the selected order")),
            ("Approve", None, _(u"Approve...")),
            ("Pause", None, _(u"Pause the work...")),
            ("Work", None, _(u"Start the work...")),
            ("Reject", None, _(u"Reject order...")),
            ("UndoRejection", None, _(u"Undo order rejection...")),
            ("Reopen", None, _(u"Reopen order...")),
        ]
        self.maintenance_ui = self.add_ui_actions("", actions,
                                                  filename="maintenance.xml")

        radio_actions = [
            ('ViewKanban', '', _("View as Kanban"),
             '', _("Show in Kanban mode")),
            ('ViewList', '', _("View as List"),
             '', _("Show in list mode")),
        ]
        self.add_ui_actions('', radio_actions, 'RadioActions',
                            'radio')

        if is_developer_mode():
            self.ViewList.props.active = True
        else:
            self.ViewList.props.visible = False
            self.ViewKanban.props.visible = False
        self.Edit.set_short_label(_(u"Edit"))
        self.Finish.set_short_label(_(u"Finish"))
        self.Edit.props.is_important = True
        self.Finish.props.is_important = True

        self.set_help_section(_(u"Maintenance help"), 'app-maintenance')
        self.popup = self.uimanager.get_widget('/MaintenanceSelection')

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

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

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

        self.results.set_cell_data_func(self._on_results__cell_data_func)

    def activate(self, refresh=True):
        self.window.NewToolItem.set_tooltip(
            _(u"Create a new work order"))
        self.window.SearchToolItem.set_tooltip(
            _(u"Search for work order categories"))

        self._update_view()

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

    def setup_focus(self):
        self.refresh()

    def new_activate(self):
        self._new_order()

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

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

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

        if not base_msg:
            return

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

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

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

        executer = self.search.get_query_executer()
        executer.add_filter_query_callback(
            self.main_filter,
            self._on_main_filter__query_callback)
        self.add_filter(self.main_filter, SearchFilterPosition.TOP)

        self._update_filters()

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

    #
    # Private
    #

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

        stock_id, tooltip = get_workorder_state_icon(item.work_order)
        if stock_id is not None:
            # We are using self.results because render_icon is a gtk.Widget's
            # method. It has nothing to do with results tough.
            return self.results.render_icon(stock_id, gtk.ICON_SIZE_MENU)

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

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

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

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

    def _update_list_aware_view(self):
        selection = self.search.get_selected_item()
        has_selected = bool(selection)
        wo = has_selected and selection.work_order
        has_quote = has_selected and bool(wo.defect_detected)

        can_edit = (has_selected and
                    (wo.can_approve() or wo.can_work() or wo.can_finish()))
        self.set_sensitive([self.Edit], can_edit)
        self.set_sensitive([self.Details], has_selected)
        self.set_sensitive([self.Finish], has_selected and wo.can_finish())
        self.set_sensitive([self.Cancel], has_selected and wo.can_cancel())
        self.set_sensitive([self.PrintReceipt], has_selected and wo.is_finished())
        self.set_sensitive([self.PrintQuote], has_quote)

        for widget, value in [
                (self.Approve, has_selected and wo.can_approve()),
                (self.Reject, has_selected and wo.can_reject()),
                (self.UndoRejection, has_selected and wo.can_undo_rejection()),
                (self.Pause, has_selected and wo.can_pause()),
                (self.Work, has_selected and wo.can_work()),
                (self.Reopen, has_selected and wo.can_reopen()),
                # DeliverOrder is grouped here since it's a special case. Only
                # finished orders without items can be delivered here, so avoid
                # showing the option if it's not sensitive to avoid confusions
                (self.DeliverOrder, (has_selected and wo.can_close() and
                                     not wo.order_items.count()))]:
            self.set_sensitive([widget], value)
            # Some of those options are mutually exclusive (except Approve,
            # but it can only be called once) so avoid confusions and
            # hide not available options
            widget.set_visible(value)

    def _update_filters(self):
        options = [
            _FilterItem(_(u'Pending'), 'status:pending'),
            _FilterItem(_(u'In progress'), 'status:in-progress'),
            _FilterItem(_(u'Finished'), 'status:finished'),
            _FilterItem(_(u'Delivered or cancelled'), 'status:delivered'),
            _FilterItem('sep', 'sep'),
            _FilterItem(_(u'Approved'), 'flag:approved'),
            _FilterItem(_(u'In transport'), 'flag:in-transport'),
            _FilterItem(_(u'Rejected'), 'flag:rejected'),
        ]

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

        self.main_filter.update_values(
            [(_(u'All work orders'), None)] +
            [(item.name, item) for item in options])

    def _new_order(self, category=None):
        with api.trans() as store:
            work_order = self.run_dialog(WorkOrderEditor, store,
                                         category=store.fetch(category))

        if store.committed:
            self._update_view(select_item=work_order)
            # A category may have been created on the editor
            self._update_filters()

    def _edit_order(self, work_order=None):
        if work_order is None:
            work_order = self.search.get_selected_item().work_order
        with api.trans() as store:
            self.run_dialog(WorkOrderEditor, store,
                            model=store.fetch(work_order))

        if store.committed:
            self._update_view()
            # A category may have been created on the editor
            self._update_filters()

    def _finish_order(self):
        if not yesno(_(u"This will finish the selected order, marking the "
                       u"work as done. Are you sure?"),
                     gtk.RESPONSE_NO, _(u"Finish order"), _(u"Don't finish")):
            return

        selection = self.search.get_selected_item()
        with api.trans() as store:
            work_order = store.fetch(selection.work_order)
            work_order.finish()

        self._update_view()

    def _cancel_order(self):
        msg_text = _(u"This will cancel the selected order. Are you sure?")
        rv = self._run_notes_editor(msg_text=msg_text, mandatory=True)
        if not rv:
            return

        selection = self.search.get_selected_item()
        with api.trans() as store:
            work_order = store.fetch(selection.work_order)
            work_order.cancel(reason=rv.notes)
        self._update_view()

    def _close_order(self):
        if not yesno(_(u"This will mark the order as delivered. Are you "
                       "sure?"),
                     gtk.RESPONSE_NO, _(u"Mark as delivered"),
                     _(u"Don't mark")):
            return

        selection = self.search.get_selected_item()
        with api.trans() as store:
            work_order = store.fetch(selection.work_order)
            work_order.close()

        self._update_view(select_item=selection)

    def _approve_order(self):
        if not yesno(_(u"This will inform the order that the client has "
                       u"approved the work. Are you sure?"),
                     gtk.RESPONSE_NO, _(u"Approve"), _(u"Don't approve")):
            return

        selection = self.search.get_selected_item()
        with api.trans() as store:
            work_order = store.fetch(selection.work_order)
            work_order.approve()

        self._update_view(select_item=selection)

    def _pause_order(self):
        msg_text = _(u"This will inform the order that we are waiting. "
                     u"Are you sure?")
        rv = self._run_notes_editor(msg_text=msg_text, mandatory=True)
        if not rv:
            return

        selection = self.search.get_selected_item()
        with api.trans() as store:
            work_order = store.fetch(selection.work_order)
            work_order.pause(reason=rv.notes)

        self._update_view(select_item=selection)

    def _work(self):
        selection = self.search.get_selected_item()
        with api.trans() as store:
            work_order = store.fetch(selection.work_order)
            work_order.work()

        self._update_view(select_item=selection)

    def _reject(self):
        msg_text = _(u"This will reject the order. Are you sure?")
        rv = self._run_notes_editor(msg_text=msg_text, mandatory=True)
        if not rv:
            return

        selection = self.search.get_selected_item()
        with api.trans() as store:
            work_order = store.fetch(selection.work_order)
            work_order.reject(reason=rv.notes)

        self._update_view(select_item=selection)

    def _undo_rejection(self):
        msg_text = _(u"This will undo the rejection of the order. "
                     u"Are you sure?")
        rv = self._run_notes_editor(msg_text=msg_text, mandatory=False)
        if not rv:
            return

        selection = self.search.get_selected_item()
        with api.trans() as store:
            work_order = store.fetch(selection.work_order)
            work_order.undo_rejection(reason=rv.notes)

        self._update_view(select_item=selection)

    def _reopen(self):
        msg_text = _(u"This will reopen the order. Are you sure?")
        rv = self._run_notes_editor(msg_text=msg_text, mandatory=True)
        if not rv:
            return

        selection = self.search.get_selected_item()
        with api.trans() as store:
            work_order = store.fetch(selection.work_order)
            work_order.reopen(reason=rv.notes)

        self._update_view(select_item=selection)

    def _send_orders(self):
        with api.trans() as store:
            self.run_dialog(WorkOrderPackageSendEditor, store)

        if store.committed:
            self._update_view()

    def _receive_orders(self):
        with api.trans() as store:
            self.run_dialog(WorkOrderPackageReceiveWizard, store)

        if store.committed:
            self._update_view()

    def _run_order_details_dialog(self):
        selection = self.search.get_selected_item()
        with api.trans() as store:
            self.run_dialog(WorkOrderEditor, store,
                            model=store.fetch(selection.work_order),
                            visual_mode=True)

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

    def _run_notes_editor(self, msg_text, mandatory):
        return self.run_dialog(NoteEditor, self.store, model=Note(),
                               message_text=msg_text, label_text=_(u"Reason"),
                               mandatory=mandatory)

    #
    # Kiwi Callbacks
    #

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

    def _on_main_filter__query_callback(self, state):
        return self._get_main_query(state)

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

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

        for prop, is_set, value in [
                ('strikethrough', is_delivered, True),
                ('style', is_finished, pango.STYLE_ITALIC),
                ('weight', is_late, pango.WEIGHT_BOLD)]:
            renderer.set_property(prop + '-set', is_set)
            if is_set:
                renderer.set_property(prop, value)

        return text

    def on_search__result_item_popup_menu(self, search, item, event):
        self.popup.popup(None, None, None, event.button, event.time)

    def on_search__result_item_activated(self, search, item):
        if self.Edit.get_sensitive():
            self._edit_order()
        elif self.Details.get_sensitive():
            self._run_order_details_dialog()
        else:
            assert False

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

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

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

        self._new_order(category=category)

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

    def on_SendOrders__activate(self, action):
        self._send_orders()

    def on_ReceiveOrders__activate(self, action):
        self._receive_orders()

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

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

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

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

    def on_Approve__activate(self, action):
        self._approve_order()

    def on_Pause__activate(self, action):
        self._pause_order()

    def on_Work__activate(self, action):
        self._work()

    def on_Reject__activate(self, action):
        self._reject()

    def on_UndoRejection__activate(self, action):
        self._undo_rejection()

    def on_Reopen__activate(self, action):
        self._reopen()

    def on_DeliverOrder__activate(self, action):
        self._close_order()

    def on_PrintQuote__activate(self, action):
        workorderview = self.search.get_selected_item()
        print_report(WorkOrderQuoteReport, workorderview.work_order)

    def on_PrintReceipt__activate(self, action):
        workorderview = self.search.get_selected_item()
        print_report(WorkOrderReceiptReport, workorderview.work_order)

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

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

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

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

    def on_ViewList__toggled(self, action):
        if not action.get_active():
            return
        self.search.set_result_view(SearchResultListView, refresh=True)
        self._update_list_aware_view()

    def on_ViewKanban__toggled(self, action):
        if not action.get_active():
            return
        self.search.set_result_view(WorkOrderResultKanbanView, refresh=True)
        self._update_list_aware_view()
コード例 #3
0
ファイル: accounts.py プロジェクト: hackedbellini/stoq
class BaseAccountWindow(ShellApp):

    #
    # Application
    #

    def create_ui(self):
        if api.sysparam.get_bool('SMART_LIST_LOADING'):
            self.search.enable_lazy_search()
        self.results.set_selection_mode(Gtk.SelectionMode.MULTIPLE)
        self.search.set_summary_label(column='value',
                                      label='<b>%s</b>' % (_('Total'), ),
                                      format='<b>%s</b>',
                                      parent=self.get_statusbar_message_area())
        self.results.set_cell_data_func(self._on_results__cell_data_func)

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

        state = states[1]
        if state and state.value is None:
            not_found = _("No payments found.")
            payment_url = '<a href="new_payment">%s</a>?' % (
                api.escape(_("create a new payment")))
            new_payment = _("Would you like to %s") % (payment_url, )
            msg = "%s\n\n%s" % (not_found, new_payment)
        else:
            v = state.value.value
            if v == 'status:late':
                msg = _("No late payments found.")
            elif v == 'status:paid':
                msg = _("No paid payments found.")
            elif v == 'status:not-paid':
                if self.search_spec == InPaymentView:
                    msg = _("No payments to receive found.")
                else:
                    msg = _("No payments to pay found.")
            elif v.startswith('category:'):
                category = v.split(':')[1]

                not_found = _("No payments in the <b>%s</b> category were found.") % (
                    api.escape(category), )
                payment_url = '<a href="new_payment?%s">%s</a>?' % (
                    urllib.parse.quote(category),
                    _("create a new payment"))
                msg = "%s\n\n%s" % (
                    not_found,
                    _("Would you like to %s") % (payment_url, ))
            else:
                return

        self.search.set_message(msg)

    #
    # Public API
    #

    def add_payment(self, category=None):
        with api.new_store() as store:
            self.run_dialog(self.editor_class, store, category=category)

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

    def show_details(self, payment_view):
        """Shows some details about the payment, allowing to edit a few
        properties
        """
        with api.new_store() as store:
            payment = store.fetch(payment_view.payment)
            run_dialog(self.editor_class, self, store, payment)

        if store.committed:
            payment_view.sync()
            self.results.update(payment_view)

        return payment

    def show_comments(self, payment_view):
        """Shows a dialog with comments saved on the payment
        @param payment_view: an OutPaymentView or InPaymentView instance
        """
        with api.new_store() as store:
            run_dialog(PaymentCommentsDialog, self, store,
                       payment_view.payment)

        if store.committed:
            payment_view.sync()
            self.results.update(payment_view)

    def change_due_date(self, payment_view, order):
        """ Receives a payment_view and change the payment due date
        related to the view.

        :param payment_view: an OutPaymentView or InPaymentView instance
        :param order: a Sale or Purchase instance related to this payment.
          This will be used to show the identifier of the order
        """
        assert payment_view.can_change_due_date()

        with api.new_store() as store:
            payment = store.fetch(payment_view.payment)
            order = store.fetch(order)
            run_dialog(PaymentDueDateChangeDialog, self, store,
                       payment, order)

        if store.committed:
            # We need to refresh the whole list as the payment(s) can possibly
            # disappear for the selected view
            self.refresh()

    def change_status(self, payment_view, order, status):
        """Show a dialog do enter a reason for status change

        :param payment_view: an OutPaymentView or InPaymentView instance
        :param order: a Sale or Purchase instance related to this payment.
          This will be used to show the identifier of the order
        :param status: The new status to set the payment to
        """
        with api.new_store() as store:
            payment = store.fetch(payment_view.payment)
            order = store.fetch(payment_view.sale)

            if order is None:
                order = store.fetch(payment_view.purchase)

            run_dialog(PaymentStatusChangeDialog, self, store,
                       payment, status, order)

        if store.committed:
            # We need to refresh the whole list as the payment(s) can possibly
            # disappear for the selected view
            self.refresh()

    def create_main_filter(self):
        self.main_filter = ComboSearchFilter(_('Show'), [])

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

        self.create_branch_filter(column=self.search_spec.branch_id)

    def add_filter_items(self, category_type, options):
        categories = PaymentCategory.get_by_type(self.store, category_type)
        items = [(_('All payments'), None)]

        if categories.count() > 0:
            options.append(FilterItem('sep', 'sep'))

        items.extend([(item.name, item) for item in options])
        for c in categories:
            item = FilterItem(c.name, 'category:%s' % (c.name, ),
                              color=c.color,
                              item_id=c.id)
            items.append((item.name, item))

        self.main_filter.update_values(items)

    #
    # Private
    #

    def _create_main_query(self, state):
        item = state.value
        if item is None:
            return None
        kind, value = item.value.split(':')
        payment_view = self.search_spec
        if kind == 'status':
            if value == 'paid':
                return payment_view.status == Payment.STATUS_PAID
            elif value == 'not-paid':
                return payment_view.status == Payment.STATUS_PENDING
            elif value == 'late':
                tolerance = api.sysparam.get_int('TOLERANCE_FOR_LATE_PAYMENTS')
                return And(
                    payment_view.status == Payment.STATUS_PENDING,
                    payment_view.due_date < localtoday() -
                    relativedelta(days=tolerance))
        elif kind == 'category':
            return payment_view.category == value

        raise AssertionError(kind, value)

    def _show_payment_categories(self):
        store = api.new_store()
        self.run_dialog(PaymentCategoryDialog, store, self.payment_category_type)
        self._update_filter_items()
        store.close()

    #
    # Callbacks
    #

    def _on_main_filter__row_separator_func(self, model, titer):
        if model[titer][0] == 'sep':
            return True
        return False

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

        state = self.main_filter.get_state()

        def show_strikethrough():
            if state.value is None:
                return True
            if state.value.value.startswith('category:'):
                return True
            return False

        is_pending = (pv.status == Payment.STATUS_PENDING)
        show_strikethrough = not is_pending and show_strikethrough()
        is_late = pv.is_late()

        renderer.set_property('strikethrough-set', show_strikethrough)
        renderer.set_property('weight-set', is_late)

        if show_strikethrough:
            renderer.set_property('strikethrough', True)
        if is_late:
            renderer.set_property('weight', Pango.Weight.BOLD)

        return text

    def on_results__activate_link(self, results, uri):
        if uri.startswith('new_payment'):
            if '?' in uri:
                category = urllib.parse.unquote(uri.split('?', 1)[1])
            else:
                category = None
            self.add_payment(category=category)

    def on_PaymentFlowHistory__activate(self, action):
        self.run_dialog(PaymentFlowHistoryDialog, self.store)

    def on_PaymentCategories__activate(self, action):
        self._show_payment_categories()
コード例 #4
0
class ServicesApp(ShellApp):
    """Services app"""

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

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

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

    #
    # Application
    #

    def create_actions(self):
        group = get_accels('app.services')
        actions = [
            # File
            ("OrderMenu", None, _(u"Order")),
            ("NewOrder", None, _(u"Work order..."),
             group.get("new_order")),
            ("SendOrders", None, _(u"Send orders...")),
            ("ReceiveOrders", None, _(u"Receive orders...")),

            # Search
            ("Products", None, _(u"Products..."),
             group.get("search_products")),
            ("Services", None, _(u"Services..."),
             group.get("search_services")),
            ("Categories", None, _(u"Categories..."),
             group.get("search_categories")),
            ("Clients", STOQ_CLIENTS, _(u"Clients..."),
             group.get("search_clients")),

            # Order
            ("Edit", Gtk.STOCK_EDIT, _(u"Edit..."),
             group.get('order_edit'),
             _(u"Edit the selected order")),
            ("Finish", Gtk.STOCK_APPLY, _(u"Finish..."),
             group.get('order_finish'),
             _(u"Finish the selected order")),
            ("Cancel", Gtk.STOCK_CANCEL, _(u"Cancel..."),
             group.get('order_cancel'),
             _(u"Cancel the selected order")),
            ("DeliverOrder", None, _(u"Deliver...")),
            ("Details", Gtk.STOCK_INFO, _(u"Details..."),
             group.get('order_details'),
             _(u"Show details of the selected order")),
            ("PrintQuote", None, _(u"Print quote..."),
             group.get('order_print_quote'),
             _(u"Print a quote report of the selected order")),
            ("PrintReceipt", None, _(u"Print receipt..."),
             group.get('order_print_receipt'),
             _(u"Print a receipt of the selected order")),
            ("Approve", None, _(u"Approve...")),
            ("Pause", None, _(u"Pause the work...")),
            ("Work", None, _(u"Start the work...")),
            ("Reject", None, _(u"Reject order...")),
            ("UndoRejection", None, _(u"Undo order rejection...")),
            ("Reopen", None, _(u"Reopen order...")),
        ]
        self.services_ui = self.add_ui_actions("", actions,
                                               filename="services.xml")

        radio_actions = [
            ('ViewKanban', '', _("View as Kanban"),
             '', _("Show in Kanban mode")),
            ('ViewList', '', _("View as List"),
             '', _("Show in list mode")),
        ]
        self.add_ui_actions('', radio_actions, 'RadioActions',
                            'radio')

        self.Edit.set_short_label(_(u"Edit"))
        self.Finish.set_short_label(_(u"Finish"))
        self.Edit.props.is_important = True
        self.Finish.props.is_important = True

        self.set_help_section(_(u"Services help"), 'app-services')
        self.popup = self.uimanager.get_widget('/ServicesSelection')

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

        self.window.add_new_items([
            self.NewOrder,
        ])
        self.window.add_search_items([
            self.Products,
            self.Services,
            self.Categories,
        ])
        self.ViewList.props.active = True

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

        self.results.set_cell_data_func(self._on_results__cell_data_func)

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

        self.window.NewToolItem.set_tooltip(
            _(u"Create a new work order"))
        self.window.SearchToolItem.set_tooltip(
            _(u"Search for work order categories"))

        if refresh:
            self._update_view()

        self.search.focus_search_entry()

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

    def new_activate(self):
        self.new_order()

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

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

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

        if not base_msg:
            return

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

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

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

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

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

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

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

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

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

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

    def new_order(self, category=None, available_categories=None):
        with api.new_store() as store:
            work_order = self.run_dialog(WorkOrderEditor, store,
                                         category=store.fetch(category),
                                         available_categories=available_categories)

        if store.committed:
            self._update_view(select_item=work_order)
            # A category may have been created on the editor
            self._update_filters()

    #
    # Private
    #

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

        stock_id, tooltip = get_workorder_state_icon(item.work_order)
        if stock_id is not None:
            # We are using self.results because render_icon is a Gtk.Widget's
            # method. It has nothing to do with results tough.
            return self.results.render_icon(stock_id, Gtk.IconSize.MENU)

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

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

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

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

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

        if wo and wo.sale is not None:
            has_quote = wo.order_items.count() > 0
        else:
            has_quote = wo and bool(wo.defect_reported or wo.defect_detected)

        self.set_sensitive([self.Edit], has_selected and wo.can_edit())
        self.set_sensitive([self.Details], has_selected)
        self.set_sensitive([self.Finish], has_selected and (wo.can_finish() or
                                                            wo.can_close()))
        self.set_sensitive([self.Cancel], has_selected and wo.can_cancel())
        self.set_sensitive([self.PrintReceipt], has_selected and wo.is_finished())
        self.set_sensitive([self.PrintQuote], has_quote)

        self.Finish.set_short_label(_(u"Finish"))
        # If the selected work order is already finished, we change the finish
        # button's label.
        if has_selected and wo.status == WorkOrder.STATUS_WORK_FINISHED:
            self.Finish.set_short_label(_(u"Deliver"))

        for widget, value in [
                (self.Approve, has_selected and wo.can_approve()),
                (self.Reject, has_selected and wo.can_reject()),
                (self.UndoRejection, has_selected and wo.can_undo_rejection()),
                (self.Pause, has_selected and wo.can_pause()),
                (self.Work, has_selected and wo.can_work()),
                (self.Reopen, has_selected and wo.can_reopen()),
                # DeliverOrder is grouped here since it's a special case
                # Only finished orders without items and without sale can be
                # delivered here, so avoid showing the option if it's not
                #sensitive to avoid confusions
                (self.DeliverOrder, (has_selected and wo.can_close() and
                                     not wo.order_items.count() and not wo.sale))]:
            self.set_sensitive([widget], value)
            # Some of those options are mutually exclusive (except Approve,
            # but it can only be called once) so avoid confusions and
            # hide not available options
            widget.set_visible(value)

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

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

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

    def _edit_order(self, work_order=None):
        if work_order is None:
            work_order = self.search.get_selected_item().work_order
        with api.new_store() as store:
            self.run_dialog(WorkOrderEditor, store,
                            model=store.fetch(work_order))

        if store.committed:
            self._update_view()
            # A category may have been created on the editor
            self._update_filters()

    def _finish_or_deliver_order(self):
        work_order = self.search.get_selected_item().work_order
        if work_order.status == WorkOrder.STATUS_WORK_FINISHED:
            self._close_order()
        else:
            self._finish_order()

    def _finish_order(self):
        work_order = self.search.get_selected_item().work_order

        if work_order.is_items_totally_reserved():
            msg = _(u"This will finish the selected order, marking the "
                    u"work as done. Are you sure?")
        else:
            msg = _(u"Some items on this work order are not fully reserved. "
                    u"Do you still want to mark it as finished?")

        if not yesno(msg, Gtk.ResponseType.NO,
                     _(u"Finish order"), _(u"Don't finish")):
            return

        with api.new_store() as store:
            work_order = store.fetch(work_order)
            work_order.finish()

        self._update_view()

    def _cancel_order(self):
        rv = self._run_notes_editor(msg_text=cancel_question, mandatory=True)
        if not rv:
            return

        selection = self.search.get_selected_item()
        with api.new_store() as store:
            work_order = store.fetch(selection.work_order)
            work_order.cancel(reason=rv.notes)
        self._update_view()

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

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

        self._update_view(select_item=selection)

    def _approve_order(self):
        if not yesno(_(u"This will inform the order that the client has "
                       u"approved the work. Are you sure?"),
                     Gtk.ResponseType.NO, _(u"Approve"), _(u"Don't approve")):
            return

        selection = self.search.get_selected_item()
        with api.new_store() as store:
            work_order = store.fetch(selection.work_order)
            work_order.approve()

        self._update_view(select_item=selection)

    def _pause_order(self):
        rv = self._run_notes_editor(msg_text=waiting_question, mandatory=True)
        if not rv:
            return

        selection = self.search.get_selected_item()
        with api.new_store() as store:
            work_order = store.fetch(selection.work_order)
            work_order.pause(reason=rv.notes)

        self._update_view(select_item=selection)

    def _work(self):
        selection = self.search.get_selected_item()
        with api.new_store() as store:
            work_order = store.fetch(selection.work_order)
            work_order.work()

        self._update_view(select_item=selection)

    def _reject(self):
        msg_text = _(u"This will reject the order. Are you sure?")
        rv = self._run_notes_editor(msg_text=msg_text, mandatory=True)
        if not rv:
            return

        selection = self.search.get_selected_item()
        with api.new_store() as store:
            work_order = store.fetch(selection.work_order)
            work_order.reject(reason=rv.notes)

        self._update_view(select_item=selection)

    def _undo_rejection(self):
        msg_text = _(u"This will undo the rejection of the order. "
                     u"Are you sure?")
        rv = self._run_notes_editor(msg_text=msg_text, mandatory=False)
        if not rv:
            return

        selection = self.search.get_selected_item()
        with api.new_store() as store:
            work_order = store.fetch(selection.work_order)
            work_order.undo_rejection(reason=rv.notes)

        self._update_view(select_item=selection)

    def _reopen(self):
        rv = self._run_notes_editor(msg_text=reopen_question, mandatory=True)
        if not rv:
            return

        selection = self.search.get_selected_item()
        with api.new_store() as store:
            work_order = store.fetch(selection.work_order)
            work_order.reopen(reason=rv.notes)

        self._update_view(select_item=selection)

    def _send_orders(self):
        with api.new_store() as store:
            self.run_dialog(WorkOrderPackageSendEditor, store)

        if store.committed:
            self._update_view()

    def _receive_orders(self):
        with api.new_store() as store:
            self.run_dialog(WorkOrderPackageReceiveWizard, store)

        if store.committed:
            self._update_view()

    def _run_order_details_dialog(self):
        selection = self.search.get_selected_item()
        with api.new_store() as store:
            self.run_dialog(WorkOrderEditor, store,
                            model=store.fetch(selection.work_order),
                            visual_mode=True)

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

    def _run_notes_editor(self, msg_text, mandatory):
        return self.run_dialog(NoteEditor, self.store, model=Note(),
                               message_text=msg_text, label_text=_(u"Reason"),
                               mandatory=mandatory)

    #
    # Kiwi Callbacks
    #

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

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

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

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

        return text

    def on_search__result_item_popup_menu(self, search, item, event):
        self.popup.popup(None, None, None, None,
                         event.button.button, event.time)

    def on_search__result_item_activated(self, search, item):
        if self.Edit.get_sensitive():
            self._edit_order()
        elif self.Details.get_sensitive():
            self._run_order_details_dialog()
        else:
            assert False

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

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

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

        self.new_order(category=category)

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

    def on_SendOrders__activate(self, action):
        self._send_orders()

    def on_ReceiveOrders__activate(self, action):
        self._receive_orders()

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

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

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

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

    def on_Approve__activate(self, action):
        self._approve_order()

    def on_Pause__activate(self, action):
        self._pause_order()

    def on_Work__activate(self, action):
        self._work()

    def on_Reject__activate(self, action):
        self._reject()

    def on_UndoRejection__activate(self, action):
        self._undo_rejection()

    def on_Reopen__activate(self, action):
        self._reopen()

    def on_DeliverOrder__activate(self, action):
        self._close_order()

    def on_PrintQuote__activate(self, action):
        workorderview = self.search.get_selected_item()
        print_report(WorkOrderQuoteReport, workorderview.work_order)

    def on_PrintReceipt__activate(self, action):
        workorderview = self.search.get_selected_item()
        print_report(WorkOrderReceiptReport, workorderview.work_order)

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

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

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

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

    def on_ViewList__toggled(self, action):
        if not action.get_active():
            return
        self.search.set_result_view(SearchResultListView, refresh=True)
        self._update_list_aware_view()

    def on_ViewKanban__toggled(self, action):
        if not action.get_active():
            return
        self.search.set_result_view(WorkOrderResultKanbanView, refresh=True)
        self._update_list_aware_view()
コード例 #5
0
ファイル: accounts.py プロジェクト: 5l1v3r1/stoq-1
class BaseAccountWindow(ShellApp):

    #
    # Application
    #

    def create_ui(self):
        if api.sysparam.get_bool('SMART_LIST_LOADING'):
            self.search.enable_lazy_search()
        self.results.set_selection_mode(Gtk.SelectionMode.MULTIPLE)
        self.search.set_summary_label(column='value',
                                      label='<b>%s</b>' % (_('Total'), ),
                                      format='<b>%s</b>',
                                      parent=self.get_statusbar_message_area())
        self.results.set_cell_data_func(self._on_results__cell_data_func)

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

        state = states[1]
        if state and state.value is None:
            not_found = _("No payments found.")
            payment_url = '<a href="new_payment">%s</a>?' % (api.escape(
                _("create a new payment")))
            new_payment = _("Would you like to %s") % (payment_url, )
            msg = "%s\n\n%s" % (not_found, new_payment)
        else:
            v = state.value.value
            if v == 'status:late':
                msg = _("No late payments found.")
            elif v == 'status:paid':
                msg = _("No paid payments found.")
            elif v == 'status:not-paid':
                if self.search_spec == InPaymentView:
                    msg = _("No payments to receive found.")
                else:
                    msg = _("No payments to pay found.")
            elif v.startswith('category:'):
                category = v.split(':')[1]

                not_found = _(
                    "No payments in the <b>%s</b> category were found.") % (
                        api.escape(category), )
                payment_url = '<a href="new_payment?%s">%s</a>?' % (
                    urllib.parse.quote(category), _("create a new payment"))
                msg = "%s\n\n%s" % (not_found, _("Would you like to %s") %
                                    (payment_url, ))
            else:
                return

        self.search.set_message(msg)

    #
    # Public API
    #

    def add_payment(self, category=None):
        with api.new_store() as store:
            self.run_dialog(self.editor_class, store, category=category)

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

    def show_details(self, payment_view):
        """Shows some details about the payment, allowing to edit a few
        properties
        """
        with api.new_store() as store:
            payment = store.fetch(payment_view.payment)
            run_dialog(self.editor_class, self, store, payment)

        if store.committed:
            payment_view.sync()
            self.results.update(payment_view)

        return payment

    def show_comments(self, payment_view):
        """Shows a dialog with comments saved on the payment
        @param payment_view: an OutPaymentView or InPaymentView instance
        """
        with api.new_store() as store:
            run_dialog(PaymentCommentsDialog, self, store,
                       payment_view.payment)

        if store.committed:
            payment_view.sync()
            self.results.update(payment_view)

    def change_due_date(self, payment_view, order):
        """ Receives a payment_view and change the payment due date
        related to the view.

        :param payment_view: an OutPaymentView or InPaymentView instance
        :param order: a Sale or Purchase instance related to this payment.
          This will be used to show the identifier of the order
        """
        assert payment_view.can_change_due_date()

        with api.new_store() as store:
            payment = store.fetch(payment_view.payment)
            order = store.fetch(order)
            run_dialog(PaymentDueDateChangeDialog, self, store, payment, order)

        if store.committed:
            # We need to refresh the whole list as the payment(s) can possibly
            # disappear for the selected view
            self.refresh()

    def change_status(self, payment_view, order, status):
        """Show a dialog do enter a reason for status change

        :param payment_view: an OutPaymentView or InPaymentView instance
        :param order: a Sale or Purchase instance related to this payment.
          This will be used to show the identifier of the order
        :param status: The new status to set the payment to
        """
        with api.new_store() as store:
            payment = store.fetch(payment_view.payment)
            order = store.fetch(payment_view.sale)

            if order is None:
                order = store.fetch(payment_view.purchase)

            run_dialog(PaymentStatusChangeDialog, self, store, payment, status,
                       order)

        if store.committed:
            # We need to refresh the whole list as the payment(s) can possibly
            # disappear for the selected view
            self.refresh()

    def create_main_filter(self):
        self.main_filter = ComboSearchFilter(_('Show'), [])

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

        self.create_branch_filter(column=self.search_spec.branch_id)

    def add_filter_items(self, category_type, options):
        categories = PaymentCategory.get_by_type(self.store, category_type)
        items = [(_('All payments'), None)]

        if categories.count() > 0:
            options.append(FilterItem('sep', 'sep'))

        items.extend([(item.name, item) for item in options])
        for c in categories:
            item = FilterItem(c.name,
                              'category:%s' % (c.name, ),
                              color=c.color,
                              item_id=c.id)
            items.append((item.name, item))

        self.main_filter.update_values(items)

    #
    # Private
    #

    def _create_main_query(self, state):
        item = state.value
        if item is None:
            return None
        kind, value = item.value.split(':')
        payment_view = self.search_spec
        if kind == 'status':
            if value == 'paid':
                return payment_view.status == Payment.STATUS_PAID
            elif value == 'not-paid':
                return payment_view.status == Payment.STATUS_PENDING
            elif value == 'late':
                tolerance = api.sysparam.get_int('TOLERANCE_FOR_LATE_PAYMENTS')
                return And(
                    payment_view.status == Payment.STATUS_PENDING,
                    payment_view.due_date <
                    localtoday() - relativedelta(days=tolerance))
        elif kind == 'category':
            return payment_view.category == value

        raise AssertionError(kind, value)

    def _show_payment_categories(self):
        store = api.new_store()
        self.run_dialog(PaymentCategoryDialog, store,
                        self.payment_category_type)
        self._update_filter_items()
        store.close()

    #
    # Callbacks
    #

    def _on_main_filter__row_separator_func(self, model, titer):
        if model[titer][0] == 'sep':
            return True
        return False

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

        state = self.main_filter.get_state()

        def show_strikethrough():
            if state.value is None:
                return True
            if state.value.value.startswith('category:'):
                return True
            return False

        is_pending = (pv.status == Payment.STATUS_PENDING)
        show_strikethrough = not is_pending and show_strikethrough()
        is_late = pv.is_late()

        renderer.set_property('strikethrough-set', show_strikethrough)
        renderer.set_property('weight-set', is_late)

        if show_strikethrough:
            renderer.set_property('strikethrough', True)
        if is_late:
            renderer.set_property('weight', Pango.Weight.BOLD)

        return text

    def on_results__activate_link(self, results, uri):
        if uri.startswith('new_payment'):
            if '?' in uri:
                category = urllib.parse.unquote(uri.split('?', 1)[1])
            else:
                category = None
            self.add_payment(category=category)

    def on_PaymentFlowHistory__activate(self, action):
        self.run_dialog(PaymentFlowHistoryDialog, self.store)

    def on_PaymentCategories__activate(self, action):
        self._show_payment_categories()
コード例 #6
0
ファイル: delivery.py プロジェクト: prohealthbox/stoq
class DeliveryApp(ShellApp):
    """Delivery app"""

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

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

    #
    # Application
    #

    def create_actions(self):
        #group = get_accels('app.delivery')
        actions = [
            # File
            ("DeliveryMenu", None, _(u"Delivery")),

            # Search
            (
                "Transporters",
                STOQ_TRANSPORTER,
                _("Transporters..."),
                # group.get("search_transporters")),
                None),
            (
                "Clients",
                STOQ_CLIENTS,
                _("Clients..."),
                # group.get("search_clients")),
                None),
            (
                "Products",
                None,
                _("Products..."),
                # group.get("search_products")),
                None),
            (
                "Services",
                None,
                _("Services..."),
                # group.get("search_services")),
                None),

            # Delivery
            (
                "Edit",
                gtk.STOCK_EDIT,
                _("Edit..."),
                # group.get('delivery_pick'),
                None,
                _("Edit the selected delivery")),
            (
                "Pick",
                None,
                _("Pick..."),
                # group.get('delivery_pick'),
                None,
                _("Pick the selected delivery")),
            (
                "Pack",
                None,
                _("Pack..."),
                # group.get('delivery_pack'),
                None,
                _("Pack the selected delivery")),
            (
                "Send",
                STOQ_TRANSPORTER,
                _("Send..."),
                # group.get('delivery_send'),
                None,
                _("Send the selected delivery to deliver")),
            (
                "Receive",
                gtk.STOCK_APPLY,
                _("Mark as received..."),
                # group.get('delivery_receive'),
                None,
                _("Mark the selected delivery as received by the client")),
            (
                "Cancel",
                gtk.STOCK_CANCEL,
                _("Cancel..."),
                # group.get('delivery_cancel'),
                None,
                _("Cancel the selected delivery")),
        ]
        self.delivery_ui = self.add_ui_actions("",
                                               actions,
                                               filename="delivery.xml")

        self.set_help_section(_(u"Delivery help"), 'app-delivery')
        self.popup = self.uimanager.get_widget('/DeliverySelection')

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

        # XXX: What should we put on new items?
        self.window.add_new_items([])
        self.window.add_search_items([
            self.Products,
            self.Services,
            self.Transporters,
            self.Clients,
        ])

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

        self.results.set_cell_data_func(self._on_results__cell_data_func)

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

        # XXX
        #self.window.NewToolItem.set_tooltip(
        #    _("Create a new delivery"))
        self.window.SearchToolItem.set_tooltip(_("Search for transporters"))

        if refresh:
            self._update_view()

        self.search.focus_search_entry()

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

    def new_activate(self):
        print "FIXME: new_activate"

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

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

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

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

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

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

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

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

    def get_columns(self):
        return [
            IdentifierColumn('sale_identifier', title=_("Sale #")),
            SearchColumn('status_str',
                         title=_(u'Status'),
                         search_attribute='status',
                         data_type=str,
                         valid_values=self._get_status_values()),
            SearchColumn('client_name',
                         title=_(u'Client'),
                         data_type=str,
                         expand=True),
            Column('flag_icon',
                   title=_(u'Status (Description)'),
                   column='client_name',
                   data_type=gtk.gdk.Pixbuf,
                   format_func=self._format_state_icon,
                   format_func_data=True),
            SearchColumn('branch_name',
                         title=_(u'Branch'),
                         data_type=str,
                         visible=False),
            SearchColumn('transporter_name',
                         title=_(u'Transporter'),
                         data_type=str),
            SearchColumn('open_date',
                         title=_(u'Open date'),
                         data_type=datetime.date),
            SearchColumn('cancel_date',
                         title=_(u'Cancel date'),
                         data_type=datetime.date,
                         visible=False),
            SearchColumn('pick_date',
                         title=_(u'Pick date'),
                         data_type=datetime.date,
                         visible=False),
            SearchColumn('pack_date',
                         title=_(u'Pack date'),
                         data_type=datetime.date,
                         visible=False),
            SearchColumn('send_date',
                         title=_(u'Send date'),
                         data_type=datetime.date),
            SearchColumn('receive_date',
                         title=_(u'Receive date'),
                         data_type=datetime.date,
                         visible=False),
        ]

    def set_open_inventory(self):
        pass

    #
    # Private
    #

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

        if store.committed:
            self._update_view()

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

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

        self._update_view(select_item=selection)

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

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

        self._update_view(select_item=selection)

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

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

        self._update_view(select_item=selection)

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

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

        self._update_view(select_item=selection)

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

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

        self._update_view(select_item=selection)

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

        stock_id, tooltip = get_delivery_state_icon(item.delivery)
        if stock_id is not None:
            # We are using self.results because render_icon is a gtk.Widget's
            # method. It has nothing to do with results tough.
            return self.results.render_icon(stock_id, gtk.ICON_SIZE_MENU)

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

        return Delivery.status == state.value

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

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

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

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

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

    #
    #  Callbacks
    #

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

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

        for prop, is_set, value in [('strikethrough', is_finished, True),
                                    ('style', is_picked, pango.STYLE_ITALIC),
                                    ('weight', is_waiting, pango.WEIGHT_BOLD)]:
            renderer.set_property(prop + '-set', is_set)
            if is_set:
                renderer.set_property(prop, value)

        return text

    def on_search__result_item_popup_menu(self, search, item, event):
        self.popup.popup(None, None, None, event.button, event.time)

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

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

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

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

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

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

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

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

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

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

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

    def on_Clients__activate(self, button):
        self.run_dialog(ClientSearch, self.store, hide_footer=True)
コード例 #7
0
class ServicesApp(ShellApp):
    """Services app"""

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

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

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

    #
    # Application
    #

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

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

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

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

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

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

        self.results.set_cell_data_func(self._on_results__cell_data_func)

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

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

        if refresh:
            self._update_view()

        self.search.focus_search_entry()

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

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

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

        if not base_msg:
            return

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

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

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

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

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

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

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

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

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

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

    #
    # Private
    #

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

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

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

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

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

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

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

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

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

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

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

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

    #
    # Kiwi Callbacks
    #

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

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

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

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

        return text

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

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

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

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

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

        self.actions.new_order(category=category)

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

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

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

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

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

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

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

    def on_ViewKanban__change_state(self, action, value):
        action.set_state(value)
        self.ViewList.set_state(
            GLib.Variant.new_boolean(not value.get_boolean()))
        self.window._current_app_settings['show-kanban'] = value.get_boolean()
        if not value.get_boolean():
            return
        self.search.set_result_view(WorkOrderResultKanbanView, refresh=True)
        self._update_list_aware_view()
コード例 #8
0
ファイル: services.py プロジェクト: hackedbellini/stoq
class ServicesApp(ShellApp):
    """Services app"""

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

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

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

    #
    # Application
    #

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

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

            ('', _('Deliver'), 'work_order.Close', False),
            # Separator
            ('', _('Approve'), 'work_order.Approve', False),
            ('', _('Pause the work'), 'work_order.Pause', False),
            ('', _('Start the work'), 'work_order.Work', False),
            ('', _('Reject order'), 'work_order.Reject', False),
            ('', _('Check order'), 'work_order.CheckOrder', False),
            ('', _('Inform client'), 'work_order.InformClient', False),
            ('', _('Undo order rejection'), 'work_order.UndoRejection', False),
            ('', _('Repoen order'), 'work_order.Reopen', False),
            # Separator
            ('', _('Print quote'), 'work_order.PrintQuote', False),
            ('', _('Print receipt'), 'work_order.PrintReceipt', False),
        ]
        return options

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

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

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

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

        self.results.set_cell_data_func(self._on_results__cell_data_func)

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

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

        if refresh:
            self._update_view()

        self.search.focus_search_entry()

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

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

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

        if not base_msg:
            return

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

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

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

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

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

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

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

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

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

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

    #
    # Private
    #

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

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

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

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

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

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

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

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

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

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

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

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

    #
    # Kiwi Callbacks
    #

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

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

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

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

        return text

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

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

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

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

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

        self.actions.new_order(category=category)

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

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

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

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

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

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

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

    def on_ViewKanban__change_state(self, action, value):
        action.set_state(value)
        self.ViewList.set_state(GLib.Variant.new_boolean(not value.get_boolean()))
        self.window._current_app_settings['show-kanban'] = value.get_boolean()
        if not value.get_boolean():
            return
        self.search.set_result_view(WorkOrderResultKanbanView, refresh=True)
        self._update_list_aware_view()