Пример #1
0
class FieldsConfigsBrick(PaginatedBrick):
    id_ = PaginatedBrick.generate_id('creme_config', 'fields_configs')
    dependencies = (FieldsConfig, )
    page_size = _PAGE_SIZE
    verbose_name = 'Fields configuration'
    template_name = 'creme_config/bricks/fields-configs.html'
    permission = None  # NB: used by the view creme_core.views.bricks.reload_basic()
    configurable = False

    def detailview_display(self, context):
        # TODO: exclude CTs that user cannot see ? (should probably done everywhere in creme_config...)
        fconfigs = [*FieldsConfig.objects.all()]
        sort_key = collator.sort_key
        fconfigs.sort(key=lambda fconf: sort_key(str(fconf.content_type)))

        used_models = {fconf.content_type.model_class() for fconf in fconfigs}
        btc = self.get_template_context(
            context,
            fconfigs,
            display_add_button=any(
                model not in used_models for model in filter(
                    FieldsConfig.is_model_valid, apps.get_models())),
        )

        for fconf in btc['page'].object_list:
            vnames = [str(f.verbose_name) for f in fconf.hidden_fields]
            vnames.sort(key=sort_key)

            fconf.fields_vnames = vnames

        return self._render(btc)
Пример #2
0
class CreditNotesBrick(PaginatedBrick):
    id_ = PaginatedBrick.generate_id('billing', 'credit_notes')
    dependencies = (Relation, CreditNote)
    relation_type_deps = (constants.REL_OBJ_CREDIT_NOTE_APPLIED, )
    verbose_name = _(u'Related Credit Notes')
    template_name = 'billing/bricks/credit-notes.html'
    target_ctypes = (
        Invoice,
        SalesOrder,
        Quote,
    )

    def detailview_display(self, context):
        billing_document = context['object']
        is_hidden = context['fields_configs'].get_4_model(
            CreditNote).is_fieldname_hidden

        return self._render(
            self.get_template_context(
                context,
                billing_document.get_credit_notes(),
                rtype_id=self.relation_type_deps[0],
                add_title=_(u'Create a credit note'),
                hidden_fields={
                    fname
                    for fname in ('issuing_date', 'expiration_date', 'comment')
                    if is_hidden(fname)
                },
            ))
Пример #3
0
class SearchConfigBrick(PaginatedBrick):
    id_ = PaginatedBrick.generate_id('creme_config', 'searchconfig')
    dependencies = (SearchConfigItem, )
    verbose_name = 'Search configuration'
    template_name = 'creme_config/bricks/search-config.html'
    order_by = 'content_type'
    # TODO _ConfigAdminBlock => Mixin
    page_size = _PAGE_SIZE * 2  # Only one brick
    permission = None  # NB: used by the view creme_core.views.blocks.reload_basic()
    configurable = False

    def detailview_display(self, context):
        # NB: we wrap the ContentType instances instead of store extra data in
        #     them because teh instances are stored in a global cache, so we do
        #     not want to mutate them.
        class _ContentTypeWrapper:  # TODO: move from here ?
            __slots__ = ('ctype', 'sc_items')

            def __init__(self, ctype):
                self.ctype = ctype
                self.sc_items = ()

        ctypes = [
            _ContentTypeWrapper(ctype)
            for ctype in creme_entity_content_types()
        ]
        sort_key = collator.sort_key
        ctypes.sort(key=lambda ctw: sort_key(str(ctw.ctype)))

        btc = self.get_template_context(
            context,
            ctypes,
            # NB: '+ 2' is for default config + super-users config.
            max_conf_count=UserRole.objects.count() + 2,
        )

        ctypes_wrappers = btc['page'].object_list

        sci_map = defaultdict(list)
        for sci in SearchConfigItem.objects \
                                   .filter(content_type__in=[ctw.ctype for ctw in ctypes_wrappers])\
                                   .select_related('role'):
            sci_map[sci.content_type_id].append(sci)

        superusers_label = gettext('Superuser')

        for ctw in ctypes_wrappers:
            ctype = ctw.ctype
            ctw.sc_items = sc_items = sci_map.get(ctype.id) or []
            sc_items.sort(key=lambda sci: sort_key(
                str(sci.role) if sci.role else superusers_label
                if sci.superuser else ''))

            if not sc_items or not sc_items[
                    0].is_default:  # No default config -> we build it
                SearchConfigItem.objects.create(content_type=ctype)

        return self._render(btc)
