Exemple #1
0
    def __init__(self, parent=None, store=None, orientation=None,
                 reuse_store=False):
        """
        Create a new ModelListDialog object.
        :param store: a store connection
        """
        if orientation is None:
            orientation = gtk.ORIENTATION_VERTICAL
        if self.columns is None:
            fmt = "%s needs to set it's columns attribute"
            raise TypeError(fmt % (self.__class__.__name__, ))
        if self.model_type is None:
            fmt = "%s needs to set it's model_type attribute"
            raise TypeError(fmt % (self.__class__.__name__, ))

        if not store:
            store = api.get_default_store()
            assert not reuse_store

        self.store = store

        self.parent = parent
        self.reuse_store = reuse_store
        columns = self.columns or self.get_columns()
        ListSlave.__init__(self, columns, orientation)
        self._setup_permission()
Exemple #2
0
    def __init__(self,
                 parent=None,
                 store=None,
                 orientation=None,
                 reuse_store=False):
        """
        Create a new ModelListDialog object.
        :param store: a store connection
        """
        if orientation is None:
            orientation = gtk.ORIENTATION_VERTICAL
        if self.columns is None:
            fmt = "%s needs to set it's columns attribute"
            raise TypeError(fmt % (self.__class__.__name__, ))
        if self.model_type is None:
            fmt = "%s needs to set it's model_type attribute"
            raise TypeError(fmt % (self.__class__.__name__, ))

        if not store:
            store = api.get_default_store()
            assert not reuse_store

        self.store = store

        self.parent = parent
        self.reuse_store = reuse_store
        columns = self.columns or self.get_columns()
        ListSlave.__init__(self, columns, orientation)
        self._setup_permission()
Exemple #3
0
    def setup_slaves(self):
        self.slave = ListSlave(self._get_columns())
        self.slave.set_list_type(ListType.READONLY)
        self.storables = self.slave.listcontainer.list
        self.storables.set_cell_data_func(self._on_storables__cell_data_func)
        self.attach_slave("on_slave_holder", self.slave)

        self._refresh_storables()
Exemple #4
0
class QuoteFillingDialog(BaseEditor):
    gladefile = "HolderTemplate"
    model_type = PurchaseOrder
    title = _(u"Quote Filling")
    size = (750, 450)

    def __init__(self, model, store):
        BaseEditor.__init__(self, store, model)
        self._setup_widgets()

    def _setup_widgets(self):
        self.slave.listcontainer.add_items(self._get_quote_items())

    def _get_columns(self):
        return [
            Column("description",
                   title=_(u"Description"),
                   sorted=True,
                   data_type=str,
                   expand=True),
            Column("quantity",
                   title=_(u"Quantity"),
                   data_type=Decimal,
                   editable=True),
            Column("cost",
                   title=_(u"Cost"),
                   data_type=currency,
                   format_func=get_formatted_cost,
                   editable=True),
            Column("last_cost",
                   title=_(u"Last Cost"),
                   data_type=currency,
                   format_func=get_formatted_cost),
            Column("average_cost",
                   title=_(u"Average Cost"),
                   data_type=currency,
                   format_func=get_formatted_cost),
        ]

    def _get_quote_items(self):
        return [_TemporaryQuoteItem(item) for item in self.model.get_items()]

    #
    # BaseEditorSlave
    #

    def setup_slaves(self):
        self.slave = ListSlave(self._get_columns())
        self.slave.set_list_type(ListType.READONLY)
        self.attach_slave("place_holder", self.slave)
 def setup_slaves(self):
     self.slave = ListSlave(self._get_columns())
     self.slave.set_list_type(ListType.READONLY)
     self.storables = self.slave.listcontainer.list
     self.storables.set_cell_data_func(
         self._on_storables__cell_data_func)
     self.attach_slave("on_slave_holder", self.slave)
Exemple #6
0
class QuoteFillingDialog(BaseEditor):
    gladefile = "HolderTemplate"
    model_type = PurchaseOrder
    title = _(u"Quote Filling")
    size = (750, 450)

    def __init__(self, model, store):
        BaseEditor.__init__(self, store, model)
        self._setup_widgets()

    def _setup_widgets(self):
        self.slave.listcontainer.add_items(self._get_quote_items())

    def _get_columns(self):
        return [Column("description", title=_(u"Description"), sorted=True,
                        data_type=str, expand=True),
                Column("quantity", title=_(u"Quantity"), data_type=Decimal,
                        editable=True),
                Column("cost", title=_(u"Cost"), data_type=currency,
                        format_func=get_formatted_cost, editable=True),
                Column("last_cost", title=_(u"Last Cost"), data_type=currency,
                        format_func=get_formatted_cost),
                Column("average_cost", title=_(u"Average Cost"),
                        data_type=currency, format_func=get_formatted_cost),
                ]

    def _get_quote_items(self):
        return [_TemporaryQuoteItem(item)
                    for item in self.model.get_items()]

    #
    # BaseEditorSlave
    #

    def setup_slaves(self):
        self.slave = ListSlave(self._get_columns())
        self.slave.set_list_type(ListType.READONLY)
        self.attach_slave("place_holder", self.slave)
Exemple #7
0
 def setup_slaves(self):
     self.slave = ListSlave(self._get_columns())
     self.slave.set_list_type(ListType.READONLY)
     self.slave.listcontainer.list.connect("cell-edited",
                                           self._on_objectlist__cell_edited)
     self.attach_slave("on_slave_holder", self.slave)
