示例#1
0
    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)))
示例#2
0
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
示例#3
0
    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
            ]
示例#4
0
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))
示例#5
0
    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
示例#6
0
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') ??
示例#7
0
    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
示例#9
0
    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)
示例#10
0
 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,
             ))
示例#11
0
        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,
            }
示例#12
0
 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,
         ))
示例#13
0
    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
示例#14
0
    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 ()
示例#15
0
        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(),
            }
示例#16
0
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
示例#17
0
    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)
示例#18
0
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)
示例#19
0
    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)
示例#20
0
    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
示例#21
0
    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
示例#22
0
 def _get_organisations(self, orga_name):
     return EntityCredentials.filter(
         self.user, Organisation.objects.filter(name=orga_name))