示例#1
0
class AbstractTemplateBase(Base):
    ct = CTypeForeignKey(editable=False).set_tags(viewable=False)
    # TODO: avoid deletion of status
    status_id = PositiveIntegerField(editable=False).set_tags(viewable=False)

    creation_label = pgettext_lazy('billing', 'Create a template')
    save_label     = pgettext_lazy('billing', 'Save the template')

    generate_number_in_create = False

    _verbose_status_cache = None

    class Meta(Base.Meta):
        abstract = True
        verbose_name = pgettext_lazy('billing', 'Template')
        verbose_name_plural = pgettext_lazy('billing', 'Templates')

    def get_absolute_url(self):
        return reverse('billing__view_template', args=(self.id,))

    @staticmethod
    def get_clone_absolute_url():
        return ''

    def get_edit_absolute_url(self):
        return reverse('billing__edit_template', args=(self.id,))

    def get_delete_absolute_url(self):
        # Means that TemplateBase can not be deleted directly
        # (because it is closely linked to its RecurrentGenerator)
        return ''

    @staticmethod
    def get_lv_absolute_url():
        return reverse('billing__list_templates')

    def create_entity(self):
        "This method is used by the generation job"
        instance_class = self.ct.model_class()
        instance = instance_class()
        instance.build(self)

        # Common rules for the recurrent generation of a "base" object for billing app.
        # See base's child for specific rules
        instance.generate_number()
        # TODO: user configurable rules ???
        instance.expiration_date = instance.issuing_date + timedelta(days=30)

        instance.additional_info = self.additional_info
        instance.payment_terms = self.payment_terms

        instance.save()

        return instance
示例#2
0
class WaitingAction(CremeModel):
    action = CharField(
        _(u'Action'), max_length=100
    )  # Action (i.e: create, update...) # TODO: int instead ??
    # TODO: split into 2 CharFields 'fetcher' & 'input' ?
    # NB: - If default backend (subject="*"): fetcher_name.
    #     - If not  'fetcher_name - input_name'  (i.e: email - raw, email - infopath, sms - raw...).
    source = CharField(_(u'Source'), max_length=100)
    # raw_data = TextField(blank=True, null=True)  # Pickled data
    raw_data = BinaryField(blank=True, null=True)  # Pickled data
    ct = CTypeForeignKey(verbose_name=_(
        u'Type of resource'))  # Redundant, but faster bd recovery
    subject = CharField(_(u'Subject'), max_length=100)
    user = CremeUserForeignKey(verbose_name=_(u'Owner'),
                               blank=True,
                               null=True,
                               default=None)  # If sandbox per user

    class Meta:
        app_label = 'crudity'
        verbose_name = _(u'Waiting action')
        verbose_name_plural = _(u'Waiting actions')

    # def get_data(self):
    @property
    def data(self):
        # data = loads(self.raw_data.encode('utf-8'))
        data = loads(self.raw_data)

        # if isinstance(data, dict):
        #     data = {
        #         k: v.decode('utf8') if isinstance(v, str) else v
        #             for k, v in data.items()
        #     }

        return data

    # def set_data(self, data):
    @data.setter
    def data(self, data):
        self.raw_data = dumps(data)

    def can_validate_or_delete(self, user):
        """self.user not None means that sandbox is by user"""
        if self.user is not None and self.user != user and not user.is_superuser:
            return False, ugettext(
                u'You are not allowed to validate/delete the waiting action <{}>'
            ).format(self.id)

        return True, ugettext(u'OK')
示例#3
0
class SimpleBillingAlgo(Model):
    organisation = ForeignKey(settings.PERSONS_ORGANISATION_MODEL,
                              verbose_name=_('Organisation'),
                              on_delete=CASCADE)
    last_number = IntegerField()
    prefix = CharField(_('Invoice prefix'), max_length=400)
    ct = CTypeForeignKey()

    ALGO_NAME = 'SIMPLE_ALGO'  # TODO: prefix with app name

    class Meta:
        app_label = 'billing'
        unique_together = ("organisation", "last_number", "ct")

    def __str__(self):
        return 'SimpleBillingAlgo(organisation="{}", ct="{}")'.format(
            self.organisation, self.ct)