Exemple #8
0
class StockCostDialog(BaseEditor):
    gladefile = "StockCostDialog"
    model_type = object
    title = _(u"Product - Stock Cost")
    size = (750, 450)

    def __init__(self, store, branch=None):
        if branch is None:
            self._branch = api.get_current_branch(store)
        else:
            self._branch = branch
        BaseEditor.__init__(self, store, model=object())
        self._setup_widgets()

    def _setup_widgets(self):
        self.branch_label.set_markup(
            _(u"Fixing stock cost for products in <b>%s</b>") %
            api.escape(self._branch.get_description()))

        items = ProductWithStockView.find_by_branch(self.store, self._branch)
        self._storables = [_TemporaryItem(s) for s in items]
        self.slave.listcontainer.add_items(self._storables)

    def _get_columns(self):
        return [
            Column("code",
                   title=_(u"Code"),
                   data_type=str,
                   sorted=True,
                   width=120),
            Column("description",
                   title=_(u"Description"),
                   data_type=str,
                   expand=True),
            Column("stock_cost",
                   title=_(u"Stock Cost"),
                   width=120,
                   data_type=currency,
                   format_func=get_formatted_cost,
                   editable=True)
        ]

    def _validate_confirm(self, item, store):
        if (item.stock_cost is not ValueUnset and item.stock_cost > 0):
            storable = item.obj.product.storable
            # TODO: add batch information here. Should we edit the the cost of
            # each batch individually, or one cost for all the batches?
            stock_item = store.fetch(
                storable.get_stock_item(self._branch, None))
            stock_item.stock_cost = item.stock_cost
            self.retval.append(item.obj.product.sellable)

    #
    # BaseEditorSlave
    #

    def setup_slaves(self):
        self.slave = ListSlave(self._get_columns())
        self.slave.set_list_type(ListType.READONLY)
        self.slave.listcontainer.list.connect("cell-edited",
                                              self._on_objectlist__cell_edited)
        self.attach_slave("on_slave_holder", self.slave)

    def on_confirm(self):
        self.retval = []
        store = api.new_store()
        for item in self._storables:
            self._validate_confirm(item, store)

        store.confirm(True)
        store.close()

    #
    # Callbacks
    #

    def _on_objectlist__cell_edited(self, objectlist, item, attr):
        # After filling a value, jump to the next cell or to the ok
        # button if we are at the last one
        treeview = objectlist.get_treeview()
        rows, column = treeview.get_cursor()
        next_row = rows[0] + 1
        nitems = len(self._storables)
        if next_row < nitems:
            treeview.set_cursor(next_row, column)
        else:
            objectlist.unselect_all()
            self.main_dialog.ok_button.grab_focus()
Exemple #9
0
class ProductCountingDialog(BaseEditor):
    gladefile = "HolderTemplate"
    model_type = Inventory
    title = _(u"Product Counting")
    size = (750, 450)

    def __init__(self, store, model):
        BaseEditor.__init__(self, store, model)
        self._setup_widgets()

    def _setup_widgets(self):
        self._inventory_items = self._get_inventory_items()
        self.slave.listcontainer.add_items(self._inventory_items)

    def _get_columns(self):
        #XXX: How to define an upper bound number for our spin button ?
        adj = gtk.Adjustment(upper=MAXINT, step_incr=1)

        return [Column("code", title=_(u"Code"), data_type=str,
                        sorted=True),
                Column("description", title=_(u"Description"),
                        data_type=str, expand=True),
                Column("actual_quantity", title=_(u"Actual quantity"),
                        data_type=Decimal, format_func=self._format_qty,
                        editable=True, spin_adjustment=adj)]

    def _format_qty(self, quantity):
        if quantity is ValueUnset:
            return None
        if quantity >= 0:
            return format_quantity(quantity)

    def _get_inventory_items(self):
        return [_TemporaryInventoryItem(i)
                    for i in self.model.get_items() if not i.adjusted()]

    def _validate_inventory_item(self, item, store):
        inventory_item = store.fetch(item.obj)
        positive = item.actual_quantity >= 0
        if not item.actual_quantity is ValueUnset and positive:
            inventory_item.actual_quantity = item.actual_quantity
        else:
            inventory_item.actual_quantity = None

    def _can_close_inventory_after_counting(self):
        if not self.model.all_items_counted():
            return False

        return not self.model.get_items_for_adjustment()

    def _close_inventory(self):
        # We will close only if the user really wants to.
        # This give the option to the user update the product
        # counting before the adjustment be done.
        msg = _('You have finished the product counting and none '
                'of the products need to be adjusted.\n\n'
                'Would you like to close this inventory now ?')
        if yesno(msg, gtk.RESPONSE_NO, _('Close inventory'),
                                       _('Continue counting')):
            store = api.new_store()
            inventory = store.fetch(self.model)
            inventory.close()
            store.confirm(inventory)
            store.close()

    #
    # BaseEditorSlave
    #

    def setup_slaves(self):
        self.slave = ListSlave(self._get_columns())
        self.slave.set_list_type(ListType.READONLY)
        self.slave.listcontainer.list.connect(
            "cell-edited", self._on_objectlist__cell_edited)
        self.attach_slave("place_holder", self.slave)

    def on_confirm(self):
        store = api.new_store()
        for item in self._inventory_items:
            self._validate_inventory_item(item, store)

        # We have to call finish_transaction here, since we will check
        # if we can close the inventory now
        store.confirm(True)
        store.close()

        if self._can_close_inventory_after_counting():
            self._close_inventory()

    #
    # Callbacks
    #

    def _on_objectlist__cell_edited(self, objectlist, item, attr):
        # After filling a value, jump to the next cell or to the ok
        # button if we are at the last one
        treeview = objectlist.get_treeview()
        rows, column = treeview.get_cursor()
        next_row = rows[0] + 1
        nitems = len(self._inventory_items)
        if next_row < nitems:
            treeview.set_cursor(next_row, column)
        else:
            self.main_dialog.ok_button.grab_focus()
Exemple #10
0
 def setup_slaves(self):
     self.slave = ListSlave(self._get_columns())
     self.slave.set_list_type(ListType.READONLY)
     self.slave.listcontainer.list.connect(
         "cell-edited", self._on_objectlist__cell_edited)
     self.attach_slave("place_holder", self.slave)