Пример #4
0
class CallersBrick(PaginatedBrick):
    id_ = PaginatedBrick.generate_id('cti', 'callers')
    verbose_name = _('Potential callers')
    template_name = 'cti/bricks/callers.html'
    configurable = False
    page_size = 128

    caller_models: Sequence[Type[CremeEntity]] = (
        Contact,
        Organisation,
    )

    def detailview_display(self, context):
        # Ensure that it will crash if we try to load it from a classic load view
        number = context['number']

        user = context['user']
        filter_viewable = EntityCredentials.filter
        fconfigs = FieldsConfig.objects.get_for_models(self.caller_models)
        all_fields_hidden = True
        callers = []

        for model in self.caller_models:
            is_hidden = fconfigs[model].is_field_hidden
            queries = [
                Q(**{field.name: number}) for field in model._meta.fields
                if isinstance(field, PhoneField) and not is_hidden(field)
            ]

            if queries:
                all_fields_hidden = False
                callers.extend(
                    filter_viewable(
                        user,
                        model.objects.exclude(is_deleted=True).filter(
                            reduce(or_, queries)),
                    ))

        if all_fields_hidden:
            raise ConflictError(
                gettext(
                    'All phone fields are hidden ; please contact your administrator.'
                ))

        can_create = user.has_perm_to_create

        return self._render(
            self.get_template_context(
                context,
                objects=callers,
                can_create_contact=can_create(Contact),
                contact_creation_label=Contact.creation_label,
                can_create_orga=can_create(Organisation),
                orga_creation_label=Organisation.creation_label,
                can_create_activity=can_create(Activity),
            ))
Пример #5
0
class RelatedOpportunitiesBrick(PaginatedBrick):
    id_ = PaginatedBrick.generate_id('commercial', 'opportunities')
    dependencies = (Relation, Opportunity)
    relation_type_deps = (REL_OBJ_COMPLETE_GOAL, )
    verbose_name = _('Opportunities related to a Commercial Action')
    template_name = 'commercial/bricks/opportunities.html'
    target_ctypes = (Act, )

    def detailview_display(self, context):
        act = context['object']

        return self._render(
            self.get_template_context(
                context,
                act.get_related_opportunities(),
                predicate_id=REL_OBJ_COMPLETE_GOAL,
            ))