示例#4
0
class ConfigBillingAlgo(CremeModel):
    organisation = ForeignKey(settings.PERSONS_ORGANISATION_MODEL,
                              verbose_name=_('Organisation'),
                              on_delete=CASCADE)
    name_algo = CharField(_('Algo name'), max_length=400)
    ct = CTypeForeignKey()

    class Meta:
        app_label = 'billing'
        # TODO unique_together = ("organisation", "name_algo", "ct") ??

    def __str__(self):
        return 'ConfigBillingAlgo(organisation="{}", name_algo="{}", ct="{}")'.format(
            self.organisation,
            self.name_algo,
            self.ct,
        )
示例#5
0
文件: algo.py 项目: mrjmad/creme_crm
class ConfigBillingAlgo(CremeModel):
    organisation = models.ForeignKey(
        settings.PERSONS_ORGANISATION_MODEL,
        verbose_name=_('Organisation'),
        on_delete=models.CASCADE,
    )
    name_algo = models.CharField(_('Algo name'), max_length=400)
    ct = CTypeForeignKey()

    class Meta:
        app_label = 'billing'
        # TODO unique_together = ("organisation", "name_algo", "ct") ??

    def __str__(self):
        return (f'ConfigBillingAlgo('
                f'organisation="{self.organisation}", '
                f'name_algo="{self.name_algo}", '
                f'ct="{self.ct}"'
                f')')
示例#6
0
class Migration(migrations.Migration):
    dependencies = [
        ('crudity', '0001_initial'),
    ]

    operations = [
        migrations.RenameField(
            model_name='waitingaction',
            old_name='data',
            new_name='raw_data',
        ),
        migrations.AlterField(
            model_name='waitingaction',
            name='ct',
            field=CTypeForeignKey(on_delete=CASCADE,
                                  to='contenttypes.ContentType',
                                  verbose_name='Type of resource'),
        ),
    ]
示例#7
0
class CremeExchangeMapping(CremeModel):
    creme_entity_id = IntegerField(u'Creme entity pk', unique=True)
    creme_entity_ct = CTypeForeignKey(
        verbose_name=u'Creme entity ct'
    )  # For filtering when the entity was deleted
    exchange_entity_id = CharField(u'Exchange entity pk',
                                   max_length=64,
                                   unique=True)
    synced = BooleanField(u'Already synced on server', default=False)
    is_creme_modified = BooleanField(u'Modified by creme?', default=False)
    was_deleted = BooleanField(
        u'Was deleted by creme?', default=False
    )  # Seems redundant with is_deleted but isn't in case of real deletion
    user = ForeignKey(settings.AUTH_USER_MODEL,
                      verbose_name=u'Belongs to',
                      on_delete=CASCADE)
    creme_entity_repr = CharField(u'Verbose entity representation',
                                  max_length=200,
                                  null=True,
                                  blank=True,
                                  default=u"")  # IHM/User purposes

    def __str__(self):
        return u"<CremeExchangeMapping ce_id: <%s>, ex_id: <%s>, belongs to %s>" % (
            self.creme_entity_id, self.exchange_entity_id, self.user)

    class Meta:
        app_label = 'activesync'
        # verbose_name = u""
        # verbose_name_plural = u""

    def get_entity(self):
        entity = None
        if not self.creme_entity_id:
            return entity

        try:
            entity = CremeEntity.objects.get(
                pk=self.creme_entity_id).get_real_entity()
        except CremeEntity.DoesNotExist:
            pass

        return entity
示例#8
0
文件: algo.py 项目: mrjmad/creme_crm
class SimpleBillingAlgo(models.Model):
    organisation = models.ForeignKey(
        settings.PERSONS_ORGANISATION_MODEL,
        verbose_name=_('Organisation'),
        on_delete=models.CASCADE,
    )
    last_number = models.IntegerField()
    prefix = models.CharField(_('Invoice prefix'), max_length=400)
    ct = CTypeForeignKey()

    ALGO_NAME = 'SIMPLE_ALGO'  # TODO: prefix with app name

    class Meta:
        app_label = 'billing'
        unique_together = ('organisation', 'last_number', 'ct')

    def __str__(self):
        return (f'SimpleBillingAlgo('
                f'organisation="{self.organisation}", '
                f'ct="{self.ct}", '
                f'last_number={self.last_number}, '
                f'prefix="{self.prefix}"'
                f')')