Exemple #11
0
class SellablePriceDialog(BaseEditor):
    gladefile = "SellablePriceDialog"
    model_type = object
    title = _(u"Price Change Dialog")
    size = (850, 450)

    def __init__(self, store):
        self.categories = store.find(ClientCategory)
        self._last_cat = None

        BaseEditor.__init__(self, store, model=object())
        self._setup_widgets()

    def _setup_widgets(self):
        self.category.prefill(api.for_combo(self.categories))

        prices = self.store.find(ClientCategoryPrice)
        category_prices = {}
        for p in prices:
            c = category_prices.setdefault(p.sellable_id, {})
            c[p.category_id] = p.price

        marker('SellableView')
        sellables = self.store.find(SellableView).order_by(Sellable.code)

        marker('add_items')
        for s in sellables:
            for category_id, price in category_prices.get(s.id, {}).items():
                s.set_price(category_id, price)
            self.slave.listcontainer.list.append(s)
        marker('Done add_items')

    def _get_columns(self):
        marker('_get_columns')
        self._price_columns = {}
        columns = [Column("code", title=_(u"Code"), data_type=str,
                          width=100),
                   Column("barcode", title=_(u"Barcode"), data_type=str,
                          width=100, visible=False),
                   Column("category_description", title=_(u"Category"),
                          data_type=str, width=100),
                   Column("description", title=_(u"Description"),
                          data_type=str, width=200),
                   Column("cost", title=_(u"Cost"),
                          data_type=currency, width=90),
                   Column("price", title=_(u"Default Price"),
                          data_type=currency, width=90)
                   ]

        self._price_columns[None] = columns[-1]
        for cat in self.categories:
            columns.append(Column('price_%s' % (cat.id, ),
                                  title=cat.get_description(), data_type=currency,
                                  width=90, visible=True))
            self._price_columns[cat.id] = columns[-1]
        self._columns = columns
        marker('Done _get_columns')
        return columns

    #
    # BaseEditorSlave
    #

    def setup_slaves(self):
        self.slave = ListSlave(self._get_columns())
        self.slave.set_list_type(ListType.READONLY)
        self.attach_slave("on_slave_holder", self.slave)

    def on_cancel(self):
        # Call clear on objectlist before destruction. Workaround for a bug
        # when destructing the dialog taking to long
        self.slave.listcontainer.list.clear()

    def on_confirm(self):
        marker('Saving prices')
        # FIXME: Improve this part. This is just a quick workaround to
        # release the bugfix asap
        self.main_dialog.ok_button.set_sensitive(False)
        self.main_dialog.cancel_button.set_sensitive(False)
        d = ProgressDialog(_('Updating items'), pulse=False)
        d.set_transient_for(self.main_dialog)
        d.start(wait=0)
        d.cancel.hide()

        total = len(self.slave.listcontainer.list)
        for i, s in enumerate(self.slave.listcontainer.list):
            s.save_changes()
            d.progressbar.set_text('%s/%s' % (i + 1, total))
            d.progressbar.set_fraction((i + 1) / float(total))
            while gtk.events_pending():
                gtk.main_iteration(False)

        d.stop()
        marker('Done saving prices')
        self.slave.listcontainer.list.clear()

    #
    #   Callbacks
    #

    def on_apply__clicked(self, button):
        markup = self.markup.read()
        cat = self.category.read()
        marker('Updating prices')
        for i in self.slave.listcontainer.list:
            i.set_markup(cat, markup)
            self.slave.listcontainer.list.refresh(i)
        marker('Done updating prices')
 def setup_slaves(self):
     self.slave = ListSlave(self._get_columns())
     self.slave.set_list_type(ListType.READONLY)
     self.attach_slave("on_slave_holder", self.slave)
Exemple #13
0
class ProductCountingDialog(BaseEditor):
    gladefile = "HolderTemplate"
    model_type = Inventory
    title = _(u"Product Counting")
    size = (750, 450)

    def __init__(self, store, model):
        BaseEditor.__init__(self, store, model)
        self._setup_widgets()

    def _setup_widgets(self):
        self._inventory_items = self._get_inventory_items()
        self.slave.listcontainer.add_items(self._inventory_items)

    def _get_columns(self):
        # XXX: How to define an upper bound number for our spin button ?
        adj = gtk.Adjustment(upper=MAXINT, step_incr=1)

        return [Column("code", title=_(u"Code"), data_type=str,
                       sorted=True),
                Column("description", title=_(u"Description"),
                       data_type=str, expand=True),
                Column("actual_quantity", title=_(u"Actual quantity"),
                       data_type=Decimal, format_func=self._format_qty,
                       editable=True, spin_adjustment=adj)]

    def _format_qty(self, quantity):
        if quantity is ValueUnset:
            return None
        if quantity >= 0:
            return format_quantity(quantity)

    def _get_inventory_items(self):
        return [_TemporaryInventoryItem(i)
                for i in self.model.get_items() if not i.adjusted()]

    def _validate_inventory_item(self, item, store):
        inventory_item = store.fetch(item.obj)
        positive = item.actual_quantity >= 0
        if not item.actual_quantity is ValueUnset and positive:
            inventory_item.actual_quantity = item.actual_quantity
        else:
            inventory_item.actual_quantity = None

    def _can_close_inventory_after_counting(self):
        if not self.model.all_items_counted():
            return False

        return not self.model.get_items_for_adjustment()

    def _close_inventory(self):
        # We will close only if the user really wants to.
        # This give the option to the user update the product
        # counting before the adjustment be done.
        msg = _('You have finished the product counting and none '
                'of the products need to be adjusted.\n\n'
                'Would you like to close this inventory now ?')
        if yesno(msg, gtk.RESPONSE_NO, _('Close inventory'),
                 _('Continue counting')):
            store = api.new_store()
            inventory = store.fetch(self.model)
            inventory.close()
            store.confirm(inventory)
            store.close()

    #
    # BaseEditorSlave
    #

    def setup_slaves(self):
        self.slave = ListSlave(self._get_columns())
        self.slave.set_list_type(ListType.READONLY)
        self.slave.listcontainer.list.connect(
            "cell-edited", self._on_objectlist__cell_edited)
        self.attach_slave("place_holder", self.slave)

    def on_confirm(self):
        store = api.new_store()
        for item in self._inventory_items:
            self._validate_inventory_item(item, store)

        # We have to call finish_transaction here, since we will check
        # if we can close the inventory now
        store.confirm(True)
        store.close()

        if self._can_close_inventory_after_counting():
            self._close_inventory()

    #
    # Callbacks
    #

    def _on_objectlist__cell_edited(self, objectlist, item, attr):
        # After filling a value, jump to the next cell or to the ok
        # button if we are at the last one
        treeview = objectlist.get_treeview()
        rows, column = treeview.get_cursor()
        next_row = rows[0] + 1
        nitems = len(self._inventory_items)
        if next_row < nitems:
            treeview.set_cursor(next_row, column)
        else:
            self.main_dialog.ok_button.grab_focus()