Пример #6
0
class BrickDetailviewLocationsBrick(PaginatedBrick):
    id_ = PaginatedBrick.generate_id('creme_config', 'blocks_dv_locations')
    dependencies = (BrickDetailviewLocation, )
    page_size = _PAGE_SIZE - 1  # '-1' because there is always the line for default config on each page
    verbose_name = 'Blocks locations on detailviews'
    template_name = 'creme_config/bricks/bricklocations-detailviews.html'
    permission = None  # NB: used by the view creme_core.views.blocks.reload_basic
    configurable = False

    brick_registry = brick_registry

    def detailview_display(self, context):
        # NB: we wrap the ContentType instances instead of store extra data in
        #     them because the instances are stored in a global cache, so we do
        #     not want to mutate them.
        class _ContentTypeWrapper:  # TODO: move from here ?
            __slots__ = ('ctype', 'locations_info', 'default_count')

            def __init__(self, ctype):
                self.ctype = ctype
                self.default_count = 0
                self.locations_info = (
                )  # List of tuples (role_arg, role_label, block_count)
                # with role_arg == role.id or 'superuser'

        # TODO: factorise with SearchConfigBlock ?
        # TODO: factorise with CustomBlockConfigItemCreateForm , add a method in block_registry ?
        get_ct = ContentType.objects.get_for_model
        is_invalid = self.brick_registry.is_model_invalid
        ctypes = [
            _ContentTypeWrapper(get_ct(model))
            for model in creme_registry.iter_entity_models()
            if not is_invalid(model)
        ]

        sort_key = collator.sort_key
        ctypes.sort(key=lambda ctw: sort_key(str(ctw.ctype)))

        btc = self.get_template_context(
            context,
            ctypes,
            max_conf_count=UserRole.objects.count() +
            1,  # NB: '+ 1' is for super-users config.
        )

        ctypes_wrappers = btc['page'].object_list

        brick_counts = defaultdict(
            lambda: defaultdict(int)
        )  # brick_counts[content_type.id][(role_id, superuser)] -> count
        role_ids = set()

        for bdl in BrickDetailviewLocation.objects \
                                          .filter(content_type__in=[ctw.ctype for ctw in ctypes_wrappers])\
                                          .exclude(zone=BrickDetailviewLocation.HAT):
            if bdl.brick_id:  # Do not count the 'place-holder' (empty block IDs which mean "no-block for this zone")
                role_id = bdl.role_id
                brick_counts[bdl.content_type_id][(role_id,
                                                   bdl.superuser)] += 1
                role_ids.add(role_id)

        role_names = dict(
            UserRole.objects.filter(id__in=role_ids).values_list('id', 'name'))
        superusers_label = gettext('Superuser')  # TODO: cached_lazy_gettext

        for ctw in ctypes_wrappers:
            count_per_role = brick_counts[ctw.ctype.id]
            ctw.default_count = count_per_role.pop((None, False), 0)

            ctw.locations_info = locations_info = []
            for (role_id, superuser), block_count in count_per_role.items():
                if superuser:
                    role_arg = 'superuser'
                    role_label = superusers_label
                else:
                    role_arg = role_id
                    role_label = role_names[role_id]

                locations_info.append((role_arg, role_label, block_count))

            locations_info.sort(
                key=lambda t: sort_key(t[1]))  # Sort by role label

        btc['default_count'] = BrickDetailviewLocation.objects.filter(
            content_type=None,
            role=None,
            superuser=False,
        ).count()

        return self._render(btc)
Пример #7
0
    class NeglectedOrganisationsBrick(PaginatedBrick):
        id_ = PaginatedBrick.generate_id('persons', 'neglected_orgas')
        verbose_name = _('Neglected organisations')
        description = _(
            'Displays customers/prospects organisations (for the Organisations managed by Creme) '
            'which have no Activity in the future. Expected Activities are related to:\n'
            '- The Organisations with a relationship «is subject of the activity» or '
            '«related to the activity»\n'
            '- The managers & employees with a relationship «participates to the activity» '
            '(plus the above ones)')
        dependencies = (Activity, )
        template_name = 'persons/bricks/neglected-organisations.html'

        _RTYPE_IDS_CUSTOMERS = (
            constants.REL_SUB_CUSTOMER_SUPPLIER,
            constants.REL_SUB_PROSPECT,
        )
        _RTYPE_IDS_ORGA_N_ACT = (
            activities_constants.REL_SUB_ACTIVITY_SUBJECT,
            activities_constants.REL_SUB_LINKED_2_ACTIVITY,
        )
        _RTYPE_IDS_EMPLOYEES = (
            constants.REL_SUB_MANAGES,
            constants.REL_SUB_EMPLOYED_BY,
        )
        _RTYPE_IDS_CONTACT_N_ACT = (
            activities_constants.REL_SUB_PART_2_ACTIVITY,
            activities_constants.REL_SUB_ACTIVITY_SUBJECT,
            activities_constants.REL_SUB_LINKED_2_ACTIVITY,
        )

        def _get_neglected(self, now):
            user_contacts = Contact.objects.filter(
                is_user__isnull=False, ).values_list('id', flat=True)
            future_activities = [
                *Activity.objects.filter(
                    start__gte=now,
                    relations__type=activities_constants.
                    REL_OBJ_PART_2_ACTIVITY,
                    relations__object_entity__in=user_contacts,
                ).values_list('id', flat=True),
            ]
            neglected_orgas_qs = Organisation.objects.filter(
                is_deleted=False,
                relations__type__in=self._RTYPE_IDS_CUSTOMERS,
                relations__object_entity__in=Organisation.objects.
                filter_managed_by_creme(),
            ).exclude(relations__type=constants.REL_SUB_INACTIVE).distinct()

            if not future_activities:
                # No need to retrieve it & transform into a list (good idea ??)
                return neglected_orgas_qs

            neglected_orgas = [
                *neglected_orgas_qs.exclude(
                    relations__object_entity__in=future_activities,
                    relations__type__in=self._RTYPE_IDS_ORGA_N_ACT,
                ),
            ]

            if neglected_orgas:
                linked_people_map = dict(
                    Relation.objects.filter(
                        type__in=self._RTYPE_IDS_EMPLOYEES,
                        object_entity__in=[o.id for o in neglected_orgas],
                    ).values_list('subject_entity_id', 'object_entity_id'), )
                activity_links = Relation.objects.filter(
                    type__in=self._RTYPE_IDS_CONTACT_N_ACT,
                    subject_entity__in=linked_people_map.keys(),
                    object_entity__in=future_activities,
                )

                # 'True' means 'neglected'
                neglected_map = {orga.id: True for orga in neglected_orgas}
                for rel in activity_links:
                    neglected_map[linked_people_map[
                        rel.subject_entity_id]] = False

                neglected_orgas = [
                    orga for orga in neglected_orgas if neglected_map[orga.id]
                ]

            return neglected_orgas

        def home_display(self, context):
            # We do not check the 'persons' permission, because it's only
            # statistics for people who cannot see Organisations.
            return self._render(
                self.get_template_context(
                    context,
                    self._get_neglected(context['today']),
                ))
