def search_for_date(self, date): dfilter = DateSearchFilter(_("Expected receival date")) dfilter.set_removable() dfilter.select(data=DateSearchFilter.Type.USER_DAY) self.add_filter(dfilter, columns=["expected_receival_date"]) dfilter.start_date.set_date(date) self.refresh()
def add_filter_by_attribute(self, attr, title, data_type, valid_values=None, callback=None, use_having=False, multiple_selection=False): """Add a filter accordingly to the attributes specified :param attr: attribute that will be filtered. This can be either the name of the attribute or the attribute itself. :param title: the title of the filter that will be visible in the interface :param data_type: the attribute type (str, bool, decimal, etc) :param callback: the callback function that will be triggered :param use_having: use having expression in the query """ if data_type is not bool: title += ':' if data_type == datetime.date: filter = DateSearchFilter(title) if valid_values: filter.clear_options() filter.add_custom_options() for opt in valid_values: filter.add_option(opt) filter.select(valid_values[0]) elif (data_type == decimal.Decimal or data_type == int or data_type == currency): filter = NumberSearchFilter(title) if data_type != int: filter.set_digits(2) elif data_type == str: if multiple_selection: filter = MultiSearchFilter(title, valid_values) elif valid_values: filter = ComboSearchFilter(title, valid_values) filter.enable_advanced() else: filter = StringSearchFilter(title) filter.enable_advanced() elif data_type == bool: filter = BoolSearchFilter(title) else: raise NotImplementedError(title, data_type) filter.set_removable() self.add_filter(filter, columns=[attr], callback=callback, use_having=use_having) if data_type is not bool: label = filter.get_title_label() label.set_alignment(1.0, 0.5) self.label_group.add_widget(label) combo = filter.get_mode_combo() if combo: self.combo_group.add_widget(combo) return filter
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 def setup_widgets(self): self.add_csv_button(_('Sale items'), _('sale items')) 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>') def get_columns(self): columns = [ SearchColumn('code', title=_('Code'), data_type=str, sorted=True, order=Gtk.SortType.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
def search_for_date(self, date): self.main_filter.select(None) dfilter = DateSearchFilter(_("Paid or due date")) dfilter.set_removable() dfilter.select(data=DateSearchFilter.Type.USER_DAY) self.add_filter(dfilter, columns=["paid_date", "due_date"]) dfilter.start_date.set_date(date) self.refresh()
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 create_filters(self): # Date date_filter = DateSearchFilter(_('Date:')) date_filter.select(Today) columns = [ ProductHistory.sold_date, ProductHistory.received_date, ProductHistory.production_date, ProductHistory.decreased_date ] self.add_filter(date_filter, columns=columns) self.date_filter = date_filter
def create_filters(self): self.set_text_field_columns(['description', 'identifier_str']) self.search.set_query(self.executer_query) # Date date_filter = DateSearchFilter(_('Date:')) date_filter.select(0) columns = [Payment.due_date, Payment.open_date, Payment.paid_date] self.add_filter(date_filter, columns=columns) self.date_filter = date_filter
def create_filters(self): # Extra filters (that are not columns) self.search.add_filter_option(SellableCategory.description, title=_(u"Product category"), data_type=str) self.search.add_filter_option(Sellable.description, title=_(u"Product"), data_type=str) # Date date_filter = DateSearchFilter(_('Date:')) date_filter.select(Today) self.add_filter(date_filter, columns=[Sale.confirm_date])
class DateRangeDialog(BasicDialog): """A simple dialog for selecting a date range When confirmed, a :class:`date_range` object will be returned containig the information about the date range selected """ title = _(u'Select a date range') size = (-1, -1) def __init__(self, title=None, header_text=None): title = title or self.title header_text = '<b>%s</b>' % header_text if header_text else '' BasicDialog.__init__(self, title=title, header_text=header_text) self._setup_widgets() # # BasicDialog # def confirm(self): BasicDialog.confirm(self) state = self.date_filter.get_state() if isinstance(state, DateQueryState): start, end = state.date, state.date else: start, end = state.start, state.end self.retval = date_range(start=start, end=end) # # Private # def _setup_widgets(self): self.date_filter = DateSearchFilter(_(u'Date:')) # FIXME: add a remove_option method in DateSearchFilter. self.date_filter.clear_options() self.date_filter.add_custom_options() for option in [Today, Yesterday, LastWeek, LastMonth]: self.date_filter.add_option(option) self.date_filter.select(position=0) self.vbox.pack_start(self.date_filter, False, False, 0) self.date_filter.show_all()
class FinancialReportDialog(BasicDialog): title = _('Financial Report Dialog') def __init__(self, store): self.store = store self.date_filter = DateSearchFilter(_('Year:')) self.date_filter.clear_options() self._populate_date_filter(self.date_filter) self.date_filter.select() self.date_filter.set_use_date_entries(False) BasicDialog.__init__(self, title=self.title) self.main_label.set_justify(Gtk.Justification.CENTER) self.ok_button.set_label(_("Generate")) self.add(self.date_filter) self.date_filter.show() def confirm(self): start = self.date_filter.get_start_date() if start is None: warning(_("There are no transactions yet")) return f = FinancialIntervalReport(self.store, start.year) if not f.run(): return temporary = tempfile.NamedTemporaryFile( # Translators: This will be part of a filename prefix=_('stoq-yearly-report'), suffix='.xls', delete=False) f.write(temporary) sse = SpreadSheetExporter() sse.export_temporary(temporary) self.close() self.temporary = temporary # # Private # def _populate_date_filter(self, date_filter): transaction = self.store.find(AccountTransaction).order_by( AccountTransaction.date).first() if transaction is None: return for i in range(transaction.date.year, localtoday().year + 1): year = datetime.datetime(i, 1, 1) date_filter.add_option_fixed_interval( _('Year %d') % (i, ), year, year.replace(month=12, day=31), position=0) def _date_filter_query(self, search_spec, column): executer = QueryExecuter(self.store) executer.set_filter_columns(self.date_filter, [column]) executer.set_search_spec(search_spec) return executer.search([self.date_filter.get_state()])
class ProductsSoldSearch(ProductSearch): title = _('Products Sold Search') search_spec = SoldItemView report_class = ProductsSoldReport csv_data = None has_print_price_button = False advanced_search = False text_field_columns = [SoldItemView.description] branch_filter_column = Sale.branch_id def __init__(self, store, hide_footer=True, hide_toolbar=True): ProductSearch.__init__(self, store, hide_footer=hide_footer, hide_toolbar=hide_toolbar) # # Private API # def _update_summary(self, klist): quantity = total = 0 for obj in klist: quantity += obj.quantity total += obj.total_sold self.quantity_label.set_label( _(u'Total quantity: %s') % format_quantity(quantity)) self.total_sold_label.set_label( _(u'Total sold: %s') % get_formatted_cost(total)) # # ProductSearch # def setup_widgets(self): super(ProductSearch, self).setup_widgets() hbox = Gtk.HBox() hbox.set_spacing(6) self.vbox.pack_start(hbox, False, True, 0) self.vbox.reorder_child(hbox, 2) self.vbox.set_spacing(6) label = Gtk.Label() hbox.pack_start(label, True, True, 0) # Create two labels to show a summary for the search (kiwi's # SummaryLabel supports only one column) self.quantity_label = Gtk.Label() hbox.pack_start(self.quantity_label, False, False, 0) self.total_sold_label = Gtk.Label() hbox.pack_start(self.total_sold_label, False, False, 0) hbox.show_all() set_bold(self.quantity_label) set_bold(self.total_sold_label) def create_filters(self): self.date_filter = DateSearchFilter(_('Date:')) self.date_filter.select(Today) self.add_filter(self.date_filter, columns=[Sale.confirm_date]) def get_columns(self): return [ Column('code', title=_('Code'), data_type=str, sorted=True), Column('description', title=_('Description'), data_type=str, expand=True), QuantityColumn('quantity', title=_('Sold')), Column('average_cost', title=_('Avg. Cost'), data_type=currency), Column('total_sold', title=_('Total sold'), data_type=currency) ] # # Callbacks # def on_search__search_completed(self, search, result_view, states): self._update_summary(result_view)
class TillHistoryDialog(SearchDialog): size = (780, -1) search_spec = TillEntry selection_mode = Gtk.SelectionMode.MULTIPLE searchbar_labels = _('Till Entries matching:') title = _('Till history') # # SearchDialog # def get_columns(self, *args): return [IdentifierColumn('identifier', title=_('Entry #'), sorted=True), Column('date', _('Date'), data_type=datetime.date), Column('time', _('Time'), data_type=datetime.time), Column('description', _('Description'), data_type=str, expand=True), ColoredColumn('value', _('Value'), data_type=currency, color='red', data_func=payment_value_colorize, width=140)] def create_filters(self): self.set_text_field_columns(['description']) self.date_filter = DateSearchFilter(_('Date:')) self.date_filter.select(Today) self.add_filter(self.date_filter, columns=['date']) # add summary label value_format = '<b>%s</b>' total_label = '<b>%s</b>' % stoq_api.escape(_(u'Total:')) self.search.set_summary_label('value', total_label, value_format) def setup_widgets(self): self.results.set_visible_rows(10) self.results.connect('has-rows', self._has_rows) self._add_editor_button(_('Cash _Add...'), CashAdvanceEditor) self._add_editor_button(_('Cash _In...'), CashInEditor) self._add_editor_button(_('Cash _Out...'), CashOutEditor) self.print_button = Gtk.Button.new_from_stock(Gtk.STOCK_PRINT) self.print_button.set_property("use-stock", True) self.print_button.connect('clicked', self._print_button_clicked) self.action_area.set_layout(Gtk.ButtonBoxStyle.START) self.action_area.pack_end(self.print_button, False, False, 6) self.print_button.show() self.print_button.set_sensitive(False) # # Private API # def _add_editor_button(self, name, editor_class): button = self.add_button(name) button.connect('clicked', self._run_editor, editor_class) button.show() def _print_button_clicked(self, button): print_report(TillHistoryReport, self.results, list(self.results), filters=self.search.get_search_filters()) def _run_editor(self, button, editor_class): with api.new_store() as store: run_dialog(editor_class, self, store) if store.committed: self.search.refresh() self.results.unselect_all() if len(self.results): self.results.select(self.results[-1]) def _has_rows(self, results, obj): self.print_button.set_sensitive(obj)
class TillDailyMovementDialog(BaseEditor): """Shows informations related to till operations over a daterange. It can also be filtered by branch. """ title = _("Daily Movement") hide_footer = True size = (950, 450) model_type = Settable gladefile = "TillDailyMovementDialog" proxy_widgets = [ 'branch', 'in_subtotal', 'in_credit', 'in_total', 'out_subtotal', 'out_credit', 'out_total' ] # # Private # def _setup_widgets(self): # Branches combo items = stoq_api.get_branches_for_filter(self.store) self.branch.prefill(items) # Daterange filter self.date_filter = DateSearchFilter(_(u'Date:')) self.date_filter.clear_options() self.date_filter.add_custom_options() for option in [Today, Yesterday, LastWeek, LastMonth]: self.date_filter.add_option(option) self.date_filter.select(position=0) self.daterange_hbox.pack_start(self.date_filter, False, False, 0) self.date_filter.show_all() # Setting report lists' columns self.sales_list.set_columns(self._get_sales_columns()) self.inpayments_list.set_columns(self._get_lonely_payments_columns()) self.purchases_list.set_columns(self._get_purchases_columns()) self.outpayments_list.set_columns(self._get_lonely_payments_columns()) self.return_sales_list.set_columns(self._get_return_sales_columns()) self.supplies_list.set_columns(self._get_till_columns()) self.removals_list.set_columns(self._get_till_columns()) self.permethod_list.set_columns(self._get_permethod_columns()) self.percard_list.set_columns(self._get_percard_columns()) # Print button is insensitive, until the first report is generated self.print_button.set_sensitive(False) self._setup_summary_labels() def _get_sales_columns(self): return [ IdentifierColumn('identifier', title=_('Sale #'), sorted=True), Column('salesperson', title=_('Sales Person'), data_type=str), Column('client', title=_('Client'), data_type=str, expand=True), Column('branch', title=_('Branch'), data_type=str, visible=False), Column('value', title=_('Value'), data_type=str, justify=Gtk.Justification.RIGHT) ] def _get_lonely_payments_columns(self): return [ IdentifierColumn('identifier', title=_('Payment #'), sorted=True), Column('method', title=_('Method'), data_type=str), Column('description', title=_('Description'), expand=True, data_type=str), Column('branch', title=_('Branch'), data_type=str, visible=False), Column('value', title=_('Payment Value'), data_type=currency) ] def _get_purchases_columns(self): return [ IdentifierColumn('identifier', title=_('Code #'), sorted=True), Column('status_str', title=_('Status'), data_type=str), Column('responsible_name', title=_('Responsible'), expand=True, data_type=str), Column('branch_name', title=_('Branch'), data_type=str), Column('notes', title=_('Notes'), data_type=str), Column('supplier_name', title=_('Supplier'), data_type=str), Column('purchase_total', title=_('Value'), data_type=currency) ] def _get_return_sales_columns(self): return [ IdentifierColumn('identifier', title=_('Code #'), sorted=True), Column('salesperson', title=_('Sales Person'), data_type=str), Column('client', title=_('Client'), expand=True, data_type=str), Column('return_date', title=_('Return Date'), data_type=datetime.date), Column('branch', title=_('Branch'), data_type=str, visible=False), Column('value', title=_('Sale Value'), data_type=currency) ] def _get_till_columns(self): return [ IdentifierColumn('identifier', title=_('Entry #'), sorted=True), Column('description', title=_('Description'), data_type=str, expand=True), Column('branch_name', title=_('Branch'), data_type=str, visible=False), Column('value', title=_('Value'), data_type=currency) ] def _get_permethod_columns(self): return [ Column('method', title=_('Payment Method'), sorted=True, expand=True), Column('in_value', title=_('Income Total'), data_type=currency), Column('out_value', title=_('Outgoing Total'), data_type=currency) ] def _get_percard_columns(self): return [ Column('provider', title=_('Provider Name'), data_type=str, expand=True), Column('income', title=_('Income Total'), data_type=currency) ] def _create_summary_label(self, report, column='value', label=None): # Setting tha data obj_list = getattr(self, report + '_list') box = getattr(self, report + '_vbox') if label is None: label = _('Total:') label = '<b>%s</b>' % stoq_api.escape(label) value_format = '<b>%s</b>' # Creating the label label = SummaryLabel(klist=obj_list, column=column, label=label, value_format=value_format) # Displaying the label box.pack_start(label, False, False, 0) label.show() return label def _setup_summary_labels(self): # Supplies self.supplies_label = self._create_summary_label('supplies') # Removals self.removals_label = self._create_summary_label('removals') # Percard self.percard_label = self._create_summary_label('percard', column='income') def _update_summary_labels(self): self.supplies_label.update_total() self.removals_label.update_total() self.percard_label.update_total() self.proxy.update_many(('in_subtotal', 'in_credit', 'in_total', 'out_subtotal', 'out_credit', 'out_total')) def _generate_dailymovement_data(self, store): query = And( Payment.status.is_in([Payment.STATUS_PENDING, Payment.STATUS_PAID]), self._get_query(Payment.open_date, Payment.branch)) # Keys are the sale objects, and values are lists with all payments self.sales = collections.OrderedDict() # Keys are the returned sale objects, and values are lists with all payments self.return_sales = collections.OrderedDict() self.purchases = collections.OrderedDict() # lonely input and output payments self.lonely_in_payments = [] self.lonely_out_payments = [] # values are lists with the first element the summary of the input, and # the second the summary of the output method_summary = {} self.card_summary = {} result = store.find(DailyInPaymentView, query) for p in result.order_by(Sale.identifier, Payment.identifier): if p.sale: subtotal = p.sale_subtotal total = p.sale.get_total_sale_amount(subtotal) salesperson = p.salesperson_name or _('Not Specified') client = p.client_name or _('Not Specified') sale = DailyMovementSale(identifier=p.sale.identifier, salesperson=salesperson, client=client, branch=p.branch_name, value=get_formatted_price(total)) sale_payments = self.sales.setdefault( sale, collections.OrderedDict()) details = '' method_desc = p.method.get_description() if p.check_data: account = p.check_data.bank_account numbers = sorted(payment.payment_number for payment in p.sale.payments if bool(payment.payment_number)) # Ensure that the check numbers are ordered parts = [] if account.bank_number: parts.append(_(u'Bank: %s') % account.bank_number) if account.bank_branch: parts.append(_(u'Agency: %s') % account.bank_branch) if account.bank_account: parts.append(_(u'Account: %s') % account.bank_account) if numbers: parts.append(_(u'Numbers: %s') % ', '.join(numbers)) details = ' / '.join(parts) if p.card_data: if p.card_data.card_type == CreditCardData.TYPE_DEBIT: method_desc += ' ' + _('Debit') else: method_desc += ' ' + _(u'Credit') details = '%s - %s - %s' % ( p.card_data.auth, p.card_data.provider.short_name or '', p.card_data.device.description or '') key = (method_desc, details) item = sale_payments.setdefault(key, [0, 0]) item[0] += p.value item[1] += 1 else: self.lonely_in_payments.append(p) method_summary.setdefault(p.method, [0, 0]) method_summary[p.method][0] += p.value if p.card_data: type_desc = p.card_data.short_desc[p.card_data.card_type] key = (p.card_data.provider.short_name, type_desc) self.card_summary.setdefault(key, 0) self.card_summary[key] += p.value result = store.find(DailyOutPaymentView, query) for p in result.order_by(Payment.identifier): if p.purchase: purchase_payments = self.purchases.setdefault(p.purchase, []) purchase_payments.append(p) elif p.sale: subtotal = p.sale_subtotal value = p.sale.get_total_sale_amount(subtotal) salesperson = p.salesperson_name or _('Not Specified') client = p.client_name or _('Not Specified') sale = DailyMovementSale(identifier=p.sale.identifier, salesperson=salesperson, client=client, return_date=p.sale.return_date, branch=p.branch_name, value=value) return_sales_payment = self.return_sales.setdefault(sale, []) return_sales_payment.append(p) else: self.lonely_out_payments.append(p) method_summary.setdefault(p.method, [0, 0]) method_summary[p.method][1] += p.value self.method_summary = [] for method, (in_value, out_value) in method_summary.items(): self.method_summary.append((method, in_value, out_value)) self.method_summary.sort(key=lambda m: _(m[0].description)) # Till removals query = And(Eq(TillEntry.payment_id, None), self._get_query(TillEntry.date, TillEntry.branch), TillEntry.value < 0) self.till_removals = store.find(TillEntry, query) # Till supply query = And(Eq(TillEntry.payment_id, None), self._get_query(TillEntry.date, TillEntry.branch), TillEntry.value > 0) self.till_supplies = store.find(TillEntry, query) def _show_lonely_payments(self, payments, widget): widget.clear() for payment in payments: payment_data = Settable(identifier=payment.identifier, method=payment.method.get_description(), description=payment.description, branch=payment.branch_name, value=payment.value) widget.append(payment_data) def _show_report(self): self._generate_dailymovement_data(self.store) # Sale data self.sales_list.clear() for sale, payments in self.sales.items(): self.sales_list.append(None, sale) for details, values in payments.items(): value = '%s (%sx)' % (get_formatted_price( values[0]), values[1]) payment_data = Settable(identifier=None, salesperson=details[0], client=details[1], value=value) self.sales_list.append(sale, payment_data) # Lonely in payments self._show_lonely_payments(self.lonely_in_payments, self.inpayments_list) # Purchase data self.purchases_list.clear() for purchase, payments in self.purchases.items(): self.purchases_list.append(None, purchase) for payment in payments: # TODO Add details refering to Bank, Agency later payment_data = Settable(identifier=payment.identifier, notes=payment.method.get_description()) self.purchases_list.append(purchase, payment_data) # Lonely out payments self._show_lonely_payments(self.lonely_out_payments, self.outpayments_list) # Return sales self.return_sales_list.clear() for sale, payments in self.return_sales.items(): self.return_sales_list.append(None, sale) for payment in payments: payment_data = Settable( identifier=payment.identifier, salesperson=payment.method.get_description(), client=payment.description, value=get_formatted_price(payment.value)) self.return_sales_list.append(sale, payment_data) # Supplies self.supplies_list.clear() self.supplies_list.add_list(self.till_supplies) # Removals self.removals_list.clear() self.removals_list.add_list(self.till_removals) # Summary's per payment method data self.permethod_list.clear() self.model.in_subtotal = self.model.out_subtotal = 0 self.model.in_credit = self.model.out_credit = currency(0) for method in self.method_summary: method_data = Settable(method=_(method[0].description), in_value=method[1], out_value=method[2]) self.permethod_list.append(method_data) self.model.in_subtotal += method[1] self.model.out_subtotal += method[2] if method[0].method_name == 'credit': self.model.in_credit = currency(method[1]) self.model.out_credit = currency(method[2]) self.model.in_subtotal = currency(self.model.in_subtotal) self.model.out_subtotal = currency(self.model.out_subtotal) self.model.in_total = currency(self.model.in_subtotal - self.model.in_credit) self.model.out_total = currency(self.model.out_subtotal - self.model.out_credit) # Summary's per card provider data self.percard_list.clear() keys = list(self.card_summary.keys()) for key in sorted(keys): card_summary_data = Settable(provider=key[0] + ' ' + key[1], income=self.card_summary[key]) self.percard_list.append(card_summary_data) self._update_summary_labels() def _get_query(self, date_attr, branch_attr): daterange = self.get_daterange() query = [ Date(date_attr) >= Date(daterange[0]), Date(date_attr) <= Date(daterange[1]) ] branch = self.model.branch if branch is not None: query.append(branch_attr == branch) return And(*query) # # Public API # def get_daterange(self): start = self.date_filter.get_start_date() end = self.date_filter.get_end_date() return (start, end) def set_daterange(self, start, end=None): self.date_filter.set_state(start, end) # # BaseEditor Hooks # def create_model(self, store): return Settable(branch=api.get_current_branch(store), in_total=currency(0), in_credit=currency(0), in_subtotal=currency(0), out_total=currency(0), out_credit=currency(0), out_subtotal=currency(0)) def setup_proxies(self): self._setup_widgets() self.proxy = self.add_proxy(self.model, TillDailyMovementDialog.proxy_widgets) # # Callbacks # def on_search_button__clicked(self, widget): self._show_report() self.print_button.set_sensitive(True) def on_print_button__clicked(self, widget): branch = self.model.branch daterange = self.get_daterange() print_report(TillDailyMovementReport, self.store, branch, daterange, self)
class SintegraDialog(BasicDialog): title = _('Fiscal Printer History') def __init__(self, store): BasicDialog.__init__(self, title=self.title) self.main_label.set_justify(Gtk.Justification.CENTER) self.store = store self.ok_button.set_label(_("Generate")) self.date_filter = DateSearchFilter(_('Month:')) self.date_filter.set_use_date_entries(False) self.date_filter.clear_options() self._populate_date_filter(self.date_filter) self.date_filter.select() self.add(self.date_filter) self.date_filter.show() def confirm(self): start = self.date_filter.get_start_date() end = self.date_filter.get_end_date() filename = save(_("Save Sintegra file"), self.get_toplevel(), "sintegra-%s.txt" % (start.strftime('%Y-%m'), )) if not filename: return try: generator = StoqlibSintegraGenerator(self.store, start, end) generator.write(filename) except SintegraError as e: warning(str(e)) return self.close() # # Private # def _populate_date_filter(self, date_filter): # The options we want to show to the users are the following: # 'May 2007' # 'June 2007' # ... # 'September 2008' initial_date = self.store.find(SystemTable).min( SystemTable.updated).date() # Start is the first day of the month # End is the last day of the month start = initial_date + relativedelta(day=1) end = localtoday().date() + relativedelta(day=31) intervals = [] while start < end: intervals.append((start, start + relativedelta(day=31))) start = start + relativedelta(months=1) # When we have the list of intervals, add them to the list and # make sure that they are translated month_names = get_month_names() for start, end in intervals: # Translators: Do not translate 'month' and 'year'. You can # change it's positions. In the way it is, # it will product for example 'December 2012' name = _('{month} {year}').format(month=month_names[start.month - 1], year=start.year) date_filter.add_option_fixed_interval(name, start, end, position=0) def _date_filter_query(self, search_spec, column): executer = QueryExecuter(self.store) executer.set_filter_columns(self.date_filter, [column]) executer.set_table(search_spec) return executer.search([self.date_filter.get_state()])
class TransactionPage(object): # shows either a list of: # - transactions # - payments def __init__(self, model, app, parent): self.model = model self.app = app self.parent_window = parent self._block = False self._create_search() self._add_date_filter() self._setup_search() self.refresh() def get_toplevel(self): return self.parent_window def _create_search(self): self.search = SearchSlave(self._get_columns(self.model.kind), store=self.app.store) self.search.connect('result-item-activated', self._on_search__item_activated) self.search.enable_advanced_search() self.search.set_result_view(FinancialSearchResults) self.result_view = self.search.result_view self.result_view.page = self self.result_view.set_cell_data_func( self._on_result_view__cell_data_func) tree_view = self.search.result_view.get_treeview() tree_view.set_rules_hint(True) tree_view.set_grid_lines(Gtk.TreeViewGridLines.BOTH) def _add_date_filter(self): self.date_filter = DateSearchFilter(_('Date:')) self.date_filter.clear_options() self.date_filter.add_option(Any, 0) year = datetime.datetime.today().year month_names = get_month_names() for i, month in enumerate(month_names): name = month_names[i] option = type(name + 'Option', (MonthOption, ), { 'name': _(name), 'month': i + 1, 'year': year }) self.date_filter.add_option(option, i + 1) self.date_filter.add_custom_options() self.date_filter.select(Any) self.search.add_filter(self.date_filter) def _append_date_query(self, field): date = self.date_filter.get_state() queries = [] if isinstance(date, DateQueryState) and date.date is not None: queries.append(Date(field) == date.date) elif isinstance(date, DateIntervalQueryState): queries.append(Date(field) >= date.start) queries.append(Date(field) <= date.end) return queries def _payment_query(self, store): executer = self.search.get_query_executer() search_spec = executer.search_spec queries = self._append_date_query(search_spec.due_date) if queries: return store.find(search_spec, And(*queries)) return store.find(search_spec) def _transaction_query(self, store): queries = [ Or(self.model.id == AccountTransaction.account_id, self.model.id == AccountTransaction.source_account_id) ] queries.extend(self._append_date_query(AccountTransaction.date)) return store.find(AccountTransactionView, And(*queries)) def show(self): self.search.show() def _setup_search(self): if self.model.kind == 'account': self.search.set_search_spec(AccountTransactionView) self.search.set_text_field_columns(['description']) self.search.set_query(self._transaction_query) elif self.model.kind == 'payable': self.search.set_text_field_columns( ['description', 'supplier_name']) self.search.set_search_spec(OutPaymentView) self.search.set_query(self._payment_query) elif self.model.kind == 'receivable': self.search.set_text_field_columns(['description', 'drawee']) self.search.set_search_spec(InPaymentView) self.search.set_query(self._payment_query) else: raise TypeError("unknown model kind: %r" % (self.model.kind, )) def refresh(self): self.search.refresh() def _get_columns(self, kind): if kind in ['payable', 'receivable']: return self._get_payment_columns() else: return self._get_account_columns() def _on_result_view__cell_data_func(self, column, renderer, account_view, text): if not isinstance(renderer, Gtk.CellRendererText): return text if self.model.kind != 'account': return text trans = account_view.transaction is_imbalance = self.app._imbalance_account_id in [ trans.dest_account_id, trans.source_account_id ] renderer.set_property('weight-set', is_imbalance) if is_imbalance: renderer.set_property('weight', Pango.Weight.BOLD) return text def _get_account_columns(self): def format_withdrawal(value): if value < 0: return currency(abs(value)).format(symbol=True, precision=2) def format_deposit(value): if value > 0: return currency(value).format(symbol=True, precision=2) if self.model.account_type == Account.TYPE_INCOME: color_func = lambda x: False else: color_func = lambda x: x < 0 return [ Column('date', title=_("Date"), data_type=datetime.date, sorted=True), Column('code', title=_("Code"), data_type=str), Column('description', title=_("Description"), data_type=str, expand=True), Column('account', title=_("Account"), data_type=str), Column('value', title=self.model.account.get_type_label(out=False), data_type=currency, format_func=format_deposit), Column('value', title=self.model.account.get_type_label(out=True), data_type=currency, format_func=format_withdrawal), ColoredColumn('total', title=_("Total"), data_type=currency, color='red', data_func=color_func) ] def _get_payment_columns(self): return [ SearchColumn('due_date', title=_("Due date"), data_type=datetime.date, sorted=True), IdentifierColumn('identifier', title=_("Payment #")), SearchColumn('description', title=_("Description"), data_type=str, expand=True), SearchColumn('value', title=_("Value"), data_type=currency) ] def append_transactions(self, transactions): for transaction in transactions: description = transaction.get_account_description(self.model) value = transaction.get_value(self.model) # If a transaction has the source equals to the destination account. # Show the same transaction, but with reversed value. if transaction.source_account_id == transaction.dest_account_id: self._add_transaction(transaction, description, -value) self._add_transaction(transaction, description, value) self.update_totals() def _add_transaction(self, transaction, description, value): item = Settable(transaction=transaction) self._update_transaction(item, transaction, description, value) self.search.result_view.append(item) return item def _update_transaction(self, item, transaction, description, value): item.account = description item.date = transaction.date item.description = transaction.description item.value = value item.code = transaction.code def update_totals(self): total = decimal.Decimal('0') for item in self.search.result_view: total += item.value item.total = total def _edit_transaction_dialog(self, item): store = api.new_store() if isinstance(item.transaction, AccountTransactionView): account_transaction = store.fetch(item.transaction.transaction) else: account_transaction = store.fetch(item.transaction) model = getattr(self.model, 'account', self.model) transaction = run_dialog(AccountTransactionEditor, self.app, store, account_transaction, model) store.confirm(transaction) if transaction: self.app.refresh_pages() self.update_totals() self.app.accounts.refresh_accounts(self.app.store) store.close() def on_dialog__opened(self, dialog): dialog.connect('account-added', self.on_dialog__account_added) def on_dialog__account_added(self, dialog): self.app.accounts.refresh_accounts(self.app.store) def add_transaction_dialog(self): store = api.new_store() model = getattr(self.model, 'account', self.model) model = store.fetch(model) transaction = run_dialog(AccountTransactionEditor, self.app, store, None, model) store.confirm(transaction) if transaction: self.app.refresh_pages() self.update_totals() self.app.accounts.refresh_accounts(self.app.store) store.close() def _on_search__item_activated(self, objectlist, item): if self.model.kind == 'account': self._edit_transaction_dialog(item)
class MedicSalesSearch(SearchDialog): title = _(u'Sold Items by medic') size = (800, 450) search_spec = MedicSoldItemsView fast_iter = True unlimited_results = True # # SearchDialog Hooks # def setup_widgets(self): self.add_csv_button(_('Sold Products'), _('sold-products')) self.sale_details_button = self.add_button(label=_('Sale Details')) self.sale_details_button.show() self.sale_details_button.set_sensitive(False) def update_widgets(self): item = self.results.get_selected() self.sale_details_button.set_sensitive(bool(item)) def create_filters(self): self.set_text_field_columns(['medic_name', 'description', 'code']) # Dont set a limit here, otherwise it might break the summary executer = self.search.get_query_executer() executer.set_limit(-1) branch_filter = self.create_branch_filter(_('In Branch:')) self.add_filter(branch_filter, SearchFilterPosition.TOP, columns=[Sale.branch_id]) self._date_filter = DateSearchFilter(_("Date:")) self._date_filter.select(data=DateSearchFilter.Type.USER_INTERVAL) self.add_filter(self._date_filter, SearchFilterPosition.BOTTOM, columns=[Sale.confirm_date]) self.search.set_summary_label('total', label=_(u'Total:'), format='<b>%s</b>') def get_columns(self): columns = [ IdentifierColumn('identifier', title=_('Sale #')), SearchColumn('open_date', title=_('Open date'), data_type=datetime.date, visible=False), SearchColumn('confirm_date', title=_('Confirm date'), data_type=datetime.date, visible=False), SearchColumn('code', title=_('Code'), data_type=str, sorted=True), SearchColumn('category', title=_('Category'), data_type=str, visible=False), SearchColumn('branch_name', title=_('Branch'), data_type=str, visible=False), SearchColumn('description', title=_('Description'), data_type=str, expand=True), SearchColumn('manufacturer', title=_('Manufacturer'), data_type=str, visible=False), SearchColumn('medic_name', title=_('Medic'), data_type=str), SearchColumn('crm_number', title=_('CRM'), data_type=str), SearchColumn('partner', title=_('Partner'), data_type=bool, visible=False, width=40), SearchColumn('batch_number', title=_('Batch'), data_type=str, visible=False), SearchColumn('batch_date', title=_('Batch Date'), data_type=datetime.date, visible=False), SearchColumn('quantity', title=_('Qty'), data_type=decimal.Decimal, format_func=format_quantity), SearchColumn('total', title=_('Total'), data_type=currency), ] return columns def on_sale_details_button__clicked(self, widget): item = self.results.get_selected() sale_view = self.store.find(SaleView, id=item.sale_id).one() run_dialog(SaleDetailsDialog, self, self.store, sale_view)
class CommissionSearch(SearchDialog): title = _("Search for Commissions") size = (800, 450) search_spec = CommissionView report_class = SalesPersonReport unlimited_results = True # # Private # def _update_summary(self, results): payments = sales = 0 sale_ids = set() for obj in results: payments += obj.payment_amount # Each sale may appear more than once in the results (once for each payment) if obj.id not in sale_ids: # If the sale was returned, Dont include it in the summary if not obj.sale_returned: sales += obj.total_amount sale_ids.add(obj.id) self.payments_label.set_label( _(u'Total payments: %s') % get_formatted_price(payments)) self.sales_label.set_label( _(u'Total sales: %s') % get_formatted_price(sales)) def _get_method_values(self): methods = PaymentMethod.get_active_methods(self.store) values = [(i.get_description(), i.method_name) for i in methods if i.method_name != 'multiple'] return values # # SearchDialog Hooks # def setup_widgets(self): hbox = Gtk.HBox() hbox.set_spacing(6) self.vbox.pack_start(hbox, False, True, 0) self.vbox.reorder_child(hbox, 2) self.vbox.set_spacing(6) label = Gtk.Label() hbox.pack_start(label, True, True, 0) # Create two labels to show a summary for the search (kiwi's # SummaryLabel supports only one column) self.payments_label = Gtk.Label() hbox.pack_start(self.payments_label, False, False, 0) self.sales_label = Gtk.Label() hbox.pack_start(self.sales_label, False, False, 0) hbox.show_all() set_bold(self.payments_label) set_bold(self.sales_label) self.add_csv_button(_('Commissions'), _('commissions')) def create_filters(self): self.set_text_field_columns(['salesperson_name', 'identifier_str']) self._salesperson_filter = self.create_salesperson_filter( _("Sold by:")) self.add_filter(self._salesperson_filter, SearchFilterPosition.TOP, callback=self._get_salesperson_query) # Adding a filter by date with custom interval self._date_filter = DateSearchFilter(_("Date:")) self._date_filter.select(data=DateSearchFilter.Type.USER_INTERVAL) if sysparam.get_bool('SALE_PAY_COMMISSION_WHEN_CONFIRMED'): self.add_filter(self._date_filter, SearchFilterPosition.BOTTOM, columns=[self.search_spec.confirm_date]) else: self.add_filter(self._date_filter, SearchFilterPosition.BOTTOM, columns=[self.search_spec.paid_date]) def get_columns(self): columns = [ IdentifierColumn('identifier', title=_('Sale #'), sorted=True), SearchColumn('salesperson_name', title=_('Salesperson'), data_type=str, expand=True), SearchColumn('method_description', title=_('Method'), data_type=str, search_attribute='method_name', valid_values=self._get_method_values()), # This column evals to an integer, and due to a bug # in kiwi, its not searchable Column('commission_percentage', title=_('Commission (%)'), data_type=Decimal, format="%.2f"), # negative commissions are shown in red color ColoredColumn('commission_value', title=_('Commission'), color='red', data_func=lambda x: x < 0, data_type=currency) ] # FIXME: The date here depends on the parameter. We could use # it as a property on the view, but then it would not be searchable. # Find a better way of handling this sometime in the future. if sysparam.get_bool('SALE_PAY_COMMISSION_WHEN_CONFIRMED'): columns.append( SearchColumn('confirm_date', title=_('Date'), data_type=datetime.date)) else: columns.append( SearchColumn('paid_date', title=_('Date'), data_type=datetime.date)) columns.extend([ Column('payment_amount', title=_('Payment value'), data_type=currency), Column('total_amount', title=_('Sale total'), data_type=currency) ]) return columns def print_report(self): salesperson_id = self._salesperson_filter.combo.get_selected() salesperson = (salesperson_id and self.store.get(SalesPerson, salesperson_id)) print_report(self.report_class, list(self.results), salesperson=salesperson, filters=self.search.get_search_filters()) # # Callbacks # def on_search__search_completed(self, search, result_view, states): self._update_summary(result_view) def _get_salesperson_query(self, state): queries = [] if api.sysparam.get_bool('SYNCHRONIZED_MODE'): current = api.get_current_branch(self.store) queries.append(Branch.id == current.id) salesperson = state.value if salesperson: queries.append(CommissionView.salesperson_id == salesperson) if queries: return And(*queries)