Exemple #14
0
class InitialStockDialog(BaseEditor):
    gladefile = "InitialStockDialog"
    model_type = object
    title = _(u"Initial Stock")
    size = (850, 450)
    help_section = 'stock-register-initial'

    def __init__(self, store, branch=None):
        if branch is None:
            self._branch = api.get_current_branch(store)
        else:
            self._branch = branch
        BaseEditor.__init__(self, store, model=object())

        self._setup_widgets()

    def _setup_widgets(self):
        self.branch_label.set_markup(
            _(u"Registering initial stock for products in <b>%s</b>") %
            api.escape(self._branch.person.name))

        # XXX: Find out how we are going to handle the initial stock dialog
        self._storables = [
            _TemporaryStorableItem(s) for s in self.store.find(Storable)
            if s.get_stock_item(self._branch, batch=None) is None
        ]

        self.slave.listcontainer.add_items(self._storables)

    def _get_columns(self):
        adj = gtk.Adjustment(lower=0, upper=MAXINT, step_incr=1)
        return [
            Column("code",
                   title=_(u"Code"),
                   data_type=str,
                   sorted=True,
                   width=100),
            Column("barcode", title=_(u"Barcode"), data_type=str, width=100),
            Column("category_description",
                   title=_(u"Category"),
                   data_type=str,
                   width=100),
            Column("description",
                   title=_(u"Description"),
                   data_type=str,
                   expand=True),
            Column('manufacturer',
                   title=_("Manufacturer"),
                   data_type=str,
                   visible=False),
            Column('model', title=_("Model"), data_type=str, visible=False),
            Column("initial_stock",
                   title=_(u"Initial Stock"),
                   data_type=Decimal,
                   format_func=self._format_qty,
                   editable=True,
                   spin_adjustment=adj,
                   width=110),
            Column("unit_cost",
                   title=_(u"Unit Cost"),
                   width=90,
                   data_type=currency,
                   editable=True,
                   spin_adjustment=adj)
        ]

    def _format_qty(self, quantity):
        if quantity is ValueUnset:
            return None
        if quantity >= 0:
            return quantity

    def _validate_initial_stock_quantity(self, item, store):
        if ValueUnset in [item.initial_stock, item.unit_cost]:
            return

        valid_stock = item.initial_stock > 0
        valid_cost = item.unit_cost >= 0
        if valid_stock and valid_cost:
            storable = store.fetch(item.obj)
            storable.register_initial_stock(item.initial_stock, self._branch,
                                            item.unit_cost)

    def _add_initial_stock(self):
        for item in self._storables:
            self._validate_initial_stock_quantity(item, self.store)

    #
    # BaseEditorSlave
    #

    def setup_slaves(self):
        self.slave = ListSlave(self._get_columns())
        self.slave.set_list_type(ListType.READONLY)
        self.slave.listcontainer.list.connect("cell-edited",
                                              self._on_objectlist__cell_edited)
        self.attach_slave("on_slave_holder", self.slave)

    def on_confirm(self):
        self._add_initial_stock()

    def on_cancel(self):
        if self._storables:
            msg = _('Save data before close the dialog ?')
            if yesno(msg, gtk.RESPONSE_NO, _("Save data"), _("Don't save")):
                self._add_initial_stock()
                # change retval to True so the store gets commited
                self.retval = True

    #
    # Callbacks
    #

    def _on_objectlist__cell_edited(self, objectlist, item, attr):
        # After filling a value, jump to the next cell or to the ok
        # button if we are at the last one
        treeview = objectlist.get_treeview()
        rows, column = treeview.get_cursor()
        next_row = rows[0] + 1
        nitems = len(self._storables)
        if next_row < nitems:
            treeview.set_cursor(next_row, column)
        else:
            objectlist.unselect_all()
            self.main_dialog.ok_button.grab_focus()
Exemple #15
0
class InitialStockDialog(BaseEditor):
    gladefile = "InitialStockDialog"
    model_type = object
    title = _(u"Initial Stock")
    size = (850, 450)
    help_section = 'stock-register-initial'

    def __init__(self, store, branch=None):
        if branch is None:
            self._branch = api.get_current_branch(store)
        else:
            self._branch = branch
        BaseEditor.__init__(self, store, model=object())

        self._setup_widgets()

    def _setup_widgets(self):
        self.branch_label.set_markup(
            _(u"Registering initial stock for products in <b>%s</b>") %
            api.escape(self._branch.person.name))

        self.slave.listcontainer.add_items(self._get_storables())

    def _get_storables(self):
        for s in Storable.get_storables_without_stock_item(
                self.store, self._branch):
            yield _TemporaryStorableItem(s)

    def _get_columns(self):
        adj = gtk.Adjustment(lower=0, upper=MAX_INT, step_incr=1)
        return [
            Column("code",
                   title=_(u"Code"),
                   data_type=str,
                   sorted=True,
                   width=100),
            Column("barcode", title=_(u"Barcode"), data_type=str, width=100),
            Column("category_description",
                   title=_(u"Category"),
                   data_type=str,
                   width=100),
            Column("description",
                   title=_(u"Description"),
                   data_type=str,
                   expand=True),
            Column('manufacturer',
                   title=_("Manufacturer"),
                   data_type=str,
                   visible=False),
            Column('model', title=_("Model"), data_type=str, visible=False),
            Column("initial_stock",
                   title=_(u"Initial Stock"),
                   data_type=Decimal,
                   format_func=self._format_qty,
                   editable=True,
                   spin_adjustment=adj,
                   width=110),
            Column("unit_cost",
                   title=_(u"Unit Cost"),
                   width=90,
                   data_type=currency,
                   editable=True,
                   spin_adjustment=adj)
        ]

    def _format_qty(self, quantity):
        if quantity is ValueUnset:
            return None
        if quantity >= 0:
            return quantity

    def _validate_initial_stock_quantity(self, item, store):
        if ValueUnset in [item.initial_stock, item.unit_cost]:
            return

        valid_stock = item.initial_stock > 0
        valid_cost = item.unit_cost >= 0
        if valid_stock and valid_cost:
            storable = store.fetch(item.obj)
            if item.is_batch:
                for batch_item in item.batches:
                    storable.register_initial_stock(
                        batch_item.quantity,
                        self._branch,
                        item.unit_cost,
                        batch_number=batch_item.batch)
            else:
                storable.register_initial_stock(item.initial_stock,
                                                self._branch, item.unit_cost)

    def _add_initial_stock(self):
        for item in self.storables:
            self._validate_initial_stock_quantity(item, self.store)

    #
    # BaseEditorSlave
    #

    def setup_slaves(self):
        self.slave = ListSlave(self._get_columns())
        self.slave.set_list_type(ListType.READONLY)
        self.storables = self.slave.listcontainer.list
        self.storables.set_cell_data_func(self._on_storables__cell_data_func)
        self.attach_slave("on_slave_holder", self.slave)

    def on_confirm(self):
        self._add_initial_stock()

    def on_cancel(self):
        if len(self.storables):
            msg = _('Save data before close the dialog ?')
            if yesno(msg, gtk.RESPONSE_NO, _("Save data"), _("Don't save")):
                self._add_initial_stock()
                # change retval to True so the store gets commited
                self.retval = True

    #
    # Callbacks
    #

    def _on_storables__cell_data_func(self, column, renderer, obj, text):
        if not isinstance(renderer, gtk.CellRendererText):
            return text

        if column.attribute == 'initial_stock':
            renderer.set_property('editable-set', not obj.is_batch)
            renderer.set_property('editable', not obj.is_batch)

        return text

    def on_storables__row_activated(self, storables, item):
        if item.is_batch:
            retval = run_dialog(BatchIncreaseSelectionDialog,
                                self,
                                store=self.store,
                                model=item.storable,
                                quantity=0,
                                original_batches=item.batches)
            item.batches = retval or item.batches
            self.storables.update(item)

    def on_storables__cell_edited(self, storables, item, attr):
        # After filling a value, jump to the next cell or to the ok
        # button if we are at the last one
        treeview = storables.get_treeview()
        rows, column = treeview.get_cursor()
        next_row = rows[0] + 1
        nitems = len(self.storables)
        if next_row < nitems:
            treeview.set_cursor(next_row, column)
        else:
            storables.unselect_all()
            self.main_dialog.ok_button.grab_focus()