class AbstractRecurrentGenerator(CremeEntity):
    name = models.CharField(_('Name of the generator'),
                            max_length=100,
                            blank=True)
    # description = models.TextField(_('Description'), blank=True)

    first_generation = models.DateTimeField(_('Date of the first generation'))
    last_generation = models.DateTimeField(
        _('Date of the last generation'),
        null=True,
        editable=False,
    )
    periodicity = DatePeriodField(_('Periodicity of the generation'))

    ct = CTypeForeignKey(verbose_name=_('Type of the recurrent resource'),
                         editable=False)
    template = models.ForeignKey(
        CremeEntity,
        verbose_name=_('Related model'),
        related_name='template_set',
        editable=False,
        on_delete=models.CASCADE,
    )

    is_working = models.BooleanField(_('Active ?'),
                                     editable=False,
                                     default=True)  # TODO: useful ?

    creation_label = _('Create a generator')
    save_label = _('Save the generator')

    class Meta:
        abstract = True
        manager_inheritance_from_future = True
        app_label = 'recurrents'
        verbose_name = _('Recurrent generator')
        verbose_name_plural = _('Recurrent generators')
        ordering = ('name', )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.__init_refreshing_cache()

    def __init_refreshing_cache(self):
        self._old_first_generation = self.first_generation
        self._old_periodicity = self.periodicity
        # TODO: is_working when it is used

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse('recurrents__view_generator', args=(self.id, ))

    @staticmethod
    def get_create_absolute_url():
        return reverse('recurrents__create_generator')

    def get_edit_absolute_url(self):
        return reverse('recurrents__edit_generator', args=(self.id, ))

    @staticmethod
    def get_lv_absolute_url():
        return reverse('recurrents__list_generators')

    def save(self, *args, **kwargs):
        from ..creme_jobs import recurrents_gendocs_type

        created = bool(not self.pk)
        super().save(*args, **kwargs)

        if created or self._old_first_generation != self.first_generation or \
           self._old_periodicity != self.periodicity:
            recurrents_gendocs_type.refresh_job()
            self.__init_refreshing_cache()
class AbstractTemplateBase(Base):
    ct = CTypeForeignKey(editable=False).set_tags(viewable=False)
    status_id = PositiveIntegerField(editable=False).set_tags(
        viewable=False)  # TODO: avoid deletion of status

    creation_label = pgettext_lazy('billing', 'Create a template')
    save_label = pgettext_lazy('billing', 'Save the template')

    _verbose_status_cache = None

    class Meta(Base.Meta):
        abstract = True
        verbose_name = pgettext_lazy('billing', 'Template')
        verbose_name_plural = pgettext_lazy('billing', 'Templates')

    def get_absolute_url(self):
        return reverse('billing__view_template', args=(self.id, ))

    @staticmethod
    def get_clone_absolute_url():
        return ''

    def get_edit_absolute_url(self):
        return reverse('billing__edit_template', args=(self.id, ))

    def get_delete_absolute_url(self):
        # Means that TemplateBase can not be deleted directly
        # (because it is closely linked to its RecurrentGenerator)
        return ''

    @staticmethod
    def get_lv_absolute_url():
        return reverse('billing__list_templates')

    # def get_verbose_status(self):
    #     warnings.warn('models.AbstractTemplateBase.get_verbose_status() is deprecated ; '
    #                   'use function_fields.TemplateBaseVerboseStatusField instead.',
    #                   DeprecationWarning
    #                  )
    #
    #     vstatus = self._verbose_status_cache
    #
    #     if vstatus is None or vstatus.id != self.status_id:
    #         status_model = self.ct.model_class()._meta.get_field('status').remote_field.model
    #
    #         try:
    #             vstatus = status_model.objects.get(id=self.status_id)
    #         except status_model.DoesNotExist as e:
    #             logger.warning('Invalid status in TemplateBase(id=%s) [%s]', self.id, e)
    #             vstatus = status_model(id=self.status_id, name='')
    #
    #         self._verbose_status_cache = vstatus
    #
    #     return vstatus.name

    # @property
    # def verbose_status(self):
    #     warnings.warn('AbstractTemplateBase.verbose_status is deprecated '
    #                   '(see get_verbose_status() warning).',
    #                   DeprecationWarning
    #                  )
    #
    #     return self.get_verbose_status()

    def create_entity(self):
        "This method is used by the generation job"
        instance_class = self.ct.model_class()
        instance = instance_class()
        instance.build(self)

        # Common rules for the recurrent generation of a "base" object for billing app.
        # See base's child for specific rules
        instance.generate_number()
        instance.expiration_date = instance.issuing_date + timedelta(
            days=30)  # TODO: user configurable rules ???

        instance.additional_info = self.additional_info
        instance.payment_terms = self.payment_terms

        instance.save()

        return instance
