# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
##
## Copyright (C) 2005-2015 Async Open Source <http://www.async.com.br>
## All rights reserved
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU Lesser General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU Lesser General Public License for more details.
##
## You should have received a copy of the GNU Lesser General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., or visit: http://www.gnu.org/.
##
## Author(s): Stoq Team <stoq-devel@async.com.br>
##
##
""" Search dialogs for sale objects """
import datetime
from decimal import Decimal
import pango
import gtk
from kiwi.currency import currency
from kiwi.ui.objectlist import Column
from storm.expr import Count, And
from stoqlib.api import api
from stoqlib.domain.sale import (Sale,
SaleView,
SalePaymentMethodView,
SoldItemsByClient,
SaleToken,
SaleTokenView)
from stoqlib.domain.person import Branch
from stoqlib.domain.till import Till
from stoqlib.domain.views import SoldItemsByBranchView, UnconfirmedSaleItemsView
from stoqlib.domain.workorder import WorkOrder
from stoqlib.exceptions import TillError
from stoqlib.gui.base.dialogs import run_dialog
from stoqlib.gui.base.gtkadds import set_bold
from stoqlib.gui.dialogs.saledetails import SaleDetailsDialog
from stoqlib.gui.editors.saleeditor import SaleTokenEditor
from stoqlib.gui.search.searchcolumns import (IdentifierColumn, SearchColumn,
QuantityColumn)
from stoqlib.gui.search.searcheditor import SearchEditor
from stoqlib.gui.search.searchfilters import (ComboSearchFilter,
DateSearchFilter)
from stoqlib.gui.search.searchdialog import SearchDialog
from stoqlib.lib.translation import stoqlib_gettext
from stoqlib.lib.formatters import (format_quantity, get_formatted_price,
format_phone_number)
from stoqlib.gui.slaves.saleslave import SaleListToolbar
from stoqlib.reporting.sale import (SoldItemsByBranchReport,
SoldItemsByClientReport)
_ = stoqlib_gettext
class _BaseSaleSearch(SearchDialog):
title = _("Search for Sales")
size = (-1, 450)
search_spec = SaleView
text_field_columns = [SaleView.client_name, SaleView.salesperson_name,
SaleView.identifier_str]
branch_filter_column = SaleView.branch_id
#
# SearchDialog Hooks
#
def create_filters(self):
items = [(value, key) for key, value in Sale.statuses.items()]
items.insert(0, (_('Any'), None))
status_filter = ComboSearchFilter(_('Show sales with status'), items)
self.add_filter(status_filter, columns=[SaleView.status])
def get_columns(self):
return [IdentifierColumn('identifier', title=_('Sale #'), sorted=True,
order=gtk.SORT_DESCENDING),
SearchColumn('open_date', title=_('Date Started'), width=110,
data_type=datetime.date, justify=gtk.JUSTIFY_RIGHT),
SearchColumn('client_name', title=_('Client'),
data_type=str, expand=True,
ellipsize=pango.ELLIPSIZE_END),
SearchColumn('salesperson_name', title=_('Salesperson'),
data_type=str, width=150),
SearchColumn('total_quantity', title=_('Items'),
data_type=Decimal, width=60,
format_func=format_quantity),
SearchColumn('total', title=_('Total'), data_type=currency,
width=90)]
[docs]class SaleSearch(_BaseSaleSearch):
#
# Callbacks
#
[docs]class SalesByPaymentMethodSearch(SaleWithToolbarSearch):
title = _(u'Search for Sales by Payment Method')
search_spec = SalePaymentMethodView
search_label = _('Items matching:')
size = (800, 450)
branch_filter_column = Branch.id
text_field_columns = [SalePaymentMethodView.client_name,
SalePaymentMethodView.salesperson_name]
[docs] def create_filters(self):
self.search.set_query(self.executer_query)
payment_filter = self.create_payment_filter(_('Payment Method:'))
self.add_filter(payment_filter, columns=[])
self.payment_filter = payment_filter
# TODO: Maybe this can be removed
[docs] def executer_query(self, store):
method = self.payment_filter.get_state().value
resultset = self.search_spec.find_by_payment_method(store, method)
return resultset
[docs] def get_columns(self):
columns = SaleWithToolbarSearch.get_columns(self)
branches = api.get_branches_for_filter(self.store, use_id=True)
branch_column = SearchColumn('branch_name', title=_('Branch'), width=110,
data_type=str, search_attribute='branch_id',
valid_values=branches)
columns.insert(3, branch_column)
return columns
[docs]class SoldItemsByBranchSearch(SearchDialog):
title = _(u'Sold Items by Branch')
report_class = SoldItemsByBranchReport
search_spec = SoldItemsByBranchView
search_label = _('Items matching:')
size = (800, 450)
unlimited_results = True
text_field_columns = [SoldItemsByBranchView.description]
branch_filter_column = Sale.branch_id
def _setup_summary(self):
hbox = gtk.HBox()
hbox.set_spacing(6)
self.vbox.pack_start(hbox, False, True)
self.vbox.reorder_child(hbox, 2)
self.vbox.set_spacing(6)
hbox.pack_start(gtk.Label(), True, True)
# Create some labels to show a summary for the search (kiwi's
# SummaryLabel supports only one column)
self.items_label = gtk.Label()
self.quantity_label = gtk.Label()
self.items_per_sale_label = gtk.Label()
self.total_label = gtk.Label()
for widget in [self.items_label, self.quantity_label,
self.items_per_sale_label, self.total_label]:
hbox.pack_start(widget, False, False)
set_bold(widget)
hbox.show_all()
def _update_summary(self, results):
total_quantity = total = 0
for obj in results:
total_quantity += obj.quantity
total += obj.total
queries, having = self.search.parse_states()
sale_results = self.store.using(*self.search_spec.tables)
sale_results = sale_results.find(Count(Sale.id, distinct=True))
if queries:
sale_results = sale_results.find(And(*queries))
sales = sale_results.one()
items_per_sale = total_quantity / sales if sales > 0 else 0
self.items_label.set_label(_(u'Sales: %s') %
format_quantity(sales))
self.quantity_label.set_label(_(u'Quantity: %s') %
format_quantity(total_quantity))
self.items_per_sale_label.set_label(_(u'Items per sale: %s') %
format_quantity(items_per_sale))
self.total_label.set_label(_(u'Total: %s') %
get_formatted_price(total))
[docs] def create_filters(self):
date_filter = DateSearchFilter(_('Date:'))
self.search.add_filter(date_filter, columns=[Sale.confirm_date])
self.date_filter = date_filter
[docs] def get_columns(self):
return [SearchColumn('code', title=_('Code'), data_type=str,
sorted=True, order=gtk.SORT_DESCENDING),
SearchColumn('description', title=_('Product'), data_type=str,
expand=True),
SearchColumn('category', title=_('Category'), data_type=str,
visible=False),
SearchColumn('branch_name', title=_('Branch'), data_type=str,
width=200),
Column('quantity', title=_('Quantity'), data_type=Decimal,
format_func=format_quantity, width=100),
Column('total', title=_('Total'), data_type=currency, width=80)
]
[docs] def on_search__search_completed(self, search, result_view, states):
self._update_summary(result_view)
[docs]class SoldItemsByClientSearch(SearchDialog):
title = _(u'Sold Items by Client')
report_class = SoldItemsByClientReport
search_spec = SoldItemsByClient
size = (800, 450)
unlimited_results = True
text_field_columns = [SoldItemsByClient.client_name,
SoldItemsByClient.description,
SoldItemsByClient.code]
branch_filter_column = Sale.branch_id
[docs] def create_filters(self):
self._date_filter = DateSearchFilter(_("Date:"))
self._date_filter.select(data=DateSearchFilter.Type.USER_INTERVAL)
self.add_filter(self._date_filter, columns=[Sale.confirm_date])
self.search.set_summary_label('quantity', label=_(u'Total:'),
format='<b>%s</b>')
[docs] def get_columns(self):
columns = [
SearchColumn('code', title=_('Code'), data_type=str, sorted=True,
order=gtk.SORT_DESCENDING),
SearchColumn('description', title=_('Description'),
data_type=str, expand=True),
SearchColumn('client_name', title=_('Client'), data_type=str),
SearchColumn('phone_number', title=_('Phone'), data_type=str,
visible=False, format_func=format_phone_number),
SearchColumn('email', title=_('Email'), data_type=str,
visible=False),
SearchColumn('sellable_category', title=_('Category'), data_type=str,
visible=False),
QuantityColumn('quantity', title=_('Qty'), use_having=True),
SearchColumn('price', title=_('Avg price'), data_type=currency,
use_having=True),
SearchColumn('total', title=_('Total'), data_type=currency,
use_having=True)
]
return columns
[docs]class UnconfirmedSaleItemsSearch(SearchDialog):
title = _(u'Unconfirmed Sale Items Search')
search_spec = UnconfirmedSaleItemsView
size = (850, 450)
branch_filter_column = Sale.branch_id
unlimited_results = True
text_field_columns = [UnconfirmedSaleItemsView.description,
UnconfirmedSaleItemsView.salesperson_name,
UnconfirmedSaleItemsView.client_name]
[docs] def get_columns(self):
return [IdentifierColumn('identifier', title=_('Sale #'),
sorted=True, order=gtk.SORT_DESCENDING),
SearchColumn('status_str', title=_('Status'),
search_attribute='status',
valid_values=self._get_status_values(), data_type=str),
SearchColumn('product_code', title=_('Code'), data_type=str),
SearchColumn('product_category', title=_('Category'), data_type=str),
SearchColumn('description', title=_('Product'), data_type=str,
expand=True),
SearchColumn('manufacturer_name', title=_('Manufacturer'),
data_type=str, expand=True, visible=False),
SearchColumn('salesperson_name', title=_('Sales Person'),
data_type=str, visible=False),
SearchColumn('client_name', title=_('Client'), data_type=str,
visible=False),
SearchColumn('open_date', title=_('Open Date'),
data_type=datetime.date),
SearchColumn('price', title=_('Price'), data_type=currency,),
SearchColumn('quantity', title=_('Quantity'), data_type=Decimal,
format_func=format_quantity),
SearchColumn('quantity_decreased', title=_('Delivered'),
data_type=Decimal, format_func=format_quantity,
visible=False),
SearchColumn('total', title=_('Total'), data_type=currency,),
IdentifierColumn('wo_identifier', title=_('WO #'),
visible=False, justify=gtk.JUSTIFY_RIGHT),
SearchColumn('wo_status_str', title=_('WO Status'), data_type=str,
search_attribute='wo_status', visible=False,
valid_values=self._get_wo_status_values()),
SearchColumn('wo_estimated_finish', title=_('Estimated finish'),
data_type=datetime.date, visible=False),
SearchColumn('wo_finish', title=_('WO Finish date'),
data_type=datetime.date, visible=False)]
def _setup_summary(self):
hbox = gtk.HBox()
hbox.set_spacing(6)
self.vbox.pack_start(hbox, False, True)
self.vbox.reorder_child(hbox, 2)
self.vbox.set_spacing(6)
hbox.pack_start(gtk.Label(), True, True)
# Create some labels to show a summary for the search (kiwi's
# SummaryLabel supports only one column)
self.quantity_label = gtk.Label()
self.reserved_label = gtk.Label()
self.total_label = gtk.Label()
for widget in [self.quantity_label, self.reserved_label,
self.total_label]:
hbox.pack_start(widget, False, False)
set_bold(widget)
hbox.show_all()
def _update_summary(self, results):
total_quantity = reserved_quantity = total_price = 0
for obj in results:
total_quantity += obj.quantity
reserved_quantity += obj.quantity_decreased
total_price += obj.total
self.quantity_label.set_label(_(u'Quantity: %s') %
format_quantity(total_quantity))
self.reserved_label.set_label(_(u'Delivered: %s') %
format_quantity(reserved_quantity))
self.total_label.set_label(_(u'Total: %s') %
get_formatted_price(total_price))
def _get_status_values(self):
items = [(value, key) for key, value in Sale.statuses.items()
if key in [Sale.STATUS_ORDERED, Sale.STATUS_QUOTE]]
items.insert(0, (_('Any'), None))
return items
def _get_wo_status_values(self):
items = [(value, key) for key, value in WorkOrder.statuses.items()]
items.insert(0, (_('Any'), None))
return items
[docs] def on_search__search_completed(self, search, result_view, states):
self._update_summary(result_view)
[docs]class SaleTokenSearch(SearchEditor):
title = _(u'Sale Token Search')
size = (600, 500)
search_spec = SaleTokenView
editor_class = SaleTokenEditor
text_field_columns = [SaleTokenView.name,
SaleTokenView.code,
SaleTokenView.client_name,
SaleTokenView.branch_name]
def __init__(self, store, search_str=None, hide_toolbar=False,
hide_footer=False):
SearchEditor.__init__(self, store, search_spec=self.search_spec,
editor_class=self.editor_class,
hide_toolbar=hide_toolbar,
hide_footer=hide_footer)
[docs] def get_columns(self):
status_values = ([(_('Any'), None)] +
[(v, k) for k, v in SaleToken.statuses.items()])
return [
SearchColumn('name', title=_('Name'), data_type=str,
sorted=True, expand=True),
SearchColumn('code', title=_('Code'), data_type=str),
SearchColumn('branch_name', title=_('Branch'), data_type=str,
visible=False),
SearchColumn('client_name', title=_('Client'), data_type=str,
expand=True),
SearchColumn('status_str', title=_('Status'), data_type=str,
search_attribute='status',
valid_values=status_values),
]
[docs] def get_editor_model(self, model):
return model.sale_token