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