示例#11
0
class ActObjectivePatternComponent(CremeModel):
    pattern = ForeignKey(ActObjectivePattern,
                         related_name='components',
                         editable=False,
                         on_delete=CASCADE)
    parent = ForeignKey('self',
                        null=True,
                        related_name='children',
                        editable=False,
                        on_delete=CASCADE)
    name = CharField(_('Name'), max_length=_NAME_LENGTH)
    ctype = CTypeForeignKey(verbose_name=_('Counted type'),
                            null=True,
                            blank=True,
                            editable=False)
    filter = ForeignKey(
        EntityFilter,
        verbose_name=_('Filter on counted entities'),
        null=True,
        blank=True,
        on_delete=PROTECT,
        editable=False,
    )
    success_rate = PositiveIntegerField(
        _('Success rate'))  # TODO: smallinteger ??

    _children_cache = None

    class Meta:
        app_label = "commercial"

    def __str__(self):
        return self.name

    # TODO: delete this code with new ForeignKey in Django1.3 ?? (maybe it causes more queries)
    # def delete(self):
    def delete(self, *args, **kwargs):
        def find_node(nodes, pk):
            for node in nodes:
                if node.id == pk:
                    return node

                found = find_node(node.get_children(), pk)

                if found: return found

        def flatten_node_ids(node, node_list):
            node_list.append(node.id)

            for child in node.get_children():
                flatten_node_ids(child, node_list)

        children2del = []

        # TODO: tree may inherit from a smart tree structure with right method like found()/flatten() etc...
        flatten_node_ids(
            find_node(self.pattern.get_components_tree(), self.id),
            children2del)
        ActObjectivePatternComponent.objects.filter(
            pk__in=children2del).delete()
        # NB super(ActObjectivePatternComponent, self).delete() is not called

    def get_children(self):
        children = self._children_cache

        if children is None:
            self._children_cache = children = list(self.children.all())

        return children

    def get_related_entity(self):  # NB: see delete_related_to_entity()
        return self.pattern

    def clone(self, pattern, parent=None):
        """Clone the entire hierarchy of the node wherever is it"""
        own_parent = None
        if self.parent and not parent:
            own_parent = self.parent.clone(pattern)

        me = ActObjectivePatternComponent.objects.create(
            pattern=pattern,
            parent=own_parent or parent,
            name=self.name,
            ctype_id=self.ctype_id,
            filter_id=self.filter_id,
            success_rate=self.success_rate,
        )

        for sub_aopc in self.children.all():
            sub_aopc.clone(pattern, me)