Exemple #16
0
class StockCostDialog(BaseEditor):
    gladefile = "InitialStockDialog"
    model_type = object
    title = _(u"Product - Stock Cost")
    size = (750, 450)

    def __init__(self, store, branch=None):
        if branch is None:
            self._branch = api.get_current_branch(store)
        else:
            self._branch = branch
        BaseEditor.__init__(self, store, model=object())
        self._setup_widgets()

    def _setup_widgets(self):
        self.branch_label.set_markup(
            _(u"Fixing stock cost for products in <b>%s</b>") %
            api.escape(self._branch.person.name))

        items = ProductWithStockView.find_by_branch(self.store, self._branch)
        self._storables = [_TemporaryItem(s) for s in items]
        self.slave.listcontainer.add_items(self._storables)

    def _get_columns(self):
        return [Column("code", title=_(u"Code"), data_type=str,
                        sorted=True, width=120),
                Column("description", title=_(u"Description"),
                        data_type=str, expand=True),
                Column("stock_cost", title=_(u"Stock Cost"), width=120,
                        data_type=currency, format_func=get_formatted_cost,
                        editable=True)]

    def _validate_confirm(self, item, store):
        if (item.stock_cost is not ValueUnset and
            item.stock_cost > 0):
            storable = item.obj.product.storable
            stock_item = store.fetch(storable.get_stock_item(self._branch))
            stock_item.stock_cost = item.stock_cost
            self.retval.append(item.obj.product.sellable)

    #
    # BaseEditorSlave
    #

    def setup_slaves(self):
        self.slave = ListSlave(self._get_columns())
        self.slave.set_list_type(ListType.READONLY)
        self.slave.listcontainer.list.connect(
            "cell-edited", self._on_objectlist__cell_edited)
        self.attach_slave("on_slave_holder", self.slave)

    def on_confirm(self):
        self.retval = []
        store = api.new_store()
        for item in self._storables:
            self._validate_confirm(item, store)

        store.confirm(True)
        store.close()

    #
    # Callbacks
    #

    def _on_objectlist__cell_edited(self, objectlist, item, attr):
        # After filling a value, jump to the next cell or to the ok
        # button if we are at the last one
        treeview = objectlist.get_treeview()
        rows, column = treeview.get_cursor()
        next_row = rows[0] + 1
        nitems = len(self._storables)
        if next_row < nitems:
            treeview.set_cursor(next_row, column)
        else:
            objectlist.unselect_all()
            self.main_dialog.ok_button.grab_focus()
