def testGetConverters(self): converters = converter.get_converters((decimal.Decimal, )) # Curreny is a subclass of Decimal, so it should be in converters conv = converter.get_converter(currency) self.assertTrue(conv in converters) converters = converter.get_converters((object, )) # Object is treated specially. Make sure its in the list conv = converter.get_converter(object) self.assertTrue(conv in converters)
def testGetConverters(self): converters = converter.get_converters((Decimal,)) # Curreny is a subclass of Decimal, so it should be in converters conv = converter.get_converter(currency) self.assertTrue(conv in converters) converters = converter.get_converters((object,)) # Object is treated specially. Make sure its in the list conv = converter.get_converter(object) self.assertTrue(conv in converters)
def testSimple(self): class status(enum): (OPEN, CLOSE) = range(2) conv = converter.get_converter(status) self.assertEqual(conv.type, status) conv2 = converter.get_converter(status) self.assertEqual(conv, conv2) self.assertEqual(conv.from_string('OPEN'), status.OPEN) self.assertEqual(conv.as_string(status.CLOSE), 'CLOSE') self.assertRaises(ValidationError, conv.from_string, 'FOO') self.assertRaises(ValidationError, conv.as_string, object())
def testSimple(self): class status(enum): (OPEN, CLOSE) = range(2) conv = converter.get_converter(status) self.assertEqual(conv.type, status) conv2 = converter.get_converter(status) self.assertEqual(conv, conv2) self.assertEqual(conv.from_string("OPEN"), status.OPEN) self.assertEqual(conv.as_string(status.CLOSE), "CLOSE") self.assertRaises(ValidationError, conv.from_string, "FOO") self.assertRaises(ValidationError, conv.as_string, object())
def _set_data_type(self, data_type): if not ProxyWidgetMixin.set_data_type(self, data_type): return conv = converter.get_converter(data_type) if conv.align == Alignment.RIGHT: self.set_property('xalign', 1.0)
def set_mask_for_data_type(self, data_type): """ Set a mask for the parameter data_type. :param data_type: """ conv = converter.get_converter(data_type) mask = conv.get_mask() self.set_mask(mask)
def set_mask_for_data_type(self, data_type): """ Set a mask for the parameter data_type. @param data_type: """ conv = converter.get_converter(data_type) mask = conv.get_mask() self.set_mask(mask)
def format_func(self, item, data=None): value = self.get_new_value(item) conv = converter.get_converter(self.data_type) if self._format_func is not None: return self._format_func(value) if value is not None: return conv.as_string(value) return ''
def _as_string(self, data): """Convert a value to a string :param data: data to convert """ conv = self._converter if conv is None: conv = converter.get_converter(str) return conv.as_string(data, format=self._data_format, **self._converter_options.get(conv.type, {}))
def prop_set_data_type(self, data_type): data_type = super(ProxyLabel, self).prop_set_data_type(data_type) if not data_type: return conv = converter.get_converter(data_type) if conv.align == Alignment.RIGHT: self.set_property('xalign', 1.0) return data_type
def _from_string(self, data): """Convert a string to the data type of the widget This may raise a :class:`kiwi.datatypes.ValidationError` if conversion failed :param data: data to convert """ conv = self._converter if conv is None: conv = converter.get_converter(str) return conv.from_string(data)
def __init__(self, entry, mode): """ :param entry: a :class:`kiwi.ui.widgets.spinbutton.ProxySpinButton` or a :class:`kiwi.ui.widgets.entry.ProxyEntry` subclass :param mode: one of :attr:`.MODE_ADD`, :attr:`.MODE_SUB` """ self._mode = mode self._new_value = None self._data_type = self._data_type_mapper[entry.data_type] self._converter = converter.get_converter(self._data_type) super(CalculatorPopup, self).__init__(entry)
def _set_data_type(self, data_type): if not ProxyWidgetMixin.set_data_type(self, data_type): return # Numbers and dates should be right aligned conv = converter.get_converter(data_type) if conv.align == Alignment.RIGHT: self.set_property('xalign', 1.0) # Apply a mask for the data types, some types like # dates has a default mask try: self.set_mask_for_data_type(data_type) except MaskError: pass
def testCompareFunc(self): def sort_func(): return True column = Column('foo', data_type=str, sort_func=sort_func) self.assertEqual(column.compare, sort_func) column = Column('foo', data_type=str) self.assertEqual(column.compare, column._str_compare) data_type = int conv = converter.get_converter(data_type) column = Column('foo', data_type=data_type) self.assertEqual(column.compare, conv.get_compare_function())
def testCompareFunc(self): def sort_func(): return True column = Column('foo', data_type=str, sort_func=sort_func) self.assertEquals(column.compare, sort_func) column = Column('foo', data_type=str) self.assertEquals(column.compare, column._str_compare) data_type = int conv = converter.get_converter(data_type) column = Column('foo', data_type=data_type) self.assertEquals(column.compare, conv.get_compare_function())
def __init__(self, entry, mode): """ :param entry: a :class:`kiwi.ui.widgets.spinbutton.ProxySpinButton` or a :class:`kiwi.ui.widgets.entry.ProxyEntry` subclass :param mode: one of :attr:`.MODE_ADD`, :attr:`.MODE_SUB` """ self._mode = mode self._new_value = None self._popped_entry = entry self._data_type = self._data_type_mapper[entry.data_type] self._converter = converter.get_converter(self._data_type) gtk.Window.__init__(self, gtk.WINDOW_POPUP) self._create_ui()
def testButtonPixbuf(self): if sys.platform == 'win32': return conv = converter.get_converter(gdk.Pixbuf) filename = environ.find_resource('pixmap', 'validation-error-16.png') pixbuf = gdk.pixbuf_new_from_file(filename) self.assertEqual(self.view.buttonpixbuf.data_type, 'Pixbuf') self.view.buttonpixbuf.update(pixbuf) self.assertEqual(type(self.view.buttonpixbuf.read()), gdk.Pixbuf) self.assertEqual(conv.as_string(self.model.buttonpixbuf), conv.as_string(pixbuf)) self.view.buttonpixbuf.update(None) self.assertEqual(self.view.buttonpixbuf.read(), None) self.assertEqual(self.model.buttonpixbuf, None)
def prop_set_data_type(self, data_type): """Set the data type for the widget @param data_type: can be None, a type object or a string with the name of the type object, so None, "<type 'str'>" or 'str' """ if data_type is None: return data_type elif data_type == '': return None # This may convert from string to type, # A type object will always be returned data_type = converter.check_supported(data_type) self._converter = converter.get_converter(data_type) return self._converter.type.__name__
def set_data_type(self, data_type): """Set the data type for the widget :param data_type: can be None, a type object or a string with the name of the type object, so None, "<type 'str'>" or 'str' """ if data_type is None: return data_type elif data_type == '': return None # This may convert from string to type, # A type object will always be returned data_type = converter.check_supported(data_type) self._converter = converter.get_converter(data_type) self._data_type = self._converter.type.__name__ return self._converter.type.__name__
def prop_set_data_type(self, data_type): data_type = super(ProxyEntry, self).prop_set_data_type(data_type) # When there is no datatype, nothing needs to be done. if not data_type: return # Numbers and dates should be right aligned conv = converter.get_converter(data_type) if conv.align == Alignment.RIGHT: self.set_property('xalign', 1.0) # Apply a mask for the data types, some types like # dates has a default mask try: self.set_mask_for_data_type(data_type) except MaskError: pass return data_type
def _get_widget_data_type(widget): data_type = widget.get_property('data-type') c = converter.get_converter(data_type) if c.type is str or c.type is unicode: return basestring return c.type
def setUp(self): set_locale(locale.LC_NUMERIC, "C") self.conv = converter.get_converter(Decimal)
def setUp(self): self.conv = converter.get_converter(gdk.Pixbuf)
if sep_by_space: space = ' ' else: space = '' if cs_precedes: currency = currency_symbol + space + currency else: currency = currency + space + currency_symbol return currency def __repr__(self): return '<currency %s>' % self.format() _DecimalConverter = type(converter.get_converter(decimal.Decimal)) class _CurrencyConverter(_DecimalConverter): type = currency name = _('Currency') align = Alignment.RIGHT def __init__(self): self.symbol = True self.precision = 2 def as_string(self, value, format=None, symbol=None, precision=None): if value == ValueUnset: return ''
def setUp(self): self.conv = converter.get_converter(float)
def setUp(self): set_locale(locale.LC_TIME, "C") self.date = datetime.date(1979, 2, 12) self.conv = converter.get_converter(datetime.date)
## ## 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 <*****@*****.**> ## import gtk from kiwi.ui.delegates import GladeDelegate from kiwi.datatypes import converter from stoqlib.gui.base.dialogs import RunnableView from stoqlib.lib.translation import stoqlib_gettext as _ _pixbuf_converter = converter.get_converter(gtk.gdk.Pixbuf) class SellableImageViewer(GladeDelegate, RunnableView): title = _("Sellable Image Viewer") domain = 'stoq' gladefile = "SellableImageViewer" position = (0, 0) def __init__(self, size): """ :param tuple size: the size for this viewer as (x, y) """ self._size = size GladeDelegate.__init__(self)
def setUp(self): self.conv = converter.get_converter(bool)
def setUp(self): set_locale(locale.LC_TIME, 'C') self.dt = datetime.datetime(1979, 2, 12, 12, 15, 30) self.conv = converter.get_converter(datetime.datetime)
def setUp(self): set_locale(locale.LC_NUMERIC, 'C') self.conv = converter.get_converter(decimal.Decimal)
# Based on date cell renderer in Planner written by Richard Hult # and Mikael Hallendal # import gettext import datetime from gi.repository import Gtk, Gdk from kiwi.datatypes import converter, ValueUnset, ValidationError from kiwi.ui.popup import PopupWindow from kiwi.utils import gsignal, type_register _ = lambda m: gettext.dgettext('kiwi', m) date_converter = converter.get_converter(datetime.date) class _DateEntryPopup(PopupWindow): # We will already have 6 at the bottom because of them buttons padding FRAME_PADDING = (6, 0, 6, 6) gsignal('date-selected', object) def __init__(self, dateentry): self._dateentry = dateentry super(_DateEntryPopup, self).__init__(dateentry) def get_main_widget(self): vbox = Gtk.VBox()
class StockApp(ShellApp): app_title = _('Stock') gladefile = "stock" search_spec = ProductFullStockView search_labels = _('Matching:') report_table = SimpleProductReport pixbuf_converter = converter.get_converter(GdkPixbuf.Pixbuf) # # Application # def create_actions(self): group = get_accels('app.stock') actions = [ ("NewReceiving", STOQ_RECEIVING, _("Order _receival..."), group.get('new_receiving')), ('NewTransfer', Gtk.STOCK_CONVERT, _('Transfer...'), group.get('transfer_product')), ('NewStockDecrease', None, _('Stock decrease...'), group.get('stock_decrease')), ('StockInitial', Gtk.STOCK_GO_UP, _('Register initial stock...')), ("LoanNew", None, _("Loan...")), ("LoanClose", None, _("Close loan...")), ("SearchPurchaseReceiving", None, _("Received purchases..."), group.get('search_receiving'), _("Search for received purchase orders")), ("SearchProductHistory", None, _("Product history..."), group.get('search_product_history'), _("Search for product history")), ("SearchStockDecrease", None, _("Stock decreases..."), '', _("Search for manual stock decreases")), ("SearchPurchasedStockItems", None, _("Purchased items..."), group.get('search_purchased_stock_items'), _("Search for purchased items")), ("SearchBrandItems", None, _("Brand items..."), group.get('search_brand_items'), _("Search for brand items on stock")), ("SearchBrandItemsByBranch", None, _("Brand item by branch..."), group.get('search_brand_by_branch'), _("Search for brand items by branch on stock")), ("SearchBatchItems", None, _("Batch items..."), group.get('search_batch_items'), _("Search for batch items on stock")), ("SearchStockItems", None, _("Stock items..."), group.get('search_stock_items'), _("Search for items on stock")), ("SearchTransfer", None, _("Transfers..."), group.get('search_transfers'), _("Search for stock transfers")), ("SearchClosedStockItems", None, _("Closed stock Items..."), group.get('search_closed_stock_items'), _("Search for closed stock items")), ("LoanSearch", None, _("Loans...")), ("LoanSearchItems", None, _("Loan items...")), ("SearchTransferItems", None, _("Transfer items...")), ("SearchReturnedItems", None, _("Returned items...")), ("SearchPendingReturnedSales", None, _("Pending returned sales...")), ("ProductMenu", None, _("Product")), ("PrintLabels", None, _("Print labels...")), ("ManageStock", None, _("Manage stock...")), ("ProductStockHistory", Gtk.STOCK_INFO, _("History..."), group.get('history'), _('Show the stock history of the selected product')), ("EditProduct", Gtk.STOCK_EDIT, _("Edit..."), group.get('edit_product'), _("Edit the selected product, allowing you to change it's " "details")), ] self.stock_ui = self.add_ui_actions('', actions, filename='stock.xml') toggle_actions = [ ('StockPictureViewer', None, _('Picture viewer'), group.get('toggle_picture_viewer')), ] self.add_ui_actions('', toggle_actions, 'ToggleActions', 'toggle') self.set_help_section(_("Stock help"), 'app-stock') self.NewReceiving.set_short_label(_("Receive")) self.NewTransfer.set_short_label(_("Transfer")) self.EditProduct.set_short_label(_("Edit")) self.ProductStockHistory.set_short_label(_("History")) self.EditProduct.props.is_important = True self.ProductStockHistory.props.is_important = True def create_ui(self): if api.sysparam.get_bool('SMART_LIST_LOADING'): self.search.enable_lazy_search() self.popup = self.uimanager.get_widget('/StockSelection') self.window.add_new_items([self.NewReceiving, self.NewTransfer, self.NewStockDecrease, self.LoanNew]) self.window.add_search_items([ self.SearchStockItems, self.SearchBrandItems, self.SearchStockDecrease, self.SearchClosedStockItems, self.SearchProductHistory, self.SearchPurchasedStockItems, self.SearchTransfer, ]) self.window.Print.set_tooltip( _("Print a report of these products")) self._inventory_widgets = [self.NewTransfer, self.NewReceiving, self.StockInitial, self.NewStockDecrease, self.LoanNew, self.LoanClose] self.register_sensitive_group(self._inventory_widgets, lambda: not self.has_open_inventory()) self.image_viewer = None self.image = Gtk.Image() self.edit_button = self.uimanager.get_widget('/toolbar/AppToolbarPH/EditProduct') self.edit_button.set_icon_widget(self.image) self.image.show() self.search.set_summary_label(column='stock', label=_('<b>Stock Total:</b>'), format='<b>%s</b>', parent=self.get_statusbar_message_area()) def activate(self, refresh=True): self.window.NewToolItem.set_tooltip( _("Create a new receiving order")) self.window.SearchToolItem.set_tooltip( _("Search for stock items")) if refresh: self.refresh() open_inventory = self.check_open_inventory() if not open_inventory: self.transfers_bar = self._create_pending_info_message() self.returned_bar = self._create_pending_returned_sale_message() else: self.transfers_bar = None self.returned_bar = None self._update_widgets() self.search.focus_search_entry() def deactivate(self): if self.transfers_bar: self.transfers_bar.hide() if self.returned_bar: self.returned_bar.hide() self.uimanager.remove_ui(self.stock_ui) self._close_image_viewer() def new_activate(self): if not self.NewReceiving.get_sensitive(): warning(_("You cannot receive a purchase with an open inventory.")) return self._receive_purchase() def search_activate(self): self.run_dialog(ProductStockSearch, self.store) def set_open_inventory(self): self.set_sensitive(self._inventory_widgets, False) def create_filters(self): self.search.set_query(self._query) self.set_text_field_columns(['description', 'code', 'barcode', 'category_description', 'manufacturer']) branches = Branch.get_active_branches(self.store) self.branch_filter = ComboSearchFilter( _('Show by:'), api.for_combo(branches, empty=_("All branches"))) self.branch_filter.select(api.get_current_branch(self.store)) self.add_filter(self.branch_filter, position=SearchFilterPosition.TOP) def get_columns(self): return [SearchColumn('code', title=_('Code'), sorted=True, sort_func=sort_sellable_code, data_type=str, width=130), SearchColumn('barcode', title=_("Barcode"), data_type=str, width=130), SearchColumn('category_description', title=_("Category"), data_type=str, width=100, visible=False), SearchColumn('description', title=_("Description"), data_type=str, expand=True, ellipsize=Pango.EllipsizeMode.END), SearchColumn('manufacturer', title=_("Manufacturer"), data_type=str, visible=False), SearchColumn('brand', title=_("Brand"), data_type=str, visible=False), SearchColumn('model', title=_("Model"), data_type=str, visible=False), SearchColumn('location', title=_("Location"), data_type=str, width=100, visible=False), QuantityColumn('stock', title=_('Quantity'), width=100, use_having=True), SearchColumn('has_image', title=_('Picture'), data_type=bool, width=80), ] # # Private API # def _open_image_viewer(self): assert self.image_viewer is None self.image_viewer = SellableImageViewer(size=(325, 325)) self.image_viewer.toplevel.connect( 'delete-event', self.on_image_viewer_closed) self.image_viewer.show_all() self._update_widgets() def _close_image_viewer(self): if self.image_viewer is None: return self.image_viewer.destroy() self.image_viewer = None def _query(self, store): branch = self.branch_filter.get_state().value return self.search_spec.find_by_branch(store, branch) def _update_widgets(self): branch = api.get_current_branch(self.store) is_main_branch = self.branch_filter.get_state().value is branch item = self.results.get_selected() sellable = item and item.product.sellable if sellable: if item.has_image: # XXX:Workaround for a bug caused by the image domain refactoring # which left some existent thumbnail as None thumbnail = sellable.image.thumbnail if thumbnail is None: # Create new store to create the thumbnail with api.new_store() as new_store: image = sellable.image image = new_store.fetch(image) size = (Image.THUMBNAIL_SIZE_WIDTH, Image.THUMBNAIL_SIZE_HEIGHT) image.thumbnail = get_thumbnail(image.image, size) thumbnail = image.thumbnail pixbuf = self.pixbuf_converter.from_string(thumbnail) else: pixbuf = None self._update_edit_image(pixbuf) if self.image_viewer: self.image_viewer.set_sellable(sellable) else: self._update_edit_image() # Always let the user choose the manage stock option and do a proper # check there (showing a warning if he can't) self.set_sensitive([self.ManageStock], bool(item)) self.set_sensitive([self.EditProduct, self.PrintLabels], bool(item)) self.set_sensitive([self.ProductStockHistory], bool(item) and is_main_branch) # We need more than one branch to be able to do transfers # Note that 'all branches' is not a real branch has_branches = len(self.branch_filter.combo) > 2 transfer_active = self.NewTransfer.get_sensitive() self.set_sensitive([self.NewTransfer], transfer_active and has_branches) # Building a list of searches that we must disable if there is no # branches other than the main company searches = [self.SearchTransfer, self.SearchTransferItems, self.SearchPendingReturnedSales] self.set_sensitive(searches, has_branches) def _update_edit_image(self, pixbuf=None): if not pixbuf: self.image.set_from_stock(Gtk.STOCK_EDIT, Gtk.IconSize.LARGE_TOOLBAR) return # FIXME: get this icon size from settings icon_size = 24 pixbuf = pixbuf.scale_simple(icon_size, icon_size, GdkPixbuf.InterpType.BILINEAR) self.image.set_from_pixbuf(pixbuf) def _update_filter_slave(self, slave): self.refresh() def _transfer_stock(self): if self.check_open_inventory(): return store = api.new_store() model = self.run_dialog(StockTransferWizard, store) store.confirm(model) store.close() self.refresh() def _receive_purchase(self): if self.check_open_inventory(): return store = api.new_store() model = self.run_dialog(ReceivingOrderWizard, store) store.confirm(model) store.close() self.refresh() def _create_pending_info_message(self): branch = api.get_current_branch(self.store) n_transfers = TransferOrder.get_pending_transfers(self.store, branch).count() if not n_transfers: return None msg = stoqlib_ngettext(_(u"You have %s incoming transfer"), _(u"You have %s incoming transfers"), n_transfers) % n_transfers info_bar = self.window.add_info_bar(Gtk.MessageType.QUESTION, msg) button = info_bar.add_button(_(u"Receive"), Gtk.ResponseType.OK) button.connect('clicked', self._on_info_transfers__clicked) return info_bar def _create_pending_returned_sale_message(self): branch = api.get_current_branch(self.store) n_returned = ReturnedSale.get_pending_returned_sales(self.store, branch).count() if not n_returned: return None msg = stoqlib_ngettext(_(u"You have %s returned sale to receive"), _(u"You have %s returned sales to receive"), n_returned) % n_returned info_returned_bar = self.window.add_info_bar(Gtk.MessageType.QUESTION, msg) button = info_returned_bar.add_button(_(u"Returned sale"), Gtk.ResponseType.OK) button.connect('clicked', self._on_info_returned_sales__clicked) return info_returned_bar def _search_transfers(self): branch = api.get_current_branch(self.store) self.run_dialog(TransferOrderSearch, self.store) # After the search is closed we may want to update , or even hide the # message, if there is no pending transfer to receive if self.transfers_bar: n_transfers = TransferOrder.get_pending_transfers(self.store, branch).count() if n_transfers > 0: msg = stoqlib_ngettext(_(u"You have %s incoming transfer"), _(u"You have %s incoming transfers"), n_transfers) % n_transfers self.transfers_bar.set_message(msg) else: self.transfers_bar.hide() self.refresh() def _search_pending_returned_sales(self): with api.new_store() as store: self.run_dialog(PendingReturnedSaleSearch, store) branch = api.get_current_branch(self.store) # After the search is closed we may want to update , or even hide the # message, if there is no pending returned sale to receive if self.returned_bar: n_returned = ReturnedSale.get_pending_returned_sales(self.store, branch).count() if n_returned > 0: msg = stoqlib_ngettext(_(u"You have %s returned sale to receive"), _(u"You have %s returned sales to receive"), n_returned) % n_returned self.returned_bar.set_message(msg) else: self.returned_bar.hide() self.refresh() # # Callbacks # def on_image_viewer_closed(self, window, event): self.image_viewer = None self.StockPictureViewer.set_active(False) def on_results__has_rows(self, results, product): self._update_widgets() def on_results__selection_changed(self, results, product): self._update_widgets() def on_results__right_click(self, results, result, event): self.popup.popup(None, None, None, None, event.button.button, event.time) def on_ProductStockHistory__activate(self, button): selected = self.results.get_selected() sellable = selected.sellable self.run_dialog(ProductStockHistoryDialog, self.store, sellable, branch=self.branch_filter.combo.get_selected()) def on_ManageStock__activate(self, action): user = api.get_current_user(self.store) if not user.profile.check_app_permission(u'inventory'): return warning(_('Only users with access to the inventory app can' ' change the stock quantity')) product = self.results.get_selected().product if product.is_grid: return warning(_("Can't change stock quantity of a grid parent")) if product.storable and product.storable.is_batch: return warning(_("It's not possible to change the stock quantity of" " a batch product")) branch = self.branch_filter.combo.get_selected() if not branch: return warning(_('You must select a branch first')) with api.new_store() as store: self.run_dialog(ProductStockQuantityEditor, store, store.fetch(product), branch=branch) if store.committed: self.refresh() def on_PrintLabels__activate(self, button): selected = self.results.get_selected() sellable = selected.sellable label_data = self.run_dialog(PrintLabelEditor, None, self.store, sellable) if label_data: print_labels(label_data, self.store) def on_EditProduct__activate(self, button): selected = self.results.get_selected() assert selected store = api.new_store() product = store.fetch(selected.product) model = self.run_dialog(ProductStockEditor, store, product) store.confirm(model) store.close() if model: self.refresh() def _on_info_transfers__clicked(self, button): self._search_transfers() def _on_info_returned_sales__clicked(self, button): self._search_pending_returned_sales() # Stock def on_NewReceiving__activate(self, button): self._receive_purchase() def on_NewTransfer__activate(self, button): self._transfer_stock() def on_NewStockDecrease__activate(self, action): if self.check_open_inventory(): return store = api.new_store() model = self.run_dialog(StockDecreaseWizard, store) store.confirm(model) store.close() self.refresh() def on_StockInitial__activate(self, action): if self.check_open_inventory(): return with api.new_store() as store: self.run_dialog(InitialStockDialog, store) if store.committed: self.refresh() def on_StockPictureViewer__toggled(self, button): if button.get_active(): self._open_image_viewer() else: self._close_image_viewer() # Loan def on_LoanNew__activate(self, action): if self.check_open_inventory(): return store = api.new_store() model = self.run_dialog(NewLoanWizard, store) store.confirm(model) store.close() self.refresh() def on_LoanClose__activate(self, action): if self.check_open_inventory(): return store = api.new_store() model = self.run_dialog(CloseLoanWizard, store) store.confirm(model) store.close() self.refresh() def on_LoanSearch__activate(self, action): self.run_dialog(LoanSearch, self.store) def on_LoanSearchItems__activate(self, action): self.run_dialog(LoanItemSearch, self.store) # Search def on_SearchPurchaseReceiving__activate(self, button): self.run_dialog(PurchaseReceivingSearch, self.store) def on_SearchTransfer__activate(self, action): self._search_transfers() def on_SearchTransferItems__activate(self, action): self.run_dialog(TransferItemSearch, self.store) def on_SearchPendingReturnedSales__activate(self, action): self._search_pending_returned_sales() def on_SearchReturnedItems__activate(self, action): self.run_dialog(ReturnedItemSearch, self.store) def on_SearchPurchasedStockItems__activate(self, action): self.run_dialog(PurchasedItemsSearch, self.store) def on_SearchStockItems__activate(self, action): self.run_dialog(ProductStockSearch, self.store) def on_SearchBrandItems__activate(self, action): self.run_dialog(ProductBrandSearch, self.store) def on_SearchBrandItemsByBranch__activate(self, action): self.run_dialog(ProductBrandByBranchSearch, self.store) def on_SearchBatchItems__activate(self, action): self.run_dialog(ProductBatchSearch, self.store) def on_SearchClosedStockItems__activate(self, action): self.run_dialog(ProductClosedStockSearch, self.store) def on_SearchProductHistory__activate(self, action): self.run_dialog(ProductSearchQuantity, self.store) def on_SearchStockDecrease__activate(self, action): self.run_dialog(StockDecreaseSearch, self.store)
## ## 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 <*****@*****.**> ## import gtk from kiwi.ui.delegates import GladeDelegate from kiwi.datatypes import converter from stoqlib.gui.base.dialogs import RunnableView from stoqlib.lib.translation import stoqlib_gettext as _ _pixbuf_converter = converter.get_converter(gtk.gdk.Pixbuf) class SellableImageViewer(GladeDelegate, RunnableView): title = _("Sellable Image Viewer") gladefile = "SellableImageViewer" position = (0, 0) def __init__(self, size): """ :param tuple size: the size for this viewer as (x, y) """ self._size = size GladeDelegate.__init__(self)
## along with this program; if not, write to the Free Software ## Foundation, Inc., or visit: http://www.gnu.org/. ## ## Author(s): Stoq Team <*****@*****.**> ## from gi.repository import Gtk, GdkPixbuf from kiwi.ui.delegates import GladeDelegate from kiwi.datatypes import converter from stoqlib.domain.image import Image from stoqlib.gui.base.dialogs import RunnableView from stoqlib.lib.imageutils import get_pixbuf, get_thumbnail from stoqlib.lib.translation import stoqlib_gettext as _ _pixbuf_converter = converter.get_converter(GdkPixbuf.Pixbuf) class SellableImageViewer(GladeDelegate, RunnableView): title = _("Sellable Image Viewer") domain = 'stoq' gladefile = "SellableImageViewer" position = (0, 0) def __init__(self, size, use_thumbnail=False): """ :param tuple size: the size for this viewer as (x, y) """ self._use_thumbnail = use_thumbnail self._size = size
def setUp(self): set_locale(locale.LC_ALL, "C") self.conv = converter.get_converter(currency)
def _get_widget_data_type(widget): data_type = widget.get_property("data-type") c = converter.get_converter(data_type) return c.type
def setUp(self): self.conv = converter.get_converter(unicode)
def _get_widget_data_type(widget): data_type = widget.get_property('data-type') c = converter.get_converter(data_type) return c.type
class currency(decimal.Decimal): """ A datatype representing currency, used together with the list and the framework """ _converter = converter.get_converter(decimal.Decimal) def __new__(cls, value): """ Convert value to currency. :param value: value to convert :type value: string or number """ if isinstance(value, str): conv = get_localeconv() currency_symbol = conv.get('currency_symbol') text = value.strip(currency_symbol) # if we cannot convert it using locale information, still try to # create try: text = filter_locale(text, monetary=True) value = currency._converter.from_string(text, filter=False) except ValidationError: # Decimal does not accept leading and trailing spaces. See # bug 1516613 value = text.strip() if value == ValueUnset: raise decimal.InvalidOperation elif isinstance(value, float): print('Warning: losing precision converting float %r to currency' % value) value = str(value) elif not isinstance(value, (int, decimal.Decimal)): raise TypeError("cannot convert %r of type %s to a currency" % (value, type(value))) return decimal.Decimal.__new__(cls, value) def format(self, symbol=True, precision=None): conv = get_localeconv() frac_digits = precision or conv.get('frac_digits', 2) # Decimal.quantize can't handle a precision of 127, which is # the default value for glibc/python. Fallback to 2 if frac_digits == 127: frac_digits = 2 value = self.quantize(decimal.Decimal('10')**-frac_digits) # Grouping (eg thousand separator) of integer part groups = conv.get('mon_grouping', [])[:] groups.reverse() if groups: group = groups.pop() else: group = 3 intparts = [] # We're iterating over every character in the integer part # make sure to remove the negative sign, it'll be added later intpart = str(int(abs(value))) while True: if not intpart: break s = intpart[-group:] intparts.insert(0, s) intpart = intpart[:-group] if not groups: continue last = groups.pop() # if 0 reuse last one, see struct lconv in locale.h if last != 0: group = last # Add the sign, and the list of decmial parts, which now are grouped # properly and can be joined by mon_thousand_sep if value > 0: sign = conv.get('positive_sign', '') elif value < 0: sign = conv.get('negative_sign', '-') else: sign = '' currency = sign + conv.get('mon_thousands_sep', '.').join(intparts) # Only add decimal part if it has one, is this correct? if precision is not None or value % 1 != 0: sign_, digits, exponent = value.as_tuple() frac = digits[exponent:] if exponent != 0 else (0, ) dec_part = ''.join(str(i) for i in frac) # Add 0s to complete the required precision dec_part = dec_part.rjust(frac_digits, '0') mon_decimal_point = conv.get('mon_decimal_point', '.') currency += mon_decimal_point + dec_part # If requested include currency symbol currency_symbol = conv.get('currency_symbol', '') if currency_symbol and symbol: if value > 0: cs_precedes = conv.get('p_cs_precedes', 1) sep_by_space = conv.get('p_sep_by_space', 1) else: cs_precedes = conv.get('n_cs_precedes', 1) sep_by_space = conv.get('n_sep_by_space', 1) if sep_by_space: space = ' ' else: space = '' if cs_precedes: currency = currency_symbol + space + currency else: currency = currency + space + currency_symbol return currency def __repr__(self): return '<currency %s>' % self.format()
# Based on date cell renderer in Planner written by Richard Hult # and Mikael Hallendal # import gettext import datetime from gi.repository import Gtk, Gdk from kiwi.datatypes import converter, ValueUnset, ValidationError from kiwi.ui.popup import PopupWindow from kiwi.utils import gsignal, type_register _ = lambda m: gettext.dgettext('kiwi', m) date_converter = converter.get_converter(datetime.date) class _DateEntryPopup(PopupWindow): # We will already have 6 at the bottom because of them buttons padding FRAME_PADDING = (6, 0, 6, 6) gsignal('date-selected', object) def __init__(self, dateentry): self._dateentry = dateentry super(_DateEntryPopup, self).__init__(dateentry) def get_main_widget(self): vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
class StockApp(ShellApp): app_title = _('Stock') gladefile = "stock" search_spec = ProductFullStockView search_labels = _('Matching:') report_table = SimpleProductReport pixbuf_converter = converter.get_converter(gtk.gdk.Pixbuf) # # Application # def create_actions(self): group = get_accels('app.stock') actions = [ ("NewReceiving", STOQ_RECEIVING, _("Order _receival..."), group.get('new_receiving')), ('NewTransfer', gtk.STOCK_CONVERT, _('Transfer...'), group.get('transfer_product')), ('NewStockDecrease', None, _('Stock decrease...'), group.get('stock_decrease')), ('StockInitial', gtk.STOCK_GO_UP, _('Register initial stock...')), ("LoanNew", None, _("Loan...")), ("LoanClose", None, _("Close loan...")), ("SearchPurchaseReceiving", None, _("Received purchases..."), group.get('search_receiving'), _("Search for received purchase orders")), ("SearchProductHistory", None, _("Product history..."), group.get('search_product_history'), _("Search for product history")), ("SearchStockDecrease", None, _("Stock decreases..."), '', _("Search for manual stock decreases")), ("SearchPurchasedStockItems", None, _("Purchased items..."), group.get('search_purchased_stock_items'), _("Search for purchased items")), ("SearchBrandItems", None, _("Brand items..."), group.get('search_brand_items'), _("Search for Brand items on stock")), ("SearchStockItems", None, _("Stock items..."), group.get('search_stock_items'), _("Search for items on stock")), ("SearchTransfer", None, _("Transfers..."), group.get('search_transfers'), _("Search for stock transfers")), ("SearchClosedStockItems", None, _("Closed stock Items..."), group.get('search_closed_stock_items'), _("Search for closed stock items")), ("LoanSearch", None, _("Loans...")), ("LoanSearchItems", None, _("Loan items...")), ("ProductMenu", None, _("Product")), ("ProductStockHistory", gtk.STOCK_INFO, _("History..."), group.get('history'), _('Show the stock history of the selected product')), ("EditProduct", gtk.STOCK_EDIT, _("Edit..."), group.get('edit_product'), _("Edit the selected product, allowing you to change it's " "details")), ] self.stock_ui = self.add_ui_actions('', actions, filename='stock.xml') toggle_actions = [ ('StockPictureViewer', None, _('Picture viewer'), group.get('toggle_picture_viewer')), ] self.add_ui_actions('', toggle_actions, 'ToggleActions', 'toggle') self.set_help_section(_("Stock help"), 'app-stock') self.NewReceiving.set_short_label(_("Receive")) self.NewTransfer.set_short_label(_("Transfer")) self.EditProduct.set_short_label(_("Edit")) self.ProductStockHistory.set_short_label(_("History")) self.EditProduct.props.is_important = True self.ProductStockHistory.props.is_important = True def create_ui(self): if api.sysparam(self.store).SMART_LIST_LOADING: self.search.enable_lazy_search() self.popup = self.uimanager.get_widget('/StockSelection') self.window.add_new_items([ self.NewReceiving, self.NewTransfer, self.NewStockDecrease, self.LoanNew ]) self.window.add_search_items([ self.SearchStockItems, self.SearchBrandItems, self.SearchStockDecrease, self.SearchClosedStockItems, self.SearchProductHistory, self.SearchPurchasedStockItems, self.SearchTransfer, ]) self.window.Print.set_tooltip(_("Print a report of these products")) self._inventory_widgets = [ self.NewTransfer, self.NewReceiving, self.StockInitial, self.NewStockDecrease, self.LoanNew, self.LoanClose ] self.register_sensitive_group(self._inventory_widgets, lambda: not self.has_open_inventory()) self.image_viewer = None self.image = gtk.Image() self.edit_button = self.uimanager.get_widget( '/toolbar/AppToolbarPH/EditProduct') self.edit_button.set_icon_widget(self.image) self.image.show() self.search.set_summary_label(column='stock', label=_('<b>Stock Total:</b>'), format='<b>%s</b>', parent=self.get_statusbar_message_area()) def activate(self, refresh=True): self.window.NewToolItem.set_tooltip(_("Create a new receiving order")) self.window.SearchToolItem.set_tooltip(_("Search for stock items")) self.check_open_inventory() self._update_widgets() def setup_focus(self): self.refresh() def deactivate(self): self.uimanager.remove_ui(self.stock_ui) def new_activate(self): if not self.NewReceiving.get_sensitive(): warning(_("You cannot receive a purchase with an open inventory.")) return self._receive_purchase() def search_activate(self): self.run_dialog(ProductStockSearch, self.store) def set_open_inventory(self): self.set_sensitive(self._inventory_widgets, False) def create_filters(self): self.search.set_query(self._query) self.set_text_field_columns(['description']) self.branch_filter = ComboSearchFilter(_('Show by:'), self._get_branches()) self.branch_filter.select(api.get_current_branch(self.store)) self.add_filter(self.branch_filter, position=SearchFilterPosition.TOP) def get_columns(self): return [ SearchColumn('code', title=_('Code'), sorted=True, sort_func=sort_sellable_code, data_type=str, width=130), SearchColumn('barcode', title=_("Barcode"), data_type=str, width=130), SearchColumn('category_description', title=_("Category"), data_type=str, width=100, visible=False), SearchColumn('description', title=_("Description"), data_type=str, expand=True, ellipsize=pango.ELLIPSIZE_END), SearchColumn('manufacturer', title=_("Manufacturer"), data_type=str, visible=False), SearchColumn('model', title=_("Model"), data_type=str, visible=False), SearchColumn('location', title=_("Location"), data_type=str, width=100, visible=False), SearchColumn('stock', title=_('Quantity'), data_type=decimal.Decimal, width=100), SearchColumn('unit', title=_("Unit"), data_type=str, width=40, visible=False), Column('has_image', title=_('Picture'), data_type=bool, width=80), ] # # Private API # def _query(self, store): branch = self.branch_filter.get_state().value return self.search_spec.find_by_branch(store, branch) def _get_branches(self): items = [(b.person.name, b) for b in self.store.find(Branch)] if not items: raise DatabaseInconsistency('You should have at least one ' 'branch on your database.' 'Found zero') items.insert(0, [_('All branches'), None]) return items def _update_widgets(self): branch = api.get_current_branch(self.store) is_main_branch = self.branch_filter.get_state().value is branch item = self.results.get_selected() sellable = item and item.product.sellable if sellable: if sellable.has_image: thumbnail = sellable.image.thumbnail pixbuf = self.pixbuf_converter.from_string(thumbnail) else: pixbuf = None self._update_edit_image(pixbuf) if self.image_viewer: self.image_viewer.set_sellable(sellable) else: self._update_edit_image() self.set_sensitive([self.EditProduct], bool(item)) self.set_sensitive([self.ProductStockHistory], bool(item) and is_main_branch) # We need more than one branch to be able to do transfers # Note that 'all branches' is not a real branch has_branches = len(self.branch_filter.combo) > 2 transfer_active = self.NewTransfer.get_sensitive() self.set_sensitive([self.NewTransfer], transfer_active and has_branches) self.set_sensitive([self.SearchTransfer], has_branches) def _update_edit_image(self, pixbuf=None): if not pixbuf: self.image.set_from_stock(gtk.STOCK_EDIT, gtk.ICON_SIZE_LARGE_TOOLBAR) return # FIXME: get this icon size from settings icon_size = 24 pixbuf = pixbuf.scale_simple(icon_size, icon_size, gtk.gdk.INTERP_BILINEAR) self.image.set_from_pixbuf(pixbuf) def _update_filter_slave(self, slave): self.refresh() def _transfer_stock(self): if self.check_open_inventory(): return store = api.new_store() model = self.run_dialog(StockTransferWizard, store) store.confirm(model) store.close() self.refresh() def _receive_purchase(self): if self.check_open_inventory(): return store = api.new_store() model = self.run_dialog(ReceivingOrderWizard, store) store.confirm(model) store.close() self.refresh() # # Callbacks # def on_image_viewer_closed(self, window, event): self.StockPictureViewer.props.active = False self.image_viewer = None def on_results__has_rows(self, results, product): self._update_widgets() def on_results__selection_changed(self, results, product): self._update_widgets() def on_results__right_click(self, results, result, event): self.popup.popup(None, None, None, event.button, event.time) def on_ProductStockHistory__activate(self, button): selected = self.results.get_selected() sellable = selected.sellable self.run_dialog(ProductStockHistoryDialog, self.store, sellable, branch=self.branch_filter.combo.get_selected()) def on_EditProduct__activate(self, button): selected = self.results.get_selected() assert selected store = api.new_store() product = store.fetch(selected.product) model = self.run_dialog(ProductStockEditor, store, product) store.confirm(model) store.close() # Stock def on_NewReceiving__activate(self, button): self._receive_purchase() def on_NewTransfer__activate(self, button): self._transfer_stock() def on_NewStockDecrease__activate(self, action): if self.check_open_inventory(): return store = api.new_store() model = self.run_dialog(StockDecreaseWizard, store) store.confirm(model) store.close() self.refresh() def on_StockInitial__activate(self, action): if self.check_open_inventory(): return branch = self.branch_filter.get_state().value store = api.new_store() retval = self.run_dialog(InitialStockDialog, store, branch) store.confirm(retval) store.close() self.refresh() def on_StockPictureViewer__activate(self, button): if self.image_viewer: self.StockPictureViewer.props.active = False self.image_viewer.destroy() self.image_viewer = None else: self.StockPictureViewer.props.active = True self.image_viewer = SellableImageViewer() selected = self.results.get_selected() if selected: self.image_viewer.set_sellable(selected.product.sellable) self.image_viewer.toplevel.connect("delete-event", self.on_image_viewer_closed) self.image_viewer.toplevel.set_property("visible", True) # Loan def on_LoanNew__activate(self, action): if self.check_open_inventory(): return store = api.new_store() model = self.run_dialog(NewLoanWizard, store) store.confirm(model) store.close() self.refresh() def on_LoanClose__activate(self, action): if self.check_open_inventory(): return store = api.new_store() model = self.run_dialog(CloseLoanWizard, store) store.confirm(model) store.close() self.refresh() def on_LoanSearch__activate(self, action): self.run_dialog(LoanSearch, self.store) def on_LoanSearchItems__activate(self, action): self.run_dialog(LoanItemSearch, self.store) # Search def on_SearchPurchaseReceiving__activate(self, button): self.run_dialog(PurchaseReceivingSearch, self.store) def on_SearchTransfer__activate(self, action): self.run_dialog(TransferOrderSearch, self.store) self.refresh() def on_SearchPurchasedStockItems__activate(self, action): self.run_dialog(PurchasedItemsSearch, self.store) def on_SearchStockItems__activate(self, action): self.run_dialog(ProductStockSearch, self.store) def on_SearchBrandItems__activate(self, action): self.run_dialog(ProductBrandSearch, self.store) def on_SearchClosedStockItems__activate(self, action): self.run_dialog(ProductClosedStockSearch, self.store) def on_SearchProductHistory__activate(self, action): self.run_dialog(ProductSearchQuantity, self.store) def on_SearchStockDecrease__activate(self, action): self.run_dialog(StockDecreaseSearch, self.store)
def setUp(self): set_locale(locale.LC_ALL, 'C') self.conv = converter.get_converter(currency)