# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
##
## Copyright (C) 2006-2007 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
## USA.
##
## Author(s): Stoq Team <stoq-devel@async.com.br>
##
"""User interfaces for configuring, editing and printing invoices."""
import operator
import gtk
from kiwi.datatypes import ValidationError
from kiwi.python import Settable
from kiwi.ui.objectlist import ObjectList, Column
from stoqlib.database.runtime import get_current_station
from stoqlib.domain.invoice import InvoiceLayout, InvoiceField, InvoicePrinter
from stoqlib.domain.sale import Sale
from stoqlib.domain.station import BranchStation
from stoqlib.gui.editors.baseeditor import BaseEditor
from stoqlib.gui.widgets.fieldgrid import FieldGrid
from stoqlib.lib.invoice import get_invoice_fields, SaleInvoice
from stoqlib.lib.message import info
from stoqlib.lib.translation import stoqlib_gettext as _
[docs]class InvoiceGrid(FieldGrid):
[docs] def objectlist_dnd_handler(self, item, x, y):
child = self.add_field(item.name, item.description, x, y)
child.show()
self.select_field(child)
return True
[docs]class InvoiceLayoutEditor(BaseEditor):
model_name = _(u'Invoice Layouts')
model_type = InvoiceLayout
gladefile = 'InvoiceLayoutEditor'
size = (780, 540)
proxy_widgets = ['description', 'width', 'height', 'continuous_page']
#
# BaseEditor
#
def __init__(self, store, model=None):
BaseEditor.__init__(self, store, model)
self.enable_normal_window()
self.text.set_sensitive(False)
self.preview_button = self.add_button(stock=gtk.STOCK_PRINT_PREVIEW)
self.preview_button.connect('clicked', self._on_preview_button__clicked)
[docs] def create_model(self, store):
return InvoiceLayout(description=u'Untitled',
width=80,
height=40,
store=store)
[docs] def setup_proxies(self):
self._create_grid()
self._create_field_list()
self.proxy = self.add_proxy(self.model,
InvoiceLayoutEditor.proxy_widgets)
for field in self.model.fields:
description = field.content or self._field_descriptions[field.field_name]
grid_field = self.grid.add_field(field.field_name, description,
field.x, field.y,
field.width, field.height, field)
grid_field.model = field
grid_field.widget.show()
if self.model.description == 'Untitled':
self.description.grab_focus()
else:
self.grid.grab_focus()
#
# Callbacks
#
[docs] def on_width__validate(self, widget, value):
if not value > 0:
return ValidationError(_(u'width value must greater than zero.'))
[docs] def on_height__validate(self, widget, value):
if not value > 0:
return ValidationError(_(u'height value must greater than zero.'))
[docs] def after_width__content_changed(self, widget):
self.grid.resize(self.model.width, self.model.height)
[docs] def after_height__content_changed(self, widget):
self.grid.resize(self.model.width, self.model.height)
def _on_grid__field_added(self, grid, grid_field):
self._field_added(grid_field)
def _on_grid__field_removed(self, grid, grid_field):
self._field_removed(grid_field)
def _on_grid__selection_changed(self, grid, grid_field):
self._field_changed(grid_field)
def _on_preview_button__clicked(self, button):
self._print_preview()
[docs] def on_text__changed(self, widget):
text = unicode(widget.get_text())
self._selected_field.model.content = text
self._selected_field.update_label(text)
#
# Private
#
def _create_grid(self):
self.grid = InvoiceGrid('Monospace 8',
self.model.width,
self.model.height)
self.grid.connect('field-added', self._on_grid__field_added)
self.grid.connect('field-removed', self._on_grid__field_removed)
self.grid.connect('selection-changed',
self._on_grid__selection_changed)
self.sw.add_with_viewport(self.grid)
self.grid.show()
def _create_field_list(self):
items = ObjectList([Column('category', sorted=True),
Column('description', width=200),
Column('len', data_type=int, visible=False)])
items.enable_dnd()
items.set_size_request(200, -1)
descriptions = {}
invoice_fields = get_invoice_fields()
for invoice_field in sorted(invoice_fields,
key=operator.attrgetter('name')):
items.append(
Settable(description=invoice_field.get_description(),
name=invoice_field.name,
len=invoice_field.length,
category=invoice_field.category))
descriptions[invoice_field.name] = invoice_field.description
self._field_descriptions = descriptions
self.left_vbox.pack_end(items, True, True)
items.show()
def _field_changed(self, grid_field):
self._selected_field = grid_field
if grid_field:
pos = 'x=%d, y=%d' % (grid_field.x + 1, grid_field.y + 1)
size = '%dx%d' % (grid_field.width, grid_field.height)
name = grid_field.name
self.text.update(grid_field.model.content)
self.text.set_sensitive(name == u'FREE_TEXT')
# This is needed because we might get a selection-changed signal
# before child-added, model is only assigned to the field info
# when adding it.
field = getattr(grid_field, 'model', None)
if field is not None:
field.x = grid_field.x
field.y = grid_field.y
field.width = grid_field.width
field.height = grid_field.height
else:
pos = ''
size = ''
name = ''
self.text.set_sensitive(False)
self.text.update(u'')
self.field_name.set_text(name)
self.field_pos.set_text(pos)
self.field_size.set_text(size)
def _field_added(self, grid_field):
if grid_field.model:
return
field = InvoiceField(layout=self.model,
field_name=grid_field.name,
x=grid_field.x,
y=grid_field.y,
width=grid_field.width,
height=grid_field.height,
store=self.store)
grid_field.model = field
def _field_removed(self, grid_field):
invoice_field = grid_field.model
self.store.remove(invoice_field)
def _print_preview(self):
# Get the last opened date
sale = self.store.find(Sale).order_by(Sale.identifier).last()
if not sale:
info(_("You need at least one sale to be able to preview "
"invoice layouts"))
return
invoice = SaleInvoice(sale, self.model)
invoice_pages = invoice.generate_pages()
if not invoice_pages:
info(_(u'Not enough fields or data to create an invoice preview.'))
return
for page in invoice_pages:
for line in page:
print(repr(line.tostring()))
[docs]class InvoicePrinterEditor(BaseEditor):
model_name = _(u'Invoice Printers')
model_type = InvoicePrinter
gladefile = 'InvoicePrinterEditor'
proxy_widgets = ['device_name',
'description',
'layout',
'station']
[docs] def create_model(self, store):
return InvoicePrinter(description=_(u'Untitled Printer'),
device_name=u'/dev/lp0',
station=get_current_station(store),
layout=None,
store=store)
[docs] def setup_proxies(self):
stations = BranchStation.get_active_stations(self.store)
self.station.prefill([(station.name, station)
for station in stations])
layouts = self.store.find(InvoiceLayout)
self.layout.prefill([(layout.get_description(), layout)
for layout in layouts.order_by(InvoiceLayout.description)])
self.proxy = self.add_proxy(self.model,
InvoicePrinterEditor.proxy_widgets)
[docs] def on_confirm(self):
# Bug!
self.model.layout = self.layout.get_selected()
[docs]def test(): # pragma nocover
from stoqlib.api import api
from stoqlib.gui.base.dialogs import run_dialog
creator = api.prepare_test()
model = creator.store.find(InvoiceLayout, description=u'Untitled').any()
retval = run_dialog(InvoiceLayoutEditor, None, creator.store, model)
creator.store.confirm(retval)
if __name__ == '__main__': # pragma nocover
test()