Exemple #17
0
class InitialStockDialog(BaseEditor):
    gladefile = "InitialStockDialog"
    model_type = Settable
    title = _(u"Initial Stock")
    size = (850, 450)
    help_section = 'stock-register-initial'
    proxy_widgets = ['branch']

    need_cancel_confirmation = True

    #
    # Private
    #

    def _refresh_storables(self):
        self.slave.listcontainer.list.add_list(self._get_storables())

    def _get_storables(self):
        self._current_branch = self.model.branch
        # Cache this data, to avoid more queries when building the list of storables.
        self._categories = list(self.store.find(SellableCategory))
        data = Storable.get_initial_stock_data(self.store, self.model.branch)
        for sellable, product, storable in data:
            yield _TemporaryStorableItem(sellable, product, storable)

    def _get_columns(self):
        adj = Gtk.Adjustment(lower=0, upper=MAX_INT, step_increment=1)
        return [Column("code", title=_(u"Code"), data_type=str, sorted=True,
                       width=100),
                Column("barcode", title=_(u"Barcode"), data_type=str,
                       width=100),
                Column("category_description", title=_(u"Category"),
                       data_type=str, width=100),
                Column("description", title=_(u"Description"),
                       data_type=str, expand=True),
                Column('manufacturer', title=_("Manufacturer"),
                       data_type=str, visible=False),
                Column('model', title=_("Model"),
                       data_type=str, visible=False),
                Column("initial_stock", title=_(u"Initial Stock"),
                       data_type=Decimal, format_func=self._format_qty,
                       editable=True, spin_adjustment=adj, width=110),
                Column("unit_cost", title=_(u"Unit Cost"), width=90,
                       data_type=currency, editable=True, spin_adjustment=adj)]

    def _format_qty(self, quantity):
        if quantity is ValueUnset:
            return None
        if quantity >= 0:
            return quantity

    def _validate_initial_stock_quantity(self, item):
        if ValueUnset in [item.initial_stock, item.unit_cost]:
            return

        valid_stock = item.initial_stock > 0
        valid_cost = item.unit_cost >= 0
        if valid_stock and valid_cost:
            storable = item.storable
            if item.is_batch:
                for batch, quantity in item.batches.items():
                    storable.register_initial_stock(quantity,
                                                    self.model.branch,
                                                    item.unit_cost,
                                                    batch_number=batch)
            else:
                storable.register_initial_stock(item.initial_stock, self.model.branch,
                                                item.unit_cost, api.get_current_user(self.store))

    def _add_initial_stock(self):
        for item in self.storables:
            self._validate_initial_stock_quantity(item)

    #
    # BaseEditorSlave
    #

    def create_model(self, store):
        self._current_branch = api.get_current_branch(store)
        return Settable(branch=self._current_branch)

    def setup_proxies(self):
        if api.sysparam.get_bool('SYNCHRONIZED_MODE'):
            current = api.get_current_branch(self.store)
            branches = [(current.get_description(), current)]
        else:
            branches = api.for_combo(Branch.get_active_branches(self.store))

        self.branch.prefill(branches)
        self.add_proxy(self.model, self.proxy_widgets)

    def setup_slaves(self):
        self.slave = ListSlave(self._get_columns())
        self.slave.set_list_type(ListType.READONLY)
        self.storables = self.slave.listcontainer.list
        self.storables.set_cell_data_func(
            self._on_storables__cell_data_func)
        self.attach_slave("on_slave_holder", self.slave)

        self._refresh_storables()

    def on_confirm(self):
        self._add_initial_stock()

    def has_changes(self):
        return any(i.initial_stock > 0 for i in self.storables)

    #
    # Callbacks
    #

    def _on_storables__cell_data_func(self, column, renderer, obj, text):
        if not isinstance(renderer, Gtk.CellRendererText):
            return text

        if column.attribute == 'initial_stock':
            renderer.set_property('editable-set', not obj.is_batch)
            renderer.set_property('editable', not obj.is_batch)

        return text

    def on_storables__row_activated(self, storables, item):
        if item.is_batch:
            retval = run_dialog(BatchIncreaseSelectionDialog, self,
                                store=self.store, model=item.storable,
                                quantity=0, original_batches=item.batches)
            item.batches = retval or item.batches
            self.storables.update(item)

    def on_storables__cell_edited(self, storables, item, attr):
        # After filling a value, jump to the next cell or to the ok
        # button if we are at the last one
        treeview = storables.get_treeview()
        rows, column = treeview.get_cursor()
        next_row = rows[0] + 1
        nitems = len(self.storables)
        if next_row < nitems:
            treeview.set_cursor(next_row, column)
        else:
            storables.unselect_all()
            self.main_dialog.ok_button.grab_focus()

    def after_branch__content_changed(self, widget):
        # There is a little bug in kiwi that causes this signal to be emmited
        # after just losing focus
        if self.model.branch == self._current_branch:
            return
        self._refresh_storables()
class InitialStockDialog(BaseEditor):
    gladefile = "InitialStockDialog"
    model_type = Settable
    title = _(u"Initial Stock")
    size = (850, 450)
    help_section = 'stock-register-initial'
    proxy_widgets = ['branch']

    #
    # Private
    #

    def _refresh_storables(self):
        self.slave.listcontainer.list.add_list(self._get_storables())

    def _get_storables(self):
        for s in Storable.get_storables_without_stock_item(self.store,
                                                           self.model.branch):
            yield _TemporaryStorableItem(s)

    def _get_columns(self):
        adj = gtk.Adjustment(lower=0, upper=MAX_INT, step_incr=1)
        return [Column("code", title=_(u"Code"), data_type=str, sorted=True,
                       width=100),
                Column("barcode", title=_(u"Barcode"), data_type=str,
                       width=100),
                Column("category_description", title=_(u"Category"),
                       data_type=str, width=100),
                Column("description", title=_(u"Description"),
                       data_type=str, expand=True),
                Column('manufacturer', title=_("Manufacturer"),
                       data_type=str, visible=False),
                Column('model', title=_("Model"),
                       data_type=str, visible=False),
                Column("initial_stock", title=_(u"Initial Stock"),
                       data_type=Decimal, format_func=self._format_qty,
                       editable=True, spin_adjustment=adj, width=110),
                Column("unit_cost", title=_(u"Unit Cost"), width=90,
                       data_type=currency, editable=True, spin_adjustment=adj)]

    def _format_qty(self, quantity):
        if quantity is ValueUnset:
            return None
        if quantity >= 0:
            return quantity

    def _validate_initial_stock_quantity(self, item, store):
        if ValueUnset in [item.initial_stock, item.unit_cost]:
            return

        valid_stock = item.initial_stock > 0
        valid_cost = item.unit_cost >= 0
        if valid_stock and valid_cost:
            storable = store.fetch(item.obj)
            if item.is_batch:
                for batch, quantity in item.batches.items():
                    storable.register_initial_stock(quantity,
                                                    self.model.branch,
                                                    item.unit_cost,
                                                    batch_number=batch)
            else:
                storable.register_initial_stock(item.initial_stock,
                                                self.model.branch,
                                                item.unit_cost)

    def _add_initial_stock(self):
        for item in self.storables:
            self._validate_initial_stock_quantity(item, self.store)

    #
    # BaseEditorSlave
    #

    def create_model(self, store):
        return Settable(branch=api.get_current_branch(store))

    def setup_proxies(self):
        self.branch.prefill(
            api.for_combo(Branch.get_active_branches(self.store)))
        self.add_proxy(self.model, self.proxy_widgets)

    def setup_slaves(self):
        self.slave = ListSlave(self._get_columns())
        self.slave.set_list_type(ListType.READONLY)
        self.storables = self.slave.listcontainer.list
        self.storables.set_cell_data_func(
            self._on_storables__cell_data_func)
        self.attach_slave("on_slave_holder", self.slave)

        self._refresh_storables()

    def on_confirm(self):
        self._add_initial_stock()

    def on_cancel(self):
        if len(self.storables):
            msg = _('Save data before close the dialog ?')
            if yesno(msg, gtk.RESPONSE_NO, _("Save data"), _("Don't save")):
                self._add_initial_stock()
                # change retval to True so the store gets commited
                self.retval = True

    #
    # Callbacks
    #

    def _on_storables__cell_data_func(self, column, renderer, obj, text):
        if not isinstance(renderer, gtk.CellRendererText):
            return text

        if column.attribute == 'initial_stock':
            renderer.set_property('editable-set', not obj.is_batch)
            renderer.set_property('editable', not obj.is_batch)

        return text

    def on_storables__row_activated(self, storables, item):
        if item.is_batch:
            retval = run_dialog(BatchIncreaseSelectionDialog, self,
                                store=self.store, model=item.storable,
                                quantity=0, original_batches=item.batches)
            item.batches = retval or item.batches
            self.storables.update(item)

    def on_storables__cell_edited(self, storables, item, attr):
        # After filling a value, jump to the next cell or to the ok
        # button if we are at the last one
        treeview = storables.get_treeview()
        rows, column = treeview.get_cursor()
        next_row = rows[0] + 1
        nitems = len(self.storables)
        if next_row < nitems:
            treeview.set_cursor(next_row, column)
        else:
            storables.unselect_all()
            self.main_dialog.ok_button.grab_focus()

    def after_branch__content_changed(self, widget):
        self._refresh_storables()
