def detailview_display(self, context): organisation = context['object'] user = context['user'] managed_orgas = Organisation.objects.filter_managed_by_creme() get_fconfigs = context['fields_configs'].get_4_model is_hidden = get_fconfigs(Organisation).is_fieldname_hidden return self._render( self.get_template_context( context, hidden_fields={ fname for fname in ('phone', 'billing_address', 'legal_form') if is_hidden(fname) }, position_is_hidden=get_fconfigs(Contact).is_fieldname_hidden( 'position'), is_customer=managed_orgas.filter( relations__type=constants.REL_OBJ_CUSTOMER_SUPPLIER, relations__object_entity=organisation.id, ).exists(), is_supplier=managed_orgas.filter( relations__type=constants.REL_SUB_CUSTOMER_SUPPLIER, relations__object_entity=organisation.id, ).exists(), managers=EntityCredentials.filter( user, organisation.get_managers())[:16], employees=EntityCredentials.filter( user, organisation.get_employees())[:16], activities=Activities4Card.get(context, organisation), opportunities=Opportunities4Card.get(context, organisation), acts=CommercialActs4Card.get(context, organisation)))
def _build_entity_queryset(user, model, list_view_state, extra_q, entity_filter, header_filter): filtered = False use_distinct = False queryset = model.objects.filter(is_deleted=False) if entity_filter: filtered = True queryset = entity_filter.filter(queryset) if extra_q: try: queryset = queryset.filter(extra_q) except Exception as e: logger.exception( 'Error when building the search queryset: invalid q_filter (%s).', e) else: filtered = True use_distinct = True list_view_state.extra_q = extra_q # TODO: only if valid ? # TODO: method in ListViewState that returns the improved queryset lv_state_q = list_view_state.get_q_with_research(model, header_filter.cells) try: queryset = queryset.filter(lv_state_q) except Exception as e: logger.exception( 'Error when building the search queryset with Q=%s (%s).', lv_state_q, e) else: if lv_state_q: filtered = True use_distinct = True queryset = EntityCredentials.filter(user, queryset) if use_distinct: queryset = queryset.distinct() # If the query does not use the real entities' specific fields to filter, # we perform a query on CremeEntity & so we avoid a JOIN. count = queryset.count() if filtered else \ EntityCredentials.filter_entities( user, CremeEntity.objects.filter( is_deleted=False, entity_type=ContentType.objects.get_for_model(model), ), as_model=model, ).count() return queryset, count
def _fetch(self, limit_to: Optional[int] = None, extra_q: Optional[models.Q] = None, user=None) -> Iterator[list]: user = user or get_user_model()(is_superuser=True) entities = EntityCredentials.filter( user, self.ct.model_class().objects.filter(is_deleted=False), ) if self.filter is not None: entities = self.filter.filter(entities) if extra_q is not None: entities = entities.filter(extra_q) if limit_to: entities = entities[:limit_to] fields = self.filtered_columns for entity in entities: yield [ field.get_value(entity, scope=entities, user=user) for field in fields ]
def sum_totals_no_vat_multi(model, entities, user, **kwargs): bill_info_map = defaultdict(list) bill_ids = [] for bill_id, total, e_id in EntityCredentials.filter( user, model.objects.filter( relations__type=REL_SUB_BILL_RECEIVED, relations__object_entity__in=[e.id for e in entities], is_deleted=False, total_no_vat__isnull=False, **kwargs).values_list('id', 'total_no_vat', 'relations__object_entity')): bill_info_map[e_id].append((bill_id, total)) bill_ids.append(bill_id) managed_bill_ids = { *Relation.objects.filter( subject_entity__in=[ # NB: not values_list() to use the cache of filter_managed_by_creme() o.id for o in Organisation.objects.filter_managed_by_creme() ], type=REL_OBJ_BILL_ISSUED, object_entity_id__in=bill_ids, ).values_list('object_entity', flat=True) } for entity in entities: yield (entity, sum(total for bill_id, total in bill_info_map[entity.id] if bill_id in managed_bill_ids))
def get_empty_message(self, form, lines): if form.is_valid(): if lines: empty_message = '' else: report = self.object user = self.request.user ct = report.ct if not EntityCredentials.filter( user, ct.model_class().objects.all()).exists(): empty_message = _('You can see no «{model}»').format( model=ct) elif report.filter and not report.fetch_all_lines(limit_to=1, user=user): empty_message = _( 'No «{model}» matches the filter «{filter}»').format( model=ct, filter=report.filter, ) else: empty_message = _('No «{model}» matches your date filter' ).format(model=ct) else: empty_message = _('Fix your date filter') return empty_message
def addresses_from_persons(queryset, user): entities = EntityCredentials.filter(user, queryset.filter(is_deleted=False)) addresses = get_address_model().objects.filter( content_type=ContentType.objects.get_for_model(queryset.model)) # get address ids which owner has billing or shipping or both billing_shipping_ids = { owner: billing or shipping for owner, billing, shipping in entities.filter( Q(billing_address__isnull=False) | Q(shipping_address__isnull=False)).values_list( 'pk', 'billing_address', 'shipping_address') } # get address ids which owner without billing nor shipping address_ids = { owner: pk for owner, pk in addresses.filter(object_id__in=entities.filter( billing_address__isnull=True, shipping_address__isnull=True, ).values_list('pk', flat=True)).order_by('-pk').values_list( 'object_id', 'pk') } # merge ids address_ids.update(billing_shipping_ids) return addresses.filter( pk__in=address_ids.values()) # TODO: select_related('geoaddress') ??
def _get_filtered_related_entities(self, entity, user): related_entities = EntityCredentials.filter(user, self._get_related_instances(entity, user)) report = self._report_field.sub_report if report.filter is not None: related_entities = report.filter.filter(related_entities) return related_entities
def extract_value(self, line, user): extracted = [] err_msg = [] for search in line[self._column_index].split(self._separator): search = search.strip() if not search: continue # TODO: it seems this line does not work ; but it would be cool to make less queries... #... EntityCredentials.filter(user, CremeEntity.objects.filter(header_filter_search_field__icontains=search)) has_perm = user.has_perm_to_link unlinkable_found = False for model in self._models: # TODO: filter with link credentials too (because here we limit _before_ filtering unlinkable...) instances = EntityCredentials.filter(user, model.objects.filter(header_filter_search_field__icontains=search), )[:MAX_RELATIONSHIPS + 1] linkable_extracted = [e for e in instances if has_perm(e)] if linkable_extracted: length = len(linkable_extracted) if length > MAX_RELATIONSHIPS: err_msg.append(_('Too many «{models}» were found for the search «{search}»').format( models=model._meta.verbose_name_plural, search=search, ) ) else: if length > 1: err_msg.append(_('Several «{models}» were found for the search «{search}»').format( models=model._meta.verbose_name_plural, search=search, ) ) extracted.extend(linkable_extracted) break if instances: unlinkable_found = True else: if self._create: extracted.append(Organisation.objects.create(user=user, name=search)) elif unlinkable_found: err_msg.append(_('No linkable entity found for the search «{}»').format(search)) else: err_msg.append(_('The subject «{}» is unfoundable').format(search)) return extracted, err_msg
def _get_value_no_subreport(self, entity, user, scope): """Used as _get_value() method by subclasses which manage sub-reports (no sub-report case). """ qs = self._get_related_instances(entity, user) extract = self._related_model_value_extractor if issubclass(qs.model, CremeEntity): qs = EntityCredentials.filter(user, qs) return ', '.join(str(extract(instance)) for instance in qs)
def get(context, entity): return EntityCredentials.filter( context['user'], Opportunity.objects.annotate( relations_w_person=FilteredRelation( 'relations', condition=Q(relations__object_entity=entity.id), ), ).filter( is_deleted=False, relations_w_person__type=opp_constants.REL_SUB_TARGETS, ))
def get(context, entity): now = context['today'] user = context['user'] if isinstance(entity, Organisation): past = Activity.objects.past_linked_to_organisation future = Activity.objects.future_linked_to_organisation else: past = Activity.objects.past_linked future = Activity.objects.future_linked return { 'last': EntityCredentials.filter(user, past(entity, now)).first(), 'next': EntityCredentials.filter(user, future(entity, now)).first(), # NB: we avoid a templatetag from activities, because dynamic # {% load %} is not possible. 'NARROW': NARROW, }
def get(context, entity): return EntityCredentials.filter( context['user'], Act.objects.annotate(relations_w_person=FilteredRelation( 'relations', condition=Q(relations__object_entity=entity.id), ), ).filter( is_deleted=False, relations_w_person__type=commercial_constants. REL_OBJ_COMPLETE_GOAL, ))
def _get_filtered_related_entities(self, entity: CremeEntity, user) -> 'QuerySet': related_entities = EntityCredentials.filter( user=user, queryset=self._get_related_instances(entity, user), ) report = self._report_field.sub_report if report.filter is not None: related_entities = report.filter.filter(related_entities) return related_entities
def _search_n_create_contacts(self, user, civility, first_name, last_name): extracted = () err_msg = None query_dict = {'last_name__iexact': last_name} if first_name: query_dict['first_name__iexact'] = first_name # TODO: filter with link credentials too (because here we limit # _before_ filtering not linkable...) contacts = EntityCredentials.filter( user, Contact.objects.filter(**query_dict), )[:MAX_RELATIONSHIPS + 1] if contacts: has_perm = user.has_perm_to_link contacts = [c for c in contacts if has_perm(c)] if contacts: length = len(contacts) if length > MAX_RELATIONSHIPS: err_msg = _( 'Too many contacts were found for the search «{}»' ).format(self._searched_contact(first_name, last_name)) else: if length > 1: err_msg = _( 'Several contacts were found for the search «{}»' ).format(self._searched_contact(first_name, last_name)) extracted = contacts else: err_msg = _( 'No linkable contact found for the search «{}»').format( self._searched_contact(first_name, last_name)) elif self._create: extracted = [ Contact.objects.create( user=user, first_name=first_name, last_name=last_name, civility=(Civility.objects.filter( Q(title=civility) | Q(shortcut=civility)).first() if civility else None), ), ] else: err_msg = _('The participant «{}» is unfoundable').format( self._searched_contact(first_name, last_name)) return extracted, (err_msg, ) if err_msg else ()
def get(context, entity): now = context['today'] user = context['user'] if isinstance(entity, Organisation): # past = Activity.get_past_linked_for_orga past = Activity.objects.past_linked_to_organisation # future = Activity.get_future_linked_for_orga future = Activity.objects.future_linked_to_organisation else: # past = Activity.get_past_linked past = Activity.objects.past_linked # future = Activity.get_future_linked future = Activity.objects.future_linked return { 'last': EntityCredentials.filter(user, past(entity, now)).first(), 'next': EntityCredentials.filter(user, future(entity, now)).first(), }
def persons_contact_first_employer(contact, user): info = {} managed_ids = [] employing_ids = [] for rtype_id, orga_id in contact.relations.filter( type__in=(REL_SUB_EMPLOYED_BY, REL_SUB_MANAGES), ).values_list('type', 'object_entity'): if rtype_id == REL_SUB_MANAGES: managed_ids.append(orga_id) else: employing_ids.append(orga_id) if managed_ids: orga = EntityCredentials.filter( user, get_organisation_model().objects.filter(id__in=managed_ids, is_deleted=False), ).first() if orga: info['organisation'] = orga info['as_manager'] = True if not info and employing_ids: orga = EntityCredentials.filter( user, get_organisation_model().objects.filter(id__in=employing_ids, is_deleted=False), ).first() if orga: info['organisation'] = orga info['as_manager'] = False return info
def fetch(self, user, extra_q=None, order='ASC'): assert order == 'ASC' or order == 'DESC' report = self.linked_report entities = EntityCredentials.filter( user=user, queryset=report.ct.model_class().objects.filter(is_deleted=False), ) if report.filter is not None: entities = report.filter.filter(entities) if extra_q is not None: entities = entities.filter(extra_q) return self.hand.fetch(entities=entities, order=order, user=user)
def sum_totals_no_vat(model, entity, user, **kwargs) -> Decimal: all_totals = dict( EntityCredentials.filter( user, model.objects.filter(relations__type=REL_SUB_BILL_RECEIVED, relations__object_entity=entity.id, is_deleted=False, total_no_vat__isnull=False, **kwargs).values_list('id', 'total_no_vat'))) managed_ids = Relation.objects.filter( subject_entity__in=[ o.id for o in Organisation.objects.filter_managed_by_creme() ], type=REL_OBJ_BILL_ISSUED, object_entity_id__in=all_totals.keys(), ).values_list('object_entity', flat=True) return sum(all_totals[b_id] for b_id in managed_ids)
def fetch(self, user, extra_q: Optional[models.Q] = None, order: str = 'ASC') -> Tuple[List[str], list]: assert order == 'ASC' or order == 'DESC' report = self.linked_report entities = EntityCredentials.filter( user=user, queryset=report.ct.model_class().objects.filter(is_deleted=False), ) if report.filter is not None: entities = report.filter.filter(entities) return self.hand.fetch(entities=entities, order=order, user=user, extra_q=extra_q)
def get_unordered_queryset_n_count(self): # Cannot use this because it use get_ordering() too early # qs = super().get_queryset().filter(is_deleted=False) qs = self.model._default_manager.filter(is_deleted=False) state = self.state filtered = False use_distinct = False # ---- entity_filter = self.entity_filter if entity_filter: filtered = True qs = entity_filter.filter(qs) # ---- extra_q = self.extra_q['total'] if extra_q: try: qs = qs.filter(extra_q) except Exception as e: logger.exception( 'Error when building the search queryset: invalid q_filter (%s).', e) else: filtered = True use_distinct = True state.extra_q = extra_q # TODO: only if valid ? no write in state HERE ? # ---- # lv_state_q = state.get_q_with_research(self.model, self.cells) # try: # qs = qs.filter(lv_state_q) # except Exception as e: # logger.exception('Error when building the search queryset with Q=%s (%s).', lv_state_q, e) # else: # if lv_state_q: # filtered = True # use_distinct = True search_q = self.search_form.search_q if search_q: try: qs = qs.filter(search_q) except Exception as e: logger.exception( 'Error when building the search queryset with Q=%s (%s).', search_q, e) else: filtered = True use_distinct = True # ---- user = self.request.user qs = EntityCredentials.filter(user, qs) if use_distinct: qs = qs.distinct() # ---- # If the query does not use the real entities' specific fields to filter, # we perform a query on CremeEntity & so we avoid a JOIN. if filtered: count = qs.count() else: model = self.model try: count = EntityCredentials.filter_entities( user, CremeEntity.objects.filter( is_deleted=False, entity_type=ContentType.objects.get_for_model(model), ), as_model=model, ).count() except EntityCredentials.FilteringError as e: logger.debug( '%s.get_unordered_queryset_n_count() : fast count is not possible (%s)', type(self).__name__, e, ) count = qs.count() return qs, count
def _execute(self, job): # NB 1: we try to delete the remaining entities (which could not be deleted # because of relationships) when there are errors, while the previous # iteration managed to remove some entities. # It will not work with cyclic references (but it is certainly very unusual). # NB 2: we do not use delete() method of queryset in order to send signals. user = job.user cmd_qs = TrashCleaningCommand.objects.filter(job=job) ctype_ids_qs = CremeEntity.objects.filter(is_deleted=True) \ .values_list('entity_type', flat=True) try: # NB: currently only supported by PostGreSQL ctype_ids = [ *ctype_ids_qs.order_by('entity_type_id').distinct( 'entity_type_id') ] except NotSupportedError: ctype_ids = {*ctype_ids_qs} entity_classes = [ ct.model_class() for ct in map(ContentType.objects.get_for_id, ctype_ids) ] while True: errors = False progress = False def create_error(entity, msg): nonlocal errors errors = True EntityJobResult.objects.update_or_create( job=job, entity=entity, defaults={'messages': [msg]}, ) # NB: 'SELECT FOR UPDATE' in a query using an 'OUTER JOIN' # and nullable ids will fail with postgresql (both 9.6 & 10.x). # TODO: This bug may be fixed in django > 2.2 # (see https://code.djangoproject.com/ticket/28010) # for entity_class in entity_classes: # paginator = FlowPaginator( # queryset=EntityCredentials.filter( # user, # entity_class.objects.filter(is_deleted=True), # EntityCredentials.DELETE, # ).order_by('id').select_for_update(), # key='id', # per_page=256, # ) # # with atomic(): # for entities_page in paginator.pages(): # for entity in entities_page.object_list: # entity = entity.get_real_entity() for entity_class in entity_classes: paginator = FlowPaginator( queryset=EntityCredentials.filter( user, entity_class.objects.filter(is_deleted=True), EntityCredentials.DELETE, ).order_by('id'), # .select_for_update() key='id', per_page=256, ) for entities_page in paginator.pages(): with atomic(): # NB (#60): Move 'SELECT FOR UPDATE' here for now (see above). for entity in entity_class.objects.filter( pk__in=entities_page.object_list ).select_for_update(): try: entity.delete() except ProtectedError: create_error( entity, _('Can not be deleted because of its dependencies.' ), ) except Exception as e: logger.exception( 'Error when trying to empty the trash') create_error( entity, _('Deletion caused an unexpected error [{}].' ).format(e), ) else: progress = True cmd_qs.update( deleted_count=F('deleted_count') + 1) if not errors or not progress: break
def _get_organisations(self, orga_name): return EntityCredentials.filter( self.user, Organisation.objects.filter(name=orga_name))