示例#12
0
class ActObjective(CremeModel):
    name = CharField(_('Name'), max_length=_NAME_LENGTH)
    act = ForeignKey(
        settings.COMMERCIAL_ACT_MODEL,
        related_name='objectives',
        editable=False,
        on_delete=CASCADE,
    )
    counter = PositiveIntegerField(_('Counter'), default=0, editable=False)
    counter_goal = PositiveIntegerField(_('Value to reach'), default=1)
    ctype = CTypeForeignKey(verbose_name=_('Counted type'),
                            null=True,
                            blank=True,
                            editable=False)
    filter = ForeignKey(
        EntityFilter,
        verbose_name=_('Filter on counted entities'),
        null=True,
        blank=True,
        on_delete=PROTECT,
        editable=False,
    )

    creation_label = _('Create an objective')
    save_label = _('Save the objective')

    _count_cache = None

    class Meta:
        app_label = 'commercial'
        verbose_name = _('Commercial Objective')
        verbose_name_plural = _('Commercial Objectives')
        # ordering = ('name',) TODO ?

    def __str__(self):
        return self.name

    def get_edit_absolute_url(self):
        return reverse('commercial__edit_objective', args=(self.id, ))

    def get_related_entity(self):  # NB: for generic views
        return self.act

    def get_count(self):  # TODO: property ??
        count = self._count_cache

        if count is None:
            ctype = self.ctype

            if ctype:
                if self.filter:
                    qs = ctype.model_class().objects.filter(
                        is_deleted=False,  # TODO: test deleted=False
                        relations__type=REL_SUB_COMPLETE_GOAL,
                        relations__object_entity=self.act_id,
                    )
                    count = self.filter.filter(qs).count()
                else:
                    count = Relation.objects.filter(type=REL_SUB_COMPLETE_GOAL,
                                                    object_entity=self.act_id,
                                                    subject_entity__is_deleted=False,
                                                    subject_entity__entity_type=ctype,
                                                   ) \
                                            .count()
            else:
                count = self.counter

            self._count_cache = count

        return count

    @property
    def reached(self):
        return self.get_count() >= self.counter_goal
示例#13
0
class UserSynchronizationHistory(CremeModel):
    user = ForeignKey(settings.AUTH_USER_MODEL,
                      verbose_name=_(u'User'),
                      on_delete=CASCADE)
    entity_repr = CharField(
        u'Entity', max_length=200, default=None, blank=True, null=True
    )  # Saving the representation of the entity in case it was deleted
    entity_pk = IntegerField(u'Entity pk', blank=True,
                             null=True)  # Saving the pk of the entity
    entity_ct = CTypeForeignKey(verbose_name=_(u'What'), null=True, blank=True)
    created = CreationDateTimeField(_(u'Creation date'), default=now)
    entity_changes = TextField(_(u'Entity changes'), default=_empty_dump)
    type = IntegerField(
        _(u'Type'), choices=USER_HISTORY_TYPE)  # TODO: SmallPositiveInteger ?
    where = IntegerField(
        _(u'Where'),
        choices=USER_HISTORY_WHERE)  # TODO: SmallPositiveInteger ?

    _entity = None

    class Meta:
        app_label = 'activesync'
        verbose_name = u'History'
        verbose_name_plural = u'History'

    def __str__(self):
        return u"<UserSynchronizationHistory user=%s, entity_repr=%s>" % (
            self.user, self.entity_repr)

    def _get_entity(self):
        if self.entity_pk is None:
            return

        _entity = self._entity

        if _entity is not None:
            return _entity  #TODO: refactor (remove this 'return')

        try:
            _entity = self._entity = CremeEntity.objects.get(
                pk=self.entity_pk).get_real_entity()
        except CremeEntity.DoesNotExist:
            pass
        return _entity

    def _set_entity(self, entity):
        self.entity_pk = entity.pk
        self.entity_repr = str(entity)
        self.entity_ct = entity.entity_type


