Ejemplo n.º 1
0
class ResourceCreateForm(CremeModelForm):
    contact = CreatorEntityField(
        label=_('Contact to be assigned to this task'),
        model=get_contact_model(),
    )

    class Meta(CremeModelForm.Meta):
        model = Resource

    def __init__(self, task, *args, **kwargs):
        super().__init__(*args, **kwargs)
        instance = self.instance
        instance.task = task

        other_resources = task.resources_set.all()

        if instance.pk:
            other_resources = other_resources.exclude(pk=instance.pk)

        contact_f = self.fields['contact']
        contact_f.q_filter = ~Q(pk__in=[
            *other_resources.values_list('linked_contact_id', flat=True)
        ], )

        # The creation view cannot create a Contact already related to Resource (& so, excluded).
        contact_f.force_creation = True  # TODO: in constructor ?

    def save(self, *args, **kwargs):
        self.instance.linked_contact = self.cleaned_data['contact']
        return super().save(*args, **kwargs)
Ejemplo n.º 2
0
class SendingCreateForm(CremeModelForm):
    template = CreatorEntityField(label=_('Message template'),
                                  model=get_messagetemplate_model())

    class Meta:
        model = Sending
        fields = ()

    def __init__(self, entity, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.campaign = entity

    def save(self, *args, **kwargs):
        instance = self.instance
        template = self.cleaned_data['template']

        instance.campaign = self.campaign
        instance.template = template
        instance.date = now()
        super().save()

        instance.content = (template.subject + ' : ' +
                            template.body) if template else ''
        instance.save()

        for phone in instance.campaign.all_phone_numbers():
            Message.objects.create(
                phone=phone,
                sending=instance,
                status=MESSAGE_STATUS_NOTSENT,
            )

        Message.send(instance)

        return instance
Ejemplo n.º 3
0
 def formfield(self, instance, user, **kwargs):
     return CreatorEntityField(
         label=self.verbose_name,
         model=get_organisation_model(),
         user=user,
         initial=first_managed_organisation() if not instance.pk else instance.source,
     )
Ejemplo n.º 4
0
class SendingCreateForm(CremeModelForm):
    sender       = EmailField(label=_('Sender address'))
    template     = CreatorEntityField(label=_('Email template'), model=get_emailtemplate_model(),
                                      credentials=EntityCredentials.VIEW,
                                     )
    sending_date = DateTimeField(label=_('Sending date'), required=False, widget=CalendarWidget,
                                 help_text=_('Required only of the sending is deferred.'))
    hour         = IntegerField(label=_('Sending hour'), required=False, min_value=0, max_value=23)
    minute       = IntegerField(label=_('Sending minute'), required=False, min_value=0, max_value=59)

    error_messages = {
        'forbidden': _('You are not allowed to modify the sender address, please contact your administrator.'),
    }

    blocks = CremeModelForm.blocks.new(
        ('sending_date', _('Sending date'), ['type', 'sending_date', 'hour', 'minute']),
    )

    class Meta:
        model   = EmailSending
        exclude = ()

    def __init__(self, entity, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.campaign = entity
        self.can_admin_emails = can_admin_emails = self.user.has_perm_to_admin("emails")

        sender_setting = SettingValue.objects.get_4_key(emailcampaign_sender)

        sender_field = self.fields['sender']
        self.can_edit_sender_value = can_edit_sender_value = not sender_setting.value and can_admin_emails
        if not can_edit_sender_value:
            sender_field.widget.attrs['readonly'] = True

        if not sender_setting.value:
            if not can_admin_emails:
                sender_field.initial = _('No sender email address has been configured, '
                                         'please contact your administrator.'
                                        )
        else:
            sender_field.help_text = _('Only an administrator can modify the sender address.')
            sender_field.initial = sender_setting.value

        self.sender_setting = sender_setting

    def clean_sender(self):
        sender_value = self.cleaned_data.get('sender')

        if not self.can_edit_sender_value and sender_value != self.sender_setting.value:
            raise ValidationError(self.error_messages['forbidden'], code='forbidden')

        return sender_value

    def clean(self):
        cleaned_data = super().clean()

        if cleaned_data['type'] == SENDING_TYPE_DEFERRED:
            sending_date = cleaned_data['sending_date']

            if sending_date is None:
                self.add_error('sending_date', _('Sending date required for a deferred sending'))
            else:
                get_data = cleaned_data.get
                sending_date = make_aware_dt(datetime.combine(sending_date,
                                                              time(hour=int(get_data('hour') or 0),
                                                                   minute=int(get_data('minute') or 0),
                                                                  ),
                                                             )
                                            )

                if sending_date < now():
                    self.add_error('sending_date', _('Sending date must be is the future'))
                else:
                    cleaned_data['sending_date'] = sending_date
        else:
            cleaned_data['sending_date'] = now()

        return cleaned_data

    def _get_variables(self, body):  # TODO: move in Emailtemplate ??
        return (varnode.filter_expression.var.var
                    for varnode in Template(body).nodelist.get_nodes_by_type(VariableNode)
               )

    def save(self):
        instance = self.instance
        cleaned_data = self.cleaned_data
        sender_setting = self.sender_setting

        instance.campaign = self.campaign

        template = cleaned_data['template']
        instance.subject   = template.subject
        instance.body      = template.body
        instance.body_html = template.body_html
        instance.signature = template.signature

        super().save()

        sender_address = cleaned_data['sender']
        if self.can_edit_sender_value:
            sender_setting.value = sender_address
            sender_setting.save()

        # M2M need a pk -> after save
        attachments = instance.attachments
        for attachment in template.attachments.all():
            attachments.add(attachment)

        varlist = [
            *self._get_variables(template.body),
            *self._get_variables(template.body_html),
        ]

        disable_history = HistoryLine.disable

        for address, recipient_entity in instance.campaign.all_recipients():
            mail = LightWeightEmail(sending=instance,
                                    sender=instance.sender,
                                    recipient=address,
                                    sending_date=instance.sending_date,
                                    recipient_entity=recipient_entity,
                                   )

            if recipient_entity:
                context = {}

                for varname in varlist:
                    val = getattr(recipient_entity, varname, None)
                    if val:
                        context[varname] = val  # TODO: str(val) ?

                if context:
                    mail.body = json_dump(context, separators=(',', ':'))

            disable_history(mail)
            mail.genid_n_save()

        return instance
Ejemplo n.º 5
0
class RelatedActivityEditForm(CremeEntityForm):
    resource      = CreatorEntityField(label=_('Allocated resource'), model=Resource)
    type_selector = ActivityTypeField(label=_('Type'))

    class Meta(CremeEntityForm.Meta):
        model = Activity
        exclude = (*CremeEntityForm.Meta.exclude,
                   'title', 'is_all_day', 'minutes', 'status', 'type', 'sub_type',
                  )

    def _get_task(self):
        try:
            return Relation.objects.get(subject_entity=self.instance.pk, type=REL_SUB_LINKED_2_PTASK) \
                           .object_entity \
                           .get_real_entity()
        except Relation.DoesNotExist as e:
            raise ConflictError('This Activity is not related to a project task.') from e

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        fields = self.fields
        fields['duration'].required = True
        fields['start'].required = True
        fields['end'].required = True

        self.old_participant = self.old_relation = None
        instance = self.instance
        pk = instance.pk

        task = self._get_task()

        resource_f = fields['resource']
        resource_f.q_filter = {'task_id': task.id}

        if pk:  # Edition
            fields['keep_participating'] = \
                BooleanField(label=_('If the contact changes, the old one '
                                     'keeps participating to the activities.'
                                    ),
                             required=False,
                            )

            get_relation = Relation.objects.get

            try:
                self.old_relation = get_relation(type=REL_SUB_PART_AS_RESOURCE,
                                                 object_entity=pk,
                                                )
            except Relation.DoesNotExist as e:
                raise ConflictError('This Activity is not related to a project task') from e

            self.old_participant = self.old_relation.subject_entity.get_real_entity()
            resource_f.initial = Resource.objects.get(task=task,
                                                      linked_contact=self.old_participant,
                                                     )

            fields['type_selector'].initial = (instance.type_id, instance.sub_type_id)

    def clean(self, *args, **kwargs):
        cdata = self.cleaned_data

        if not self._errors:
            collisions = check_activity_collisions(cdata['start'], cdata['end'],
                                                   [cdata['resource'].linked_contact],
                                                   busy=cdata['busy'],
                                                   exclude_activity_id=self.instance.pk,
                                                  )

            if collisions:
                raise ValidationError(collisions)

        return cdata

    def save(self, *args, **kwargs):
        instance = self.instance
        cdata = self.cleaned_data
        instance.type, instance.sub_type = cdata['type_selector']

        super().save(*args, **kwargs)

        participant = cdata['resource'].linked_contact
        old_participant = self.old_participant

        if old_participant != participant:  # Creation mode OR edition mode with resource change
            if old_participant:
                self.old_relation.delete()

                if not cdata.get('keep_participating'):
                    # NB: no delete() on queryset (with a filter()) in order to send signals
                    Relation.objects.get(subject_entity=old_participant.id,
                                         type=REL_SUB_PART_2_ACTIVITY,
                                         object_entity=instance.pk,
                                        ).delete()

            _link_contact_n_activity(participant, instance, self.user)

        return instance
Ejemplo n.º 6
0
class VcfImportForm(CremeModelForm):
    class Meta:
        model = Contact
        fields = ('user', 'civility', 'first_name', 'last_name', 'position')

    vcf_step = IntegerField(widget=HiddenInput)

    image_encoded = CharField(required=False, widget=HiddenInput)

    # Details
    phone = CharField(label=_('Phone number'), required=False)
    mobile = CharField(label=_('Mobile'), required=False)
    fax = CharField(label=_('Fax'), required=False)
    email = EmailField(label=_('Email address'), required=False)
    url_site = URLField(label=_('Web Site'), required=False)

    # Address
    homeaddr_name = CharField(label=_('Name'), required=False)
    homeaddr_address = CharField(label=_('Address'), required=False)
    homeaddr_city = CharField(label=_('City'), required=False)
    homeaddr_country = CharField(label=_('Country'), required=False)
    homeaddr_code = CharField(label=_('Zip code'), required=False)
    homeaddr_region = CharField(label=_('Region'), required=False)

    # Related Organisation
    create_or_attach_orga = BooleanField(
        label=_('Create or attach organisation'),
        required=False,
        initial=False,
    )
    organisation = CreatorEntityField(label=_('Organisation'),
                                      required=False,
                                      model=Organisation)
    relation = ModelChoiceField(
        label=_('Position in the organisation'),
        queryset=RelationType.objects.none(),
        initial=REL_SUB_EMPLOYED_BY,
        required=False,
        empty_label='',
        widget=DynamicSelect(attrs={'autocomplete': True}),
    )

    # TODO: Composite field
    update_work_name = BooleanField(
        label=_('Update name'),
        required=False,
        initial=False,
        help_text=_('Update organisation selected name'))
    update_work_phone = BooleanField(
        label=_('Update phone'),
        required=False,
        initial=False,
        help_text=_('Update organisation selected phone'))
    update_work_fax = BooleanField(
        label=_('Update fax'),
        required=False,
        initial=False,
        help_text=_('Update organisation selected fax'))
    update_work_email = BooleanField(
        label=_('Update email'),
        required=False,
        initial=False,
        help_text=_('Update organisation selected email'))
    update_work_url_site = BooleanField(
        label=_('Update web site'),
        required=False,
        initial=False,
        help_text=_('Update organisation selected web site'))
    update_work_address = BooleanField(
        label=_('Update address'),
        required=False,
        initial=False,
        help_text=_('Update organisation selected address'))

    # Organisation name & details
    work_name = CharField(label=_('Name'), required=False)
    work_phone = CharField(label=_('Phone number'), required=False)
    work_fax = CharField(label=_('Fax'), required=False)
    work_email = EmailField(label=_('Email address'), required=False)
    work_url_site = URLField(label=_('Web Site'), required=False)

    # Organisation address
    workaddr_name = CharField(label=_('Name'), required=False)
    workaddr_address = CharField(label=_('Address'), required=False)
    workaddr_city = CharField(label=_('City'), required=False)
    workaddr_country = CharField(label=_('Country'), required=False)
    workaddr_code = CharField(label=_('Zip code'), required=False)
    workaddr_region = CharField(label=_('Region'), required=False)

    error_messages = {
        'required4orga': _('Required, if you want to create organisation'),
        'no_orga_creation': _('Create organisation not checked'),
        'orga_not_selected': _('Organisation not selected'),
        'required2update': _('Required, if you want to update organisation'),
    }

    # Names of the fields corresponding to the Contact's details.
    contact_details = ['phone', 'mobile', 'fax', 'email', 'url_site']

    # Names of the fields corresponding to the related Organisation (but not its Address).
    orga_fields = ['name', 'phone', 'email', 'fax', 'url_site']

    # Correspondence between VCF field types & form-field names.
    phone_dict = {
        'HOME': 'phone',
        'CELL': 'mobile',
        'FAX': 'fax',
        'WORK': 'work_phone',
    }
    email_dict = {
        'HOME': 'email',
        'INTERNET': 'email',
        'WORK': 'work_email',
    }
    url_dict = {
        'HOME': 'url_site',
        'INTERNET': 'url_site',
        'WORK': 'work_url_site',
    }

    # Form-field names prefix for address + correspondence with VCF field types.
    address_prefixes = {
        'HOME': HOME_ADDR_PREFIX,
        'WORK': WORK_ADDR_PREFIX,
    }
    # Mapping between form fields names (which use vcf lib names) & Address fields names.
    address_mapping = [
        ('name', 'name'),
        ('address', 'address'),
        ('city', 'city'),
        ('country', 'country'),
        ('code', 'zipcode'),
        ('region', 'department'),
    ]

    # blocks = CremeModelWithUserForm.blocks.new(
    blocks = CremeModelForm.blocks.new(
        ('details', _('Details'), contact_details),
        ('contact_address', _('Billing address'),
         [HOME_ADDR_PREFIX + n[0] for n in address_mapping]),
        ('organisation', _('Organisation'), [
            'create_or_attach_orga', 'organisation', 'relation',
            *chain.from_iterable(
                ('update_work_' + fn, 'work_' + fn) for fn in orga_fields)
        ]),
        ('organisation_address', _('Organisation billing address'), [
            'update_work_address', *(WORK_ADDR_PREFIX + n[0]
                                     for n in address_mapping)
        ]),
    )

    type_help_text = _('Read in VCF File without type : ')
    other_help_text = _('Read in VCF File : ')

    def __init__(self, vcf_data=None, *args, **kwargs):
        super().__init__(*args, **kwargs)
        fields = self.fields

        if vcf_data:
            self._init_contact_fields(vcf_data)
            self._init_orga_field(vcf_data)
            self._init_addresses_fields(vcf_data)

            if vcf_data.contents.get('photo'):
                fields['image_encoded'].initial = vcf_data.photo.value.replace(
                    '\n', '')

        # Beware: this queryset directly in the field declaration does not work on some systems in unit tests...
        #         (it seems that the problem it caused by the M2M - other fields work, but why ???)
        fields['relation'].queryset = RelationType.objects.filter(
            subject_ctypes=_get_ct(Contact),
            object_ctypes=_get_ct(Organisation),
        )

        self._hide_fields()

    def _hide_fields(self):
        fields = self.fields
        address_mapping = self.address_mapping
        fconfigs = FieldsConfig.get_4_models((Contact, Organisation, Address))

        # TODO: use shipping address if not hidden ?
        if fconfigs[Contact].is_fieldname_hidden('billing_address'):
            prefix = HOME_ADDR_PREFIX

            for form_fname, __ in address_mapping:
                del fields[prefix + form_fname]

        is_orga_field_hidden = fconfigs[Organisation].is_fieldname_hidden
        for fname in [*fields
                      ]:  # NB: Cannot mutate the OrderedDict during iteration.
            # NB: 5 == len('work_')
            if fname.startswith('work_') and is_orga_field_hidden(fname[5:]):
                del fields[fname]
                del fields['update_' + fname]

        if is_orga_field_hidden('billing_address'):
            prefix = WORK_ADDR_PREFIX

            for form_fname, __ in address_mapping:
                del fields[prefix + form_fname]

            del fields['update_work_address']

        is_addr_field_hidden = fconfigs[Address].is_fieldname_hidden
        addr_prefixes = self.address_prefixes.values()
        for form_fname, model_fname in address_mapping:
            if is_addr_field_hidden(model_fname):
                for prefix in addr_prefixes:
                    fields.pop(prefix + form_fname, None)

    def _init_contact_fields(self, vcf_data):
        contents = vcf_data.contents
        fields = self.fields
        contact_data = contents.get('n')

        if contact_data:
            value = vcf_data.n.value
            last_name = value.family
            fields['first_name'].initial = value.given
            fields['last_name'].initial = last_name
            fields['homeaddr_name'].initial = last_name
            prefix = value.prefix

            if prefix:
                # TODO: find in title too ?
                civ = Civility.objects.filter(
                    shortcut__icontains=prefix).first()
                if civ:
                    fields['civility'].initial = civ.id
                else:
                    fields[
                        'civility'].help_text = self.other_help_text + prefix
        else:
            first_name, sep, last_name = vcf_data.fn.value.partition(' ')
            fields['first_name'].initial = first_name
            fields['last_name'].initial = last_name
            fields['homeaddr_name'].initial = last_name

        if contents.get('title'):
            title = vcf_data.title.value
            position = Position.objects.filter(title__icontains=title).first()

            if position:
                fields['position'].initial = position.id
            else:
                fields['position'].help_text = self.other_help_text + title

        init_detail = self._init_detail_field
        init_detail(contents.get('tel'), self.phone_dict)
        init_detail(contents.get('email'), self.email_dict)
        init_detail(contents.get('url'), self.url_dict)

    def _init_detail_field(self, detail_data, field_dict):
        if detail_data:
            fields = self.fields

            for key in detail_data:
                param = key.params.get('TYPE')

                if param:
                    try:
                        fields[field_dict[param[0]]].initial = key.value
                    except KeyError:  # eg: invalid type, hidden field
                        pass
                else:
                    self._generate_help_text(field_dict['HOME'], key.value)

    def _init_orga_field(self, vcf_data):
        if vcf_data.contents.get('org'):
            fields = self.fields

            org_name = vcf_data.org.value[0]
            orga = Organisation.objects.filter(name=org_name).first()

            if orga:
                fields['organisation'].initial = orga.id
                fields['create_or_attach_orga'].initial = True

            fields['work_name'].initial = org_name
            fields['workaddr_name'].initial = org_name

    def _init_addresses_fields(self, vcf_data):
        fields = self.fields
        get_prefix = self.address_prefixes.get

        for adr in vcf_data.contents.get('adr', ()):
            param = adr.params.get('TYPE')
            value = adr.value

            if param:
                prefix = get_prefix(param[0])

                if prefix is None:
                    continue

                box = value.box
                fields[prefix + 'address'].initial = (
                    box + ' ' + value.street) if box else value.street
                fields[prefix + 'city'].initial = value.city
                fields[prefix + 'country'].initial = value.country
                fields[prefix + 'code'].initial = value.code
                fields[prefix + 'region'].initial = value.region
            else:
                self._generate_help_text(
                    'homeaddr_address',
                    ', '.join([
                        value.box,
                        value.street,
                        value.city,
                        value.region,
                        value.code,
                        value.country,
                    ]),
                )

    def _generate_help_text(self, field_name, value):
        field = self.fields[field_name]
        help_text = field.help_text

        if not help_text:
            field.help_text = self.type_help_text + value
        else:
            field.help_text = '{} | {}'.format(help_text, value)

    def _clean_orga_field(self, field_name):
        cleaned_data = self.cleaned_data
        cleaned = cleaned_data.get(field_name)

        if cleaned_data['create_or_attach_orga'] and not cleaned:
            raise ValidationError(
                self.error_messages['required4orga'],
                code='required4orga',
            )

        return cleaned

    clean_work_name = lambda self: self._clean_orga_field('work_name')
    clean_relation = lambda self: self._clean_orga_field('relation')

    def _clean_update_checkbox(self, checkbox_name):
        cleaned_data = self.cleaned_data
        checked = cleaned_data[checkbox_name]

        if checked:
            if not cleaned_data['create_or_attach_orga']:
                raise ValidationError(
                    self.error_messages['no_orga_creation'],
                    code='no_orga_creation',
                )
            elif not cleaned_data['organisation']:
                raise ValidationError(
                    self.error_messages['orga_not_selected'],
                    code='orga_not_selected',
                )

        return checked

    clean_update_work_name = lambda self: self._clean_update_checkbox(
        'update_work_name')
    clean_update_work_phone = lambda self: self._clean_update_checkbox(
        'update_work_phone')
    clean_update_work_email = lambda self: self._clean_update_checkbox(
        'update_work_email')
    clean_update_work_fax = lambda self: self._clean_update_checkbox(
        'update_work_fax')
    clean_update_work_url_site = lambda self: self._clean_update_checkbox(
        'update_work_url_site')
    clean_update_work_address = lambda self: self._clean_update_checkbox(
        'update_work_address')

    def clean_update_field(self, field_name):
        cleaned_data = self.cleaned_data
        value = cleaned_data[field_name]

        if not value and \
           all(cleaned_data[k] for k in ('create_or_attach_orga', 'organisation', 'update_' + field_name)):
            raise ValidationError(self.error_messages['required2update'],
                                  code='required2update')

        return value

    clean_work_phone = lambda self: self.clean_update_field('work_phone')
    clean_work_email = lambda self: self.clean_update_field('work_email')
    clean_work_fax = lambda self: self.clean_update_field('work_fax')
    clean_work_url_site = lambda self: self.clean_update_field('work_url_site')
    clean_work_address = lambda self: self.clean_update_field('work_address')

    def _create_contact(self, cleaned_data):
        get_data = cleaned_data.get

        return Contact.objects.create(
            user=cleaned_data['user'],
            civility=cleaned_data['civility'],
            first_name=cleaned_data['first_name'],
            last_name=cleaned_data['last_name'],
            position=get_data('position'),
            # NB: we do not use cleaned_data.get() in order to not overload
            #     default fields values
            **{
                fname: cleaned_data[fname]
                for fname in self.contact_details if fname in cleaned_data
            })

    def _create_address(self, cleaned_data, owner, data_prefix):
        # NB: we do not use cleaned_data.get() in order to not overload default fields values
        kwargs = {}
        for form_fname, model_fname in self.address_mapping:
            try:
                kwargs[model_fname] = cleaned_data[data_prefix + form_fname]
            except KeyError:
                pass

        address = Address(owner=owner, **kwargs)

        if address:
            address.save()
            return address

    def _create_image(self, contact):
        cleaned_data = self.cleaned_data
        image_encoded = cleaned_data['image_encoded']

        if image_encoded:
            img_name = secure_filename('{}_{}_{}'.format(
                contact.last_name, contact.first_name, contact.id))
            img_path = None

            if image_encoded.startswith(URL_START):
                tmp_img_path = None

                try:
                    if int(urlopen(image_encoded).info()
                           ['content-length']) <= settings.VCF_IMAGE_MAX_SIZE:
                        tmp_img_path = path.normpath(
                            path.join(IMG_UPLOAD_PATH, img_name))

                        urlretrieve(
                            image_encoded,
                            path.normpath(
                                path.join(settings.MEDIA_ROOT, tmp_img_path)))
                except:
                    logger.exception('Error with image')
                else:
                    img_path = tmp_img_path
            else:  # TODO: manage urls encoded in base64 ??
                try:
                    # TODO: factorise with activesync ??
                    img_data = base64.decodebytes(image_encoded.encode())
                    img_path = handle_uploaded_file(
                        ContentFile(img_data),
                        path=IMG_UPLOAD_PATH.split('/'),
                        name='{}.{}'.format(
                            img_name,
                            get_image_format(img_data),
                        ),
                    )
                except Exception:
                    logger.exception('VcfImportForm.save()')

            if img_path:
                return Document.objects.create(
                    user=cleaned_data['user'],
                    title=gettext('Image of {contact}').format(
                        contact=contact),
                    filedata=img_path,
                    linked_folder=Folder.objects.get(uuid=UUID_FOLDER_IMAGES),
                    description=gettext('Imported by VCFs'),
                )

    def _create_orga(self, contact):
        cleaned_data = self.cleaned_data

        if cleaned_data['create_or_attach_orga']:
            get_data = cleaned_data.get
            organisation = get_data('organisation')
            save_orga = False
            user = cleaned_data['user']
            addr_prefix = WORK_ADDR_PREFIX

            if organisation:
                # TODO: select_for_update() option in CreatorEntityField ?
                organisation = Organisation.objects.select_for_update().get(
                    id=organisation.id)

                for fname in self.orga_fields:
                    if get_data('update_work_' + fname):
                        setattr(organisation, fname, get_data('work_' + fname))

                if get_data('update_work_address'):
                    billing_address = organisation.billing_address

                    if billing_address is not None:
                        for form_fname, model_fname in self.address_mapping:
                            value = get_data(addr_prefix + form_fname)

                            if value:
                                setattr(billing_address, model_fname, value)

                        organisation.billing_address.save()
                    else:
                        organisation.billing_address = self._create_address(
                            cleaned_data,
                            owner=organisation,
                            data_prefix=addr_prefix,
                        )

                save_orga = True
            else:
                # NB: we do not use cleaned_data.get() in order to not overload default fields values
                orga_kwargs = {}
                for fname in self.orga_fields:
                    try:
                        orga_kwargs[fname] = cleaned_data['work_' + fname]
                    except KeyError:
                        pass

                organisation = Organisation.objects.create(user=user,
                                                           **orga_kwargs)

                orga_addr = self._create_address(
                    cleaned_data,
                    owner=organisation,
                    data_prefix=addr_prefix,
                )
                if orga_addr is not None:
                    organisation.billing_address = orga_addr
                    save_orga = True

            if save_orga:
                organisation.save()

            Relation.objects.create(
                user=user,
                subject_entity=contact,
                type=cleaned_data['relation'],
                object_entity=organisation,
            )

    @atomic
    def save(self, *args, **kwargs):
        cleaned_data = self.cleaned_data
        save_contact = False
        contact = self._create_contact(cleaned_data)

        image = self._create_image(contact)
        if image is not None:
            contact.image = image
            save_contact = True

        contact_addr = self._create_address(
            cleaned_data,
            owner=contact,
            data_prefix=HOME_ADDR_PREFIX,
        )
        if contact_addr is not None:
            contact.billing_address = contact_addr
            save_contact = True

        self._create_orga(contact)

        if save_contact:
            contact.save()

        return contact
Ejemplo n.º 7
0
class BaseEditForm(CremeEntityForm):
    source = CreatorEntityField(label=pgettext_lazy('billing', 'Source organisation'), model=get_organisation_model())
    target = GenericEntityField(label=pgettext_lazy('billing', 'Target'),
                                models=[get_organisation_model(), get_contact_model()],
                               )

    class Meta(CremeEntityForm.Meta):
        labels = {
                'discount': _('Overall discount (in %)'),
            }

    blocks = CremeEntityForm.blocks.new(
        ('orga_n_address', _('Organisations'), ['source', 'target']),  # TODO: rename (beware to template)
    )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.issued_relation   = None
        self.received_relation = None
        self.old_user_id = self.instance.user_id

        pk = self.instance.pk

        if pk is not None:  # Edit mode
            relations = Relation.objects.filter(subject_entity=pk, type__in=(REL_SUB_BILL_ISSUED, REL_SUB_BILL_RECEIVED))

            issued_relation   = find_first(relations, (lambda r: r.type_id == REL_SUB_BILL_ISSUED), None)
            received_relation = find_first(relations, (lambda r: r.type_id == REL_SUB_BILL_RECEIVED), None)

            if issued_relation:
                self.issued_relation = issued_relation
                self.fields['source'].initial = issued_relation.object_entity_id

            if received_relation:
                self.received_relation = received_relation
                self.fields['target'].initial = received_relation.object_entity

    def _manage_relation(self, existing_relation, type_id, related_entity):
        if existing_relation:
            if related_entity.id != existing_relation.object_entity_id:
                existing_relation.delete()
                existing_relation = None

        if not existing_relation:
            instance = self.instance
            Relation.objects.safe_create(
                subject_entity=instance,
                type_id=type_id,
                object_entity=related_entity,
                user=instance.user,
            )

    def save(self, *args, **kwargs):
        instance = self.instance

        cleaned_data = self.cleaned_data
        source = cleaned_data['source']
        target = cleaned_data['target']
        user   = cleaned_data['user']

        payment_info = instance.payment_info
        pinfo_orga_id = payment_info.organisation_id if payment_info else None

        if source.id != pinfo_orga_id:
            instance.payment_info = None

        if instance.payment_info is None:  # Optimization
            source_pis = PaymentInformation.objects.filter(organisation=source.id)[:2]
            if len(source_pis) == 1:
                instance.payment_info = source_pis[0]

        instance = super().save(*args, **kwargs)

        self._manage_relation(self.issued_relation, REL_SUB_BILL_ISSUED, source)  # TODO: move this intelligence in models.Base.save()
        self._manage_relation(self.received_relation, REL_SUB_BILL_RECEIVED, target)

        # TODO: do this in model/with signal to avoid errors ???
        if self.old_user_id and self.old_user_id != user.id:
            # Do not use queryset.update() to call the CremeEntity.save() method TODO: change with future Credentials system ??
            for line in instance.iter_all_lines():
                line.user = instance.user
                line.save()

        return instance
Ejemplo n.º 8
0
class PollRepliesCreateForm(CremeForm):
    user = ModelChoiceField(
        label=_('User'),
        required=True,
        queryset=get_user_model().objects.filter(is_staff=False),
    )
    name = CharField(label=_('Name'), required=True)
    campaign = CreatorEntityField(
        label=pgettext_lazy('polls', 'Related campaign'),
        model=PollCampaign,
        required=False,
    )
    number = IntegerField(label=_('Number of replies'),
                          initial=1,
                          min_value=1,
                          required=False)
    persons = MultiGenericEntityField(
        label=_('Persons who filled'),
        required=False,
        models=[Organisation, Contact],
        help_text=_('Each reply will be linked to a person '
                    '(and "Number of replies" will be ignored)'),
    )
    pform = CreatorEntityField(label=_('Related form'),
                               model=polls.get_pollform_model())

    def __init__(self, entity=None, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # self.instance = None
        self.preplies = []
        fields = self.fields
        fields['user'].initial = self.user.id

        if entity is not None:
            if isinstance(entity, PollCampaign):
                del fields['campaign']
                self.campaign = entity
            elif isinstance(entity, PollForm):
                del fields['pform']
                self._set_pform_n_validate(entity, Http404)
            elif isinstance(entity, (Contact, Organisation)):
                del fields['persons']
                del fields['number']
                self.persons = [entity]

        # get_initial = self.initial.get

        # pform = get_initial('pform')
        # if pform:
        #     del fields['pform']
        #     self._set_pform_n_validate(pform, Http404)

        # campaign = get_initial('campaign')
        # if campaign:
        #     del fields['campaign']
        #     self.campaign = campaign

        # linked_persons = get_initial('persons')
        # if linked_persons is not None:
        #     del fields['persons']
        #     del fields['number']
        #     self.persons = linked_persons

    def clean_campaign(self):
        self.campaign = campaign = self.cleaned_data['campaign']

        return campaign

    def clean_number(self):
        return self.cleaned_data['number'] or 1

    def clean_persons(self):
        self.persons = linked_persons = self.cleaned_data['persons']

        return linked_persons

    def clean_pform(self):
        pform = self.cleaned_data['pform']
        self._set_pform_n_validate(pform, ValidationError)

        return pform

    def _set_pform_n_validate(self, pform, exception_class):
        lines = pform.lines.filter(disabled=False)

        if not lines:
            raise exception_class(
                gettext('The form must contain one line at least.'))

        self.pform = pform
        self.pform_lines = lines

    def create_preply(
            self, index, person,
            total_number):  # Easy to hook it in another app -> do not save
        cleaned_data = self.cleaned_data
        name = cleaned_data['name']

        if total_number != 1:
            name += '#{}'.format(index)

        return PollReply(
            user=cleaned_data['user'],
            pform=self.pform,
            type=self.pform.type,
            name=name,
            campaign=self.campaign,
            person=person,
        )

    def save(self, *args, **kwargs):
        linked_persons = self.persons

        if linked_persons:
            reply_number = len(linked_persons)
            linked_persons = linked_persons
        else:
            reply_number = self.cleaned_data['number']
            linked_persons = repeat(None, reply_number)

        duplicate_tree = self.pform.duplicate_tree
        # instance = None

        for i, person in enumerate(linked_persons, start=1):
            instance = self.create_preply(i, person, reply_number)
            instance.save()
            duplicate_tree(instance, self.pform_lines)
            self.preplies.append(instance)