def _getVisiblePrivateSpecificationIDs(self, person, specifications): store = Store.of(specifications[0]) tables = ( Specification, Join( AccessPolicy, And( Or( Specification.distributionID == AccessPolicy.distribution_id, Specification.productID == AccessPolicy.product_id), AccessPolicy.type == Specification.information_type)), Join( AccessPolicyGrantFlat, AccessPolicy.id == AccessPolicyGrantFlat.policy_id ), LeftJoin( AccessArtifact, AccessArtifact.id == AccessPolicyGrantFlat.abstract_artifact_id), Join( TeamParticipation, TeamParticipation.teamID == AccessPolicyGrantFlat.grantee_id)) spec_ids = [spec.id for spec in specifications] return set(store.using(*tables).find( Specification.id, Or( AccessPolicyGrantFlat.abstract_artifact_id == None, AccessArtifact.specification == Specification.id), TeamParticipation.personID == person.id, In(Specification.id, spec_ids)))
def _calculate_bugtask_condition(query_arguments): """Return a condition matching importance and status for the bugtasks. :param query_arguments: an iterable of (bugtask, target) pairs, as returned by get_structural_subscription_targets. """ # This handles importance and status, which are per bugtask. # The `query_arguments` collection has pairs of bugtask, target, # where one bugtask may repeat, to be paired with multiple targets. # We know it will not be empty, because we already escaped early in # _get_structural_subscription_filter_id_query with "if not # query_arguments: return None". # This approach groups the queries around shared statuses and importances # in order to address concerns like those raised in bug 731009. # We begin be establishing that grouping. The `statuses` dict maps # bugtask statuses to a mapping of bugtask importances to a list of # targets. More concisely: # statuses map -> importances map -> list of targets statuses = defaultdict(lambda: defaultdict(list)) for bugtask, target in query_arguments: statuses[bugtask.status][bugtask.importance].append(target) # Now that they are grouped, we will build our query. If we only have # a single bugtask target, the goal is to produce a query something # like this: # And( # Or(filter's importance = bugtask's importance, # filter's importance is null), # And( # Or(filter's status = bugtask's status, # filter's status is null), # subscription matches target)) # If there are multiple bugtask targets, but the associated bugtasks # share the same importance and status, then "subscription matches # target" will become something like this: # Or(subscription matches target 1, # subscription matches target 2, # ...) # Matching targets is done using the useful # IStructuralSubscriptionTargetHelper adapter, which has a "join" # attribute on it that tells us how to distinguish that target. outer_or_conditions = [] for status, importances in statuses.items(): inner_or_conditions = [] for importance, targets in importances.items(): target_query = Or( *[IStructuralSubscriptionTargetHelper(target).join for target in targets]) importance_query = Or( BugSubscriptionFilterImportance.importance == importance, BugSubscriptionFilterImportance.importance == None) inner_or_conditions.append(And(target_query, importance_query)) status_query = Or( BugSubscriptionFilterStatus.status == status, BugSubscriptionFilterStatus.status == None) outer_or_conditions.append( And(status_query, Or(*inner_or_conditions))) return Or(*outer_or_conditions)
def get_sellable_view_query(self): branch = api.get_current_branch(self.store) branch_query = Or(Field('_stock_summary', 'branch_id') == branch.id, Eq(Field('_stock_summary', 'branch_id'), None)) query = And(branch_query, Sellable.get_available_sellables_query(self.store)) return self.sellable_view, query
def get_specification_privacy_filter(user): # Circular imports. from lp.registry.model.accesspolicy import AccessPolicyGrant public_spec_filter = ( Specification.information_type.is_in(PUBLIC_INFORMATION_TYPES)) if user is None: return [public_spec_filter] elif IPersonRoles.providedBy(user): user = user.person artifact_grant_query = Coalesce( ArrayIntersects( SQL('Specification.access_grants'), Select(ArrayAgg(TeamParticipation.teamID), tables=TeamParticipation, where=(TeamParticipation.person == user))), False) policy_grant_query = Coalesce( ArrayIntersects( Array(SQL('Specification.access_policy')), Select(ArrayAgg(AccessPolicyGrant.policy_id), tables=(AccessPolicyGrant, Join( TeamParticipation, TeamParticipation.teamID == AccessPolicyGrant.grantee_id)), where=(TeamParticipation.person == user))), False) return [Or(public_spec_filter, artifact_grant_query, policy_grant_query)]
def get_sellables_for_inventory(cls, store, branch, extra_query=None): """Returns a generator with the necessary data about the stock to open an Inventory :param store: The store to fetch data from :param branch: The branch that is being inventoried :param query: A query that should be used to restrict the storables for the inventory. This can filter based on categories or other aspects of the product. :returns: a generator of the following objects: (Sellable, Product, Storable, StorableBatch, ProductStockItem) """ # XXX: If we should want all storables to be inclued in the inventory, even if if # never had a ProductStockItem before, than we should inclue this query in the # LeftJoin with ProductStockItem below query = ProductStockItem.branch_id == branch.id if extra_query: query = And(query, extra_query) tables = [ Sellable, Join(Product, Product.id == Sellable.id), Join(Storable, Storable.id == Product.id), LeftJoin(StorableBatch, StorableBatch.storable_id == Storable.id), LeftJoin( ProductStockItem, And( ProductStockItem.storable_id == Storable.id, Or(ProductStockItem.batch_id == StorableBatch.id, Eq(ProductStockItem.batch_id, None)))), ] return store.using(*tables).find( (Sellable, Product, Storable, StorableBatch, ProductStockItem), query)
def findFromMany(self, object_ids, types=None): from lp.registry.model.person import Person object_ids = list(object_ids) if not object_ids: return {} store = IStore(XRef) extract_type = lambda id: id[0] rows = list( store.using(XRef).find( (XRef.from_type, XRef.from_id, XRef.to_type, XRef.to_id, XRef.creator_id, XRef.date_created, XRef.metadata), Or(*[ And(XRef.from_type == from_type, XRef.from_id.is_in([id[1] for id in group])) for from_type, group in groupby( sorted(object_ids, key=extract_type), extract_type) ]), XRef.to_type.is_in(types) if types is not None else True)) bulk.load(Person, [row[4] for row in rows]) result = {} for row in rows: result.setdefault((row[0], row[1]), {})[(row[2], row[3])] = { "creator": store.get(Person, row[4]) if row[4] else None, "date_created": row[5], "metadata": row[6] } return result
def getBranch(self, allow_private=False, allow_junk=True): """See `IRevision`.""" from lp.code.model.branch import Branch from lp.code.model.branchrevision import BranchRevision store = Store.of(self) query = And(self.id == BranchRevision.revision_id, BranchRevision.branch_id == Branch.id) if not allow_private: query = And( query, Branch.information_type.is_in(PUBLIC_INFORMATION_TYPES)) if not allow_junk: query = And( query, # Not-junk branches are either associated with a product # or with a source package. Or((Branch.product != None), And(Branch.sourcepackagename != None, Branch.distroseries != None))) result_set = store.find(Branch, query) if self.revision_author.person is None: result_set.order_by(Asc(BranchRevision.sequence)) else: result_set.order_by( Branch.ownerID != self.revision_author.personID, Asc(BranchRevision.sequence)) return result_set.first()
def getBuildsForBuilder(self, builder_id, status=None, user=None): """See `IBuildFarmJobSet`.""" # Imported here to avoid circular imports. from lp.soyuz.model.archive import (Archive, get_archive_privacy_filter) clauses = [ BuildFarmJob.builder == builder_id, Or(Archive.id == None, get_archive_privacy_filter(user)) ] if status is not None: clauses.append(BuildFarmJob.status == status) # We need to ensure that we don't include any private builds. # Currently only package builds can be private (via their # related archive), but not all build farm jobs will have a # related package build - hence the left join. origin = [ BuildFarmJob, LeftJoin(Archive, Archive.id == BuildFarmJob.archive_id), ] return IStore(BuildFarmJob).using(*origin).find( BuildFarmJob, *clauses).order_by(Desc(BuildFarmJob.date_finished), BuildFarmJob.id)
class SaleItemsView(Viewable): """Show information about sold items and about the corresponding sale. This is slightlig difrent than SoldItemView that groups sold items from diferent sales. """ id = SaleItem.id sellable_id = Sellable.id code = Sellable.code description = Sellable.description sale_id = SaleItem.sale_id sale_identifier = Sale.identifier sale_date = Sale.open_date client_name = Person.name quantity = SaleItem.quantity unit_description = SellableUnit.description tables = [ SaleItem, LeftJoin(Sellable, Sellable.id == SaleItem.sellable_id), LeftJoin(Sale, SaleItem.sale_id == Sale.id), LeftJoin(SellableUnit, Sellable.unit_id == SellableUnit.id), LeftJoin(Client, Sale.client_id == Client.id), LeftJoin(Person, Client.person_id == Person.id), ] clause = Or(Sale.status == Sale.STATUS_CONFIRMED, Sale.status == Sale.STATUS_PAID, Sale.status == Sale.STATUS_RENEGOTIATED, Sale.status == Sale.STATUS_ORDERED)
def _get_products(self, sort_by_name=True, additional_query=False): # FIXME: This is a kind of workaround until we have the # SQLCompletion funcionality, then we will not need to sort the # data. if sort_by_name: attr = ProductFullStockView.description else: attr = ProductFullStockView.category_description products = [] query = Eq(Product.is_grid, False) if additional_query: # XXX For now, we are not allowing package_product to have another # package_product or batch_product as component exclude_batch = Or(Eq(Storable.is_batch, False), Eq(Storable.is_batch, None)) query = And(query, exclude_batch) for product_view in self.store.find(ProductFullStockView, query).order_by(attr): if product_view.product is self._product: continue description = product_view.get_product_and_category_description() products.append((description, product_view.product)) return products
def _construct_state_query(self, search_spec, state, columns): queries = [] for column in columns: query = None if isinstance(column, str): table_field = getattr(search_spec, column) else: table_field = column if isinstance(table_field, Alias): table_field = table_field.expr if isinstance(state, NumberQueryState): query = self._parse_number_state(state, table_field) elif isinstance(state, NumberIntervalQueryState): query = self._parse_number_interval_state(state, table_field) elif isinstance(state, StringQueryState): query = self._parse_string_state(state, table_field) elif isinstance(state, DateQueryState): query = self._parse_date_state(state, table_field) elif isinstance(state, DateIntervalQueryState): query = self._parse_date_interval_state(state, table_field) elif isinstance(state, BoolQueryState): query = self._parse_bool_state(state, table_field) elif isinstance(state, MultiQueryState): query = self._parse_multi_query_state(state, table_field) else: raise NotImplementedError(state.__class__.__name__) if query: queries.append(query) if queries: return Or(*queries)
def get_sellable_view_query(self): branch = api.get_current_branch(self.store) branch_query = Or(ProductStockItem.branch_id == branch.id, Eq(ProductStockItem.branch_id, None)) query = And(branch_query, Sellable.get_available_sellables_query(self.store)) return self.sellable_view, query
def find_by_branch(cls, store, branch): if branch is None: return store.find(cls) # When we need to filter on the branch, we also need to add the branch # column on the ProductStockItem subselect, so the filter works. We cant # keep the branch_id on the main subselect, since that will cause the # results to be duplicate when not filtering by branch (probably the # most common case). So, we need all of this workaround # Make sure that the join we are replacing is the correct one. assert cls.tables[3].right == _StockSummary # Highjack the class being queried, since we need to add the branch # on the ProductStockItem subselect to filter it later class HighjackedViewable(cls): tables = cls.tables[:] tables[3] = LeftJoin( _StockBranchSummary, Field('_stock_summary', 'storable_id') == Storable.id) # Also show products that were never purchased. query = Or( Field('_stock_summary', 'branch_id') == branch.id, Eq(Field('_stock_summary', 'branch_id'), None)) return store.find(HighjackedViewable, query)
def _updateTranslationMessages(self, tm_ids): # Unset imported messages that might be in the way. PreviousImported = ClassAlias(TranslationMessage, 'PreviousImported') CurrentTranslation = ClassAlias(TranslationMessage, 'CurrentTranslation') previous_imported_select = Select( PreviousImported.id, tables=[PreviousImported, CurrentTranslation], where=And( PreviousImported.is_current_upstream == True, (PreviousImported.potmsgsetID == CurrentTranslation.potmsgsetID), Or( And(PreviousImported.potemplateID == None, CurrentTranslation.potemplateID == None), (PreviousImported.potemplateID == CurrentTranslation.potemplateID)), PreviousImported.languageID == CurrentTranslation.languageID, CurrentTranslation.id.is_in(tm_ids))) previous_imported = self.store.find( TranslationMessage, TranslationMessage.id.is_in(previous_imported_select)) previous_imported.set(is_current_upstream=False) translations = self.store.find(TranslationMessage, TranslationMessage.id.is_in(tm_ids)) translations.set(is_current_upstream=True)
def load_referencing(object_type, owning_objects, reference_keys, extra_conditions=[]): """Load objects of object_type that reference owning_objects. Note that complex types like Person are best loaded through dedicated helpers that can eager load other related things (e.g. validity for Person). :param object_type: The object type to load - e.g. BranchSubscription. :param owning_objects: The objects which are referenced. E.g. [Branch()] At this point, all the objects should be of the same type, but that constraint could be lifted in future. :param reference_keys: A list of attributes that should be used to select object_type keys. e.g. ['branchID'] :param extra_conditions: A list of Storm clauses that will be used in the final query. :return: A list of object_type where any of reference_keys refered to the primary key of any of owning_objects. """ store = IStore(object_type) if type(owning_objects) not in (list, tuple): owning_objects = tuple(owning_objects) if not owning_objects: return [] exemplar = owning_objects[0] primary_key = _primary_key(get_type(exemplar)) attribute = primary_key.name ids = set(map(attrgetter(attribute), owning_objects)) conditions = [] # Note to future self doing perf tuning: may want to make ids a WITH # clause. for column in map(partial(getattr, object_type), reference_keys): conditions.append(column.is_in(ids)) return list(store.find(object_type, Or(conditions), *extra_conditions))
class _BillandCheckPaymentView(Viewable): """A base view for check and bill payments.""" payment = Payment id = Payment.id identifier = Payment.identifier due_date = Payment.due_date paid_date = Payment.paid_date status = Payment.status value = Payment.value payment_number = Payment.payment_number method_name = PaymentMethod.method_name bank_number = BankAccount.bank_number branch = BankAccount.bank_branch account = BankAccount.bank_account tables = [ Payment, LeftJoin(CheckData, Payment.id == CheckData.payment_id), Join(PaymentMethod, Payment.method_id == PaymentMethod.id), LeftJoin(BankAccount, BankAccount.id == CheckData.bank_account_id), ] clause = Or(PaymentMethod.method_name == u'bill', PaymentMethod.method_name == u'check') def get_status_str(self): return Payment.statuses[self.status] @property def method_description(self): return get_payment_operation(self.method_name).description
def _queryTranslatableFiles(self, no_older_than=None, languages=None): """Get `POFile`s this person could help translate. :param no_older_than: Oldest involvement to consider. If the person last worked on a `POFile` before this date, that counts as not having worked on it. :param languages: Optional set of languages to restrict search to. :return: An unsorted query yielding `POFile`s. """ if self.person.is_team: return [] tables = self._composePOFileReviewerJoins(expect_reviewer_status=False) join_condition = And( POFileTranslator.personID == self.person.id, POFileTranslator.pofileID == POFile.id, POFile.language != getUtility(ILaunchpadCelebrities).english) if no_older_than is not None: join_condition = And( join_condition, POFileTranslator.date_last_touched >= no_older_than) translator_join = Join(POFileTranslator, join_condition) tables.append(translator_join) translated_count = ( POFile.currentcount + POFile.updatescount + POFile.rosettacount) conditions = translated_count < POTemplate.messagecount # The person must not be a reviewer for this translation (unless # it's in the sense that any user gets review permissions # for it). permission = Coalesce( Distribution.translationpermission, Product.translationpermission, ProjectGroup.translationpermission) Reviewership = ClassAlias(TeamParticipation, 'Reviewership') # XXX JeroenVermeulen 2009-08-28 bug=420364: Storm's Coalesce() # can't currently infer its return type from its inputs, leading # to a "can't adapt" error. Using the enum's .value works # around the problem. not_reviewer = Or( permission == TranslationPermission.OPEN.value, And( permission == TranslationPermission.STRUCTURED.value, Translator.id == None), And( permission == TranslationPermission.RESTRICTED.value, Translator.id != None, Reviewership.id == None)) conditions = And(conditions, not_reviewer) if languages is not None: conditions = And(conditions, POFile.languageID.is_in(languages)) return Store.of(self.person).using(*tables).find(POFile, conditions)
def find_by_branch(cls, store, branch): if branch: # We need the OR part to be able to list services query = Or(ProductStockItem.branch == branch, Eq(ProductStockItem.branch_id, None)) return store.find(cls, query) return store.find(cls)
def get_sellable_view_query(self): return ( self.sellable_view, # FIXME: How to do this using sellable_view.find_by_branch ? And( Or(ProductStockItem.branch_id == self.model.branch.id, Eq(ProductStockItem.branch_id, None)), Sellable.get_available_sellables_query(self.store)))
def _transaction_query(self, store): queries = [ Or(self.model.id == AccountTransaction.account_id, self.model.id == AccountTransaction.source_account_id) ] queries.extend(self._append_date_query(AccountTransaction.date)) return store.find(AccountTransactionView, And(*queries))
def transactions(self): """Returns a list of transactions to this account. :returns: list of |accounttransaction| """ return self.store.find(AccountTransaction, Or(self.id == AccountTransaction.account_id, self.id == AccountTransaction.source_account_id))
def find(cls, links): """See `IAccessArtifactGrantSource`.""" links = list(links) if len(links) == 0: return EmptyResultSet() return IStore(cls).find( cls, Or(*(And(cls.abstract_artifact == artifact, cls.policy == policy) for (artifact, policy) in links)))
def find(cls, pillars_and_types): """See `IAccessPolicySource`.""" pillars_and_types = list(pillars_and_types) if len(pillars_and_types) == 0: return EmptyResultSet() return IStore(cls).find( cls, Or(*(And(cls._constraintForPillar(pillar), cls.type == type) for (pillar, type) in pillars_and_types)))
def _get_purchase_items_by_sellable(self): query = And( PurchaseItem.sellable_id == self.target.sellable.id, Or(PurchaseOrder.status == PurchaseOrder.ORDER_CONFIRMED, PurchaseOrder.status == PurchaseOrder.ORDER_CLOSED)) join = LeftJoin(PurchaseOrder, PurchaseItem.order_id == PurchaseOrder.id) store = self.target.store return store.using(PurchaseItem, join).find(PurchaseItem, query)
def get_distroseries_pofiles(self, series, date=None, component=None, languagepack=None): """See `IVPOExport`. Selects `POFiles` based on the 'series', last modified 'date', archive 'component', and whether it belongs to a 'languagepack' """ tables = [ POFile, POTemplate, ] conditions = [ POTemplate.distroseries == series, POTemplate.iscurrent == True, POFile.potemplate == POTemplate.id, ] if date is not None: conditions.append( Or(POTemplate.date_last_updated > date, POFile.date_changed > date)) if component is not None: tables.extend([ SourcePackagePublishingHistory, Component, ]) conditions.extend([ SourcePackagePublishingHistory.distroseries == series, SourcePackagePublishingHistory.component == Component.id, POTemplate.sourcepackagename == SourcePackagePublishingHistory.sourcepackagenameID, Component.name == component, SourcePackagePublishingHistory.dateremoved == None, SourcePackagePublishingHistory.archive == series.main_archive, ]) if languagepack: conditions.append(POTemplate.languagepack == True) # Use the slave store. We may want to write to the distroseries # to register a language pack, but not to the translation data # we retrieve for it. # XXX wgrant 2017-03-21: Moved to master to avoid termination # due to long transactions. query = IStore(POFile).using(*tables).find(POFile, And(*conditions)) # Order by POTemplate. Caching in the export scripts can be # much more effective when consecutive POFiles belong to the # same POTemplate, e.g. they'll have the same POTMsgSets. sort_list = [POFile.potemplateID, POFile.languageID] return query.order_by(sort_list).config(distinct=True)
def get_sellable_view_query(self): return ( self.sellable_view, # FIXME: How to do this using sellable_view.find_by_branch ? And( Or( Field('_stock_summary', 'branch_id') == self.model.branch.id, Eq(Field('_stock_summary', 'branch_id'), None)), Sellable.get_available_sellables_query(self.store)))
def get_specification_active_product_filter(context): if (IDistribution.providedBy(context) or IDistroSeries.providedBy(context) or IProduct.providedBy(context) or IProductSeries.providedBy(context)): return [], [] from lp.registry.model.product import Product tables = [LeftJoin(Product, Specification.productID == Product.id)] active_products = (Or(Specification.product == None, Product.active == True)) return tables, [active_products]
def hwInfoByBugRelatedUsers( self, bug_ids=None, bug_tags=None, affected_by_bug=False, subscribed_to_bug=False, user=None): """See `IHWSubmissionSet`.""" if ((bug_ids is None or len(bug_ids) == 0) and (bug_tags is None or len(bug_tags) == 0)): raise ParameterError('bug_ids or bug_tags must be supplied.') tables = [ Person, HWSubmission, HWSubmissionDevice, HWDeviceDriverLink, HWDevice, HWVendorID, Bug, BugTag, ] clauses = [ Person.id == HWSubmission.ownerID, HWSubmissionDevice.submission == HWSubmission.id, HWSubmissionDevice.device_driver_link == HWDeviceDriverLink.id, HWDeviceDriverLink.device == HWDevice.id, HWDevice.bus_vendor == HWVendorID.id] if bug_ids is not None and bug_ids is not []: clauses.append(Bug.id.is_in(bug_ids)) if bug_tags is not None and bug_tags is not []: clauses.extend( [Bug.id == BugTag.bugID, BugTag.tag.is_in(bug_tags)]) clauses.append(_userCanAccessSubmissionStormClause(user)) person_clauses = [Bug.ownerID == HWSubmission.ownerID] if subscribed_to_bug: person_clauses.append( And(BugSubscription.person_id == HWSubmission.ownerID, BugSubscription.bug == Bug.id)) tables.append(BugSubscription) if affected_by_bug: person_clauses.append( And(BugAffectsPerson.personID == HWSubmission.ownerID, BugAffectsPerson.bug == Bug.id, BugAffectsPerson.affected)) tables.append(BugAffectsPerson) clauses.append(Or(person_clauses)) query = Select( columns=[ Person.name, HWVendorID.bus, HWVendorID.vendor_id_for_bus, HWDevice.bus_product_id], tables=tables, where=And(*clauses), distinct=True, order_by=[HWVendorID.bus, HWVendorID.vendor_id_for_bus, HWDevice.bus_product_id, Person.name]) return [ (person_name, HWBus.items[bus_id], vendor_id, product_id) for person_name, bus_id, vendor_id, product_id in IStore(HWSubmission).execute(query)]
def test_set_not_paid(self): sale = self.create_sale() self.add_product(sale) payment = self.add_payments(sale, method_type=u'check')[0] sale.order(self.current_user) sale.confirm(self.current_user) account = self.create_account() payment.method.destination_account = account payment.pay() # Verify if payment is referenced on account transaction. transactions = self.store.find(AccountTransaction, account=account, payment=payment) self.assertEqual(transactions.count(), 1) original_transaction = list(transactions)[0] self.assertEqual(original_transaction.operation_type, AccountTransaction.TYPE_IN) self.assertEqual(original_transaction.value, payment.value) entry = PaymentChangeHistory(payment=payment, change_reason=u'foo', store=self.store) payment.set_not_paid(entry) # Now that the payment was reverted, there should also be a reverted operation, # and the payment will not be referenced in transactions anymore. transactions = self.store.find(AccountTransaction, account=account, payment=payment) self.assertEqual(transactions.count(), 0) new_transactions = self.store.find(AccountTransaction, source_account=account) self.assertEqual(new_transactions.count(), 1) reversed_transaction = list(new_transactions)[0] self.assertEqual(reversed_transaction.operation_type, AccountTransaction.TYPE_OUT) self.assertEqual( self.store.find(AccountTransaction, payment=payment).count(), 0) # Verify all transactions - The created account, will be referenced as source # and destination account. query = Or(AccountTransaction.source_account == account, AccountTransaction.account == account) total_transactions = self.store.find(AccountTransaction, query).count() self.assertEqual(total_transactions, 2) payment.pay() self.assertEqual( self.store.find(AccountTransaction, payment=payment).count(), 1) total_transactions = self.store.find(AccountTransaction, query).count() self.assertEqual(total_transactions, 3)
def get_sellable_view_query(self): branch = self.model.branch # Also include products that are not storable branch_query = Or(self.sellable_view.branch_id == branch.id, Eq(self.sellable_view.branch_id, None)) # The stock quantity of consigned products can not be # decreased manually. See bug 5212. query = And(branch_query, Sellable.get_available_sellables_query(self.store)) return self.sellable_view, query