#        self._entity     = entity

    entity = property(_get_entity, _set_entity)
    del _get_entity, _set_entity

    @staticmethod
    def populate_entities(histories):
        #        entities_pks = histories.values_list('entity_pk', flat=True)
        entities_pks = [history.entity_pk for history in histories]
        #        entities = CremeEntity.objects.filter(pk__in=entities_pks)
        entities = CremeEntity.objects.filter(
            pk__in=set(entities_pks)
        )  #Forcing the retrieve for MySQL <= v5.1.49 which "doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery"
        CremeEntity.populate_real_entities(entities)
        entities_map = {
            entity.pk: entity.get_real_entity()
            for entity in entities
        }

        for hist in histories:
            hist._entity = entities_map.get(hist.entity_pk)

    #TODO: Optimize db queries
    def _get_changes(self):
        changes = pickle.loads(self.entity_changes.encode('utf-8'))
        get_for_id = ContentType.objects.get_for_id

        for k, v in changes.items():
            if isinstance(v, dict):
                model_class = get_for_id(v['ct_id']).model_class()
                try:
                    changes[k] = model_class._default_manager.get(pk=v['pk'])
                except model_class.DoesNotExist:
                    changes[k] = _(u"This entity doesn't exist anymore")

        return changes

    def _set_changes(self, entity_changes):
        """ Set changes in self.entity_changes
            @params entity_changes has to be an iterable of (key, value) => [('a',1),...] or .items(), etc.
            if a value is None the key is deleted
            if a value is a django 'Model' it will be transformed into a dict {'ct_id': ContentType id, 'pk': Its pk}
        """
        changes = self.changes

        get_for_model = ContentType.objects.get_for_model
        django_model = Model

        for k_change, v_change in entity_changes:
            if v_change is not None:
                if isinstance(v_change, django_model):
                    v_change = {
                        'ct_id': get_for_model(v_change).id,
                        'pk': v_change.pk,
                    }
                changes[k_change] = v_change

            # elif changes.has_key(k_change):
            #     del changes[k_change]
            else:
                changes.pop(k_change, None)

        self.entity_changes = pickle.dumps(changes)

    changes = property(_get_changes, _set_changes)
    del _get_changes, _set_changes

    @staticmethod
    def _add(user, entity, where, type, entity_changes=None):
        ush = UserSynchronizationHistory(user=user, where=where, type=type)

        if entity_changes is not None:
            ush.changes = entity_changes

        if isinstance(entity, CremeEntity):
            ush.entity = entity
        else:
            repr, ct = entity
            ush.entity_repr = str(repr)
            ush.entity_ct = ct

        ush.save()
        return ush

    @staticmethod
    def add_create(user, entity, where):
        return UserSynchronizationHistory._add(user, entity, where, CREATE)

    @staticmethod
    def add_update(user, entity, where, entity_changes):
        return UserSynchronizationHistory._add(user, entity, where, UPDATE,
                                               entity_changes)

    @staticmethod
    def add_delete(user, entity, where):
        return UserSynchronizationHistory._add(user, entity, where, DELETE)
class Migration(migrations.Migration):
    dependencies = [
        ('creme_core', '0059_v2_1__convert_old_filter_format'),
    ]

    operations = [
        migrations.AddField(
            model_name='entityfilter',
            name='filter_type',
            field=models.PositiveSmallIntegerField(
                choices=[
                    (0, 'Credentials filter (internal use)'),
                    (1, 'Regular filter (usable in list-view...'),
                ],
                default=1,
                editable=False,
            ),
        ),
        migrations.AddField(
            model_name='setcredentials',
            name='efilter',
            field=models.ForeignKey(editable=False,
                                    null=True,
                                    on_delete=deletion.PROTECT,
                                    to='creme_core.EntityFilter'),
        ),
        migrations.AlterField(
            model_name='setcredentials',
            name='ctype',
            field=CTypeForeignKey(
                blank=True,
                null=True,
                on_delete=deletion.CASCADE,
                to='contenttypes.ContentType',
                verbose_name='Apply to a specific type',
            ),
        ),
        migrations.AlterField(
            model_name='setcredentials',
            name='forbidden',
            field=models.BooleanField(
                choices=[
                    (False,
                     'The users are allowed to perform the selected actions'),
                    (True,
                     'The users are NOT allowed to perform the selected actions'
                     ),
                ],
                default=False,
                help_text=
                'Notice that actions which are forbidden & allowed at the same time are considered as forbidden when final permissions are computed.',
                verbose_name='Allow or forbid?',
            ),
        ),
        migrations.AlterField(
            model_name='setcredentials',
            name='set_type',
            field=models.PositiveIntegerField(
                choices=[(1, 'All entities'), (2, "User's own entities"),
                         (3, 'Filtered entities')],
                default=1,
                help_text=
                'The choice «Filtered entities» allows to configure credentials based on values of fields or relationships for example.',
                verbose_name='Type of entities set',
            ),
        ),
    ]