class InitialStockDialog(BaseEditor):
    gladefile = "InitialStockDialog"
    model_type = object
    title = _(u"Initial Stock")
    size = (850, 450)
    help_section = 'stock-register-initial'

    def __init__(self, store, branch=None):
        if branch is None:
            self._branch = api.get_current_branch(store)
        else:
            self._branch = branch
        BaseEditor.__init__(self, store, model=object())

        self._setup_widgets()

    def _setup_widgets(self):
        self.branch_label.set_markup(
            _(u"Registering initial stock for products in <b>%s</b>") %
            api.escape(self._branch.person.name))

        # XXX: Find out how we are going to handle the initial stock dialog
        self._storables = [_TemporaryStorableItem(s)
                           for s in self.store.find(Storable)
                           if s.get_stock_item(self._branch, batch=None) is None]

        self.slave.listcontainer.add_items(self._storables)

    def _get_columns(self):
        adj = gtk.Adjustment(lower=0, upper=MAXINT, step_incr=1)
        return [Column("code", title=_(u"Code"), data_type=str, sorted=True,
                       width=100),
                Column("barcode", title=_(u"Barcode"), data_type=str,
                       width=100),
                Column("category_description", title=_(u"Category"),
                       data_type=str, width=100),
                Column("description", title=_(u"Description"),
                       data_type=str, expand=True),
                Column('manufacturer', title=_("Manufacturer"),
                       data_type=str, visible=False),
                Column('model', title=_("Model"),
                       data_type=str, visible=False),
                Column("initial_stock", title=_(u"Initial Stock"),
                       data_type=Decimal, format_func=self._format_qty,
                       editable=True, spin_adjustment=adj, width=110),
                Column("unit_cost", title=_(u"Unit Cost"), width=90,
                       data_type=currency, editable=True, spin_adjustment=adj)]

    def _format_qty(self, quantity):
        if quantity is ValueUnset:
            return None
        if quantity >= 0:
            return quantity

    def _validate_initial_stock_quantity(self, item, store):
        if ValueUnset in [item.initial_stock, item.unit_cost]:
            return

        valid_stock = item.initial_stock > 0
        valid_cost = item.unit_cost >= 0
        if valid_stock and valid_cost:
            storable = store.fetch(item.obj)
            storable.register_initial_stock(item.initial_stock, self._branch,
                                            item.unit_cost)

    def _add_initial_stock(self):
        for item in self._storables:
            self._validate_initial_stock_quantity(item, self.store)

    #
    # BaseEditorSlave
    #

    def setup_slaves(self):
        self.slave = ListSlave(self._get_columns())
        self.slave.set_list_type(ListType.READONLY)
        self.slave.listcontainer.list.connect(
            "cell-edited", self._on_objectlist__cell_edited)
        self.attach_slave("on_slave_holder", self.slave)

    def on_confirm(self):
        self._add_initial_stock()

    def on_cancel(self):
        if self._storables:
            msg = _('Save data before close the dialog ?')
            if yesno(msg, gtk.RESPONSE_NO, _("Save data"), _("Don't save")):
                self._add_initial_stock()
                # change retval to True so the store gets commited
                self.retval = True

    #
    # Callbacks
    #

    def _on_objectlist__cell_edited(self, objectlist, item, attr):
        # After filling a value, jump to the next cell or to the ok
        # button if we are at the last one
        treeview = objectlist.get_treeview()
        rows, column = treeview.get_cursor()
        next_row = rows[0] + 1
        nitems = len(self._storables)
        if next_row < nitems:
            treeview.set_cursor(next_row, column)
        else:
            objectlist.unselect_all()
            self.main_dialog.ok_button.grab_focus()
Exemple #20
0
 def setup_slaves(self):
     self.slave = ListSlave(self._get_columns())
     self.slave.set_list_type(ListType.READONLY)
     self.attach_slave("on_slave_holder", self.slave)
class SellablePriceDialog(BaseEditor):
    gladefile = "SellablePriceDialog"
    model_type = object
    title = _(u"Price Change Dialog")
    size = (850, 450)

    def __init__(self, store):
        self.categories = store.find(ClientCategory)
        self._last_cat = None

        BaseEditor.__init__(self, store, model=object())
        self._setup_widgets()

    def _setup_widgets(self):
        self.category.prefill(api.for_combo(self.categories))

        prices = self.store.find(ClientCategoryPrice).order_by(ClientCategoryPrice.id)
        category_prices = {}
        for p in prices:
            c = category_prices.setdefault(p.sellable_id, {})
            c[p.category_id] = p.price

        marker('SellableView')
        sellables = self.store.find(SellableView).order_by(Sellable.code)

        marker('add_items')
        for s in sellables:
            for category_id, price in category_prices.get(s.id, {}).items():
                s.set_price(category_id, price)
            self.slave.listcontainer.list.append(s)
        marker('Done add_items')

    def _get_columns(self):
        marker('_get_columns')
        self._price_columns = {}
        columns = [Column("code", title=_(u"Code"), data_type=str,
                          width=100),
                   Column("barcode", title=_(u"Barcode"), data_type=str,
                          width=100, visible=False),
                   Column("category_description", title=_(u"Category"),
                          data_type=str, width=100),
                   Column("description", title=_(u"Description"),
                          data_type=str, width=200),
                   Column("cost", title=_(u"Cost"),
                          data_type=currency, width=90),
                   Column("price", title=_(u"Default Price"),
                          data_type=currency, width=90)
                   ]

        self._price_columns[None] = columns[-1]
        for cat in self.categories:
            columns.append(Column('price_%s' % (cat.id, ),
                                  title=cat.get_description(), data_type=currency,
                                  width=90, visible=True))
            self._price_columns[cat.id] = columns[-1]
        self._columns = columns
        marker('Done _get_columns')
        return columns

    #
    # BaseEditorSlave
    #

    def setup_slaves(self):
        self.slave = ListSlave(self._get_columns())
        self.slave.set_list_type(ListType.READONLY)
        self.attach_slave("on_slave_holder", self.slave)

    def on_cancel(self):
        # Call clear on objectlist before destruction. Workaround for a bug
        # when destructing the dialog taking to long
        self.slave.listcontainer.list.clear()

    def on_confirm(self):
        marker('Saving prices')
        # FIXME: Improve this part. This is just a quick workaround to
        # release the bugfix asap
        self.main_dialog.ok_button.set_sensitive(False)
        self.main_dialog.cancel_button.set_sensitive(False)
        d = ProgressDialog(_('Updating items'), pulse=False)
        d.set_transient_for(self.main_dialog)
        d.start(wait=0)
        d.cancel.hide()

        total = len(self.slave.listcontainer.list)
        for i, s in enumerate(self.slave.listcontainer.list):
            s.save_changes()
            d.progressbar.set_text('%s/%s' % (i + 1, total))
            d.progressbar.set_fraction((i + 1) / float(total))
            while gtk.events_pending():
                gtk.main_iteration(False)

        d.stop()
        marker('Done saving prices')
        self.slave.listcontainer.list.clear()

    #
    #   Callbacks
    #

    def on_apply__clicked(self, button):
        markup = self.markup.read()
        cat = self.category.read()
        marker('Updating prices')
        for i in self.slave.listcontainer.list:
            i.set_markup(cat, markup)
            self.slave.listcontainer.list.refresh(i)
        marker('Done updating prices')
