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)
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
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, )
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
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
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
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
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)