Пример #8
0
class EntityFiltersBrick(PaginatedBrick):
    id_ = PaginatedBrick.generate_id('creme_config', 'entity_filters')
    verbose_name = 'All entity filters'
    dependencies = (EntityFilter, )
    page_size = _PAGE_SIZE
    template_name = 'creme_config/bricks/entity-filters.html'
    configurable = False

    def detailview_display(self, context):
        # NB: we wrap the ContentType instances instead of store extra data in
        #     them because the instances are stored in a global cache, so we do
        #     not want to mutate them.
        class _ContentTypeWrapper:
            __slots__ = ('ctype', 'all_users_filters', 'owned_filters')

            def __init__(this, ctype):
                this.ctype = ctype
                this.all_users_filters = ()
                this.owned_filters = ()

        # TODO: factorise with SearchConfigBrick ?
        get_ct = ContentType.objects.get_for_model
        user = context['user']
        has_perm = user.has_perm_to_access
        ctypes = [
            _ContentTypeWrapper(get_ct(model))
            for model in creme_registry.iter_entity_models()
            if has_perm(model._meta.app_label)
        ]

        sort_key = collator.sort_key
        ctypes.sort(key=lambda ctw: sort_key(str(ctw.ctype)))

        btc = self.get_template_context(context, ctypes)

        ctypes_wrappers = btc['page'].object_list

        # NB: efilters[content_type.id][user.id] -> List[EntityFilter]
        efilters = defaultdict(lambda: defaultdict(list))
        user_ids = set()

        for efilter in EntityFilter.objects.filter(
                filter_type=EF_USER,
                entity_type__in=[ctw.ctype for ctw in ctypes_wrappers],
        ):
            # TODO: templatetags instead ?
            efilter.edition_perm = efilter.can_edit(user)[0]
            efilter.deletion_perm = efilter.can_delete(user)[0]

            user_id = efilter.user_id
            efilters[efilter.entity_type_id][user_id].append(efilter)
            user_ids.add(user_id)

        users = get_user_model().objects.in_bulk(user_ids)

        def efilter_key(efilter):
            return sort_key(efilter.name)

        for ctw in ctypes_wrappers:
            ctype_efilters_per_users = efilters[ctw.ctype.id]

            all_users_filters = ctype_efilters_per_users.pop(None, None) or []
            all_users_filters.sort(key=efilter_key)

            ctw.all_users_filters = all_users_filters

            ctw.owned_filters = [(
                str(users[user_id]),
                sorted(user_efilters, key=efilter_key),
            ) for user_id, user_efilters in ctype_efilters_per_users.items()]

        return self._render(btc)