Exemple #22
0
class InitialStockDialog(BaseEditor):
    gladefile = "InitialStockDialog"
    model_type = Settable
    title = _(u"Initial Stock")
    size = (850, 450)
    help_section = 'stock-register-initial'
    proxy_widgets = ['branch']

    need_cancel_confirmation = True

    #
    # Private
    #

    def _refresh_storables(self):
        self.slave.listcontainer.list.add_list(self._get_storables())

    def _get_storables(self):
        self._current_branch = self.model.branch
        # Cache this data, to avoid more queries when building the list of storables.
        self._categories = list(self.store.find(SellableCategory))
        data = Storable.get_initial_stock_data(self.store, self.model.branch)
        for sellable, product, storable in data:
            yield _TemporaryStorableItem(sellable, product, storable)

    def _get_columns(self):
        adj = gtk.Adjustment(lower=0, upper=MAX_INT, step_incr=1)
        return [Column("code", title=_(u"Code"), data_type=str, sorted=True,
                       width=100),
                Column("barcode", title=_(u"Barcode"), data_type=str,
                       width=100),
                Column("category_description", title=_(u"Category"),
                       data_type=str, width=100),
                Column("description", title=_(u"Description"),
                       data_type=str, expand=True),
                Column('manufacturer', title=_("Manufacturer"),
                       data_type=str, visible=False),
                Column('model', title=_("Model"),
                       data_type=str, visible=False),
                Column("initial_stock", title=_(u"Initial Stock"),
                       data_type=Decimal, format_func=self._format_qty,
                       editable=True, spin_adjustment=adj, width=110),
                Column("unit_cost", title=_(u"Unit Cost"), width=90,
                       data_type=currency, editable=True, spin_adjustment=adj)]

    def _format_qty(self, quantity):
        if quantity is ValueUnset:
            return None
        if quantity >= 0:
            return quantity

    def _validate_initial_stock_quantity(self, item):
        if ValueUnset in [item.initial_stock, item.unit_cost]:
            return

        valid_stock = item.initial_stock > 0
        valid_cost = item.unit_cost >= 0
        if valid_stock and valid_cost:
            storable = item.storable
            if item.is_batch:
                for batch, quantity in item.batches.items():
                    storable.register_initial_stock(quantity,
                                                    self.model.branch,
                                                    item.unit_cost,
                                                    batch_number=batch)
            else:
                storable.register_initial_stock(item.initial_stock,
                                                self.model.branch,
                                                item.unit_cost)

    def _add_initial_stock(self):
        for item in self.storables:
            self._validate_initial_stock_quantity(item)

    #
    # BaseEditorSlave
    #

    def create_model(self, store):
        self._current_branch = api.get_current_branch(store)
        return Settable(branch=self._current_branch)

    def setup_proxies(self):
        if api.sysparam.get_bool('SYNCHRONIZED_MODE'):
            current = api.get_current_branch(self.store)
            branches = [(current.get_description(), current)]
        else:
            branches = api.for_combo(Branch.get_active_branches(self.store))

        self.branch.prefill(branches)
        self.add_proxy(self.model, self.proxy_widgets)

    def setup_slaves(self):
        self.slave = ListSlave(self._get_columns())
        self.slave.set_list_type(ListType.READONLY)
        self.storables = self.slave.listcontainer.list
        self.storables.set_cell_data_func(
            self._on_storables__cell_data_func)
        self.attach_slave("on_slave_holder", self.slave)

        self._refresh_storables()

    def on_confirm(self):
        self._add_initial_stock()

    def has_changes(self):
        return any(i.initial_stock > 0 for i in self.storables)

    #
    # Callbacks
    #

    def _on_storables__cell_data_func(self, column, renderer, obj, text):
        if not isinstance(renderer, gtk.CellRendererText):
            return text

        if column.attribute == 'initial_stock':
            renderer.set_property('editable-set', not obj.is_batch)
            renderer.set_property('editable', not obj.is_batch)

        return text

    def on_storables__row_activated(self, storables, item):
        if item.is_batch:
            retval = run_dialog(BatchIncreaseSelectionDialog, self,
                                store=self.store, model=item.storable,
                                quantity=0, original_batches=item.batches)
            item.batches = retval or item.batches
            self.storables.update(item)

    def on_storables__cell_edited(self, storables, item, attr):
        # After filling a value, jump to the next cell or to the ok
        # button if we are at the last one
        treeview = storables.get_treeview()
        rows, column = treeview.get_cursor()
        next_row = rows[0] + 1
        nitems = len(self.storables)
        if next_row < nitems:
            treeview.set_cursor(next_row, column)
        else:
            storables.unselect_all()
            self.main_dialog.ok_button.grab_focus()

    def after_branch__content_changed(self, widget):
        # There is a little bug in kiwi that causes this signal to be emmited
        # after just losing focus
        if self.model.branch == self._current_branch:
            return
        self._refresh_storables()