def record_disclosure_email(self): config = Configuration.get_solo() template = config.record_disclosure_template data = get_member_data(self) for profile in self.profiles: data += get_member_data(profile) key_value_data = [ d for d in data if len(d) == 2 and not isinstance(d, str) ] text_data = [d for d in data if isinstance(d, str)] key_length = min(max(len(d[0]) for d in key_value_data), 20) key_value_text = [] for key, value in key_value_data: key = (key + ":").ljust(key_length) + " " value = value.strip() if value in [None, "None", ""]: value = "-" if isinstance(value, str) and "\n" in value: value = "\n".join([ " " * (key_length + 1) + line for line in value.split("\n") ]).strip() key_value_text.append(key + value) key_value_text = "\n".join(key_value_text) if text_data: key_value_text += "\n" + "\n".join(text_data) context = { "association_name": config.name, "data": key_value_text, "number": self.number, "balance": format_with_currency(self.balance, long_form=True), } return template.to_mail(self.email, context=context, save=False)
def __init__(self, *args, **kwargs): if 'locales' not in kwargs: from byro.common.models import Configuration config = Configuration.get_solo() kwargs['locales'] = [config.language or settings.LANGUAGE_CODE] return super().__init__(*args, **kwargs)
def to_mail(self, email, locale=None, context=None, skip_queue=False, attachments=None): from byro.common.models import Configuration config = Configuration.get_solo() locale = locale or config.language with override(locale): context = context or dict() try: subject = str(self.subject).format(**context) text = str(self.text).format(**context) except KeyError as e: raise SendMailException( 'Experienced KeyError when rendering Text: {e}'.format( e=e)) mail = EMail( to=email, reply_to=self.reply_to, bcc=self.bcc, subject=subject, text=text, ) mail.save() if attachments: for a in attachments: mail.attachments.add(a) if skip_queue: mail.send() return mail
def statute_barred_debt(self, future_limit=relativedelta()) -> Decimal: limit = relativedelta( months=Configuration.get_solo().liability_interval) - future_limit last_unenforceable_date = now().replace( month=12, day=31) - limit - relativedelta(years=1) return max(Decimal('0.00'), -self._calc_balance(last_unenforceable_date))
def record_disclosure_email(self): config = Configuration.get_solo() template = config.record_disclosure_template data = get_member_data(self) for profile in self.profiles: data += get_member_data(profile) key_value_data = [ d for d in data if len(d) == 2 and not isinstance(d, str) ] text_data = [d for d in data if isinstance(d, str)] key_length = min(max(len(d[0]) for d in key_value_data), 20) key_value_text = '\n'.join((key + ':').ljust(key_length) + ' ' + value for key, value in key_value_data) if text_data: key_value_text += '\n' + '\n'.join(text_data) context = { 'association_name': config.name, 'data': key_value_text, 'number': self.number, 'balance': '{currency} {balance}'.format(currency=config.currency, balance=self.balance) } return template.to_mail(self.email, context=context, save=False)
def form_valid(self, form): self.form = form form.save() messages.success(self.request, _('The member was added, please edit additional details if applicable.')) form.instance.log(self, '.created') responses = new_member.send_robust(sender=form.instance) for module, response in responses: if isinstance(response, Exception): messages.warning(self.request, _('Some post processing steps could not be completed: ') + str(response)) config = Configuration.get_solo() if config.welcome_member_template and form.instance.email: context = { 'name': config.name, 'contact': config.mail_from, 'number': form.instance.number, 'member_name': form.instance.name, } responses = [r[1] for r in new_member_mail_information.send_robust(sender=form.instance) if r] context['additional_information'] = '\n'.join(responses).strip() config.welcome_member_template.to_mail(email=form.instance.email, context=context) if config.welcome_office_template: context = {'member_name': form.instance.name} responses = [r[1] for r in new_member_office_mail_information.send_robust(sender=form.instance) if r] context['additional_information'] = '\n'.join(responses).strip() config.welcome_office_template.to_mail(email=config.backoffice_mail, context=context) return super().form_valid(form)
def form_valid(self, form): self.form = form form.save() messages.success( self.request, _('The member was added, please edit additional details if applicable.' )) new_member.send_robust(sender=form.instance) config = Configuration.get_solo() if config.welcome_member_template: context = { 'name': config.name, 'contact': config.mail_from, 'number': form.instance.number, 'member_name': form.instance.name, } responses = [ r[1] for r in new_member_mail_information.send_robust( sender=form.instance) if r ] context['additional_information'] = '\n'.join(responses).strip() config.welcome_member_template.to_mail(email=form.instance.email, context=context) if config.welcome_office_template: context = {'member_name': form.instance.name} responses = [ r[1] for r in new_member_office_mail_information.send_robust( sender=form.instance) if r ] context['additional_information'] = '\n'.join(responses).strip() config.welcome_office_template.to_mail( email=config.backoffice_mail, context=context) return super().form_valid(form)
def byro_information(request): ctx = { "config": Configuration.get_solo(), "pending_mails": EMail.objects.filter(sent__isnull=True).count(), "pending_transactions": Transaction.objects.unbalanced_transactions().count(), "log_end": LogEntry.objects.get_chain_end(), "effective_date_format": formats.get_format("SHORT_DATE_FORMAT", lang=translation.get_language()), } ctx["effective_date_format_js"] = (ctx["effective_date_format"].replace( "d", "dd").replace("m", "mm").replace("Y", "yyyy")) try: ctx["url_name"] = resolve(request.path_info).url_name except Http404: ctx["url_name"] = "" if settings.DEBUG: ctx["development_warning"] = True ctx["byro_version"] = get_version() return ctx
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) config = Configuration.get_solo().registration_form or [] config = sorted( [field for field in config if field['position'] is not None], key=lambda field: field['position']) profiles = { profile.related_model.__name__: profile.related_model for profile in Member._meta.related_objects if isinstance(profile, OneToOneRel) and profile.name.startswith('profile_') } for field in config: model_name = field['name'].split('__')[0] if model_name in MAPPING: model = MAPPING[model_name] else: model = profiles[field['name'].split('__')[0]] temp_form = forms.modelform_factory( model, fields=[field['name'].split('__')[-1]])() form_field = [field for field in temp_form.fields.values()][0] form_field.model = model self.fields[field['name']] = form_field
def save(self): data = [{ 'position': value, 'name': key, } for key, value in self.cleaned_data.items()] config = Configuration.get_solo() config.registration_form = list(data) config.save()
def get_context_data(self, **kwargs): result = super().get_context_data(**kwargs) config = Configuration.get_solo() result["name_config"] = { "order": config.default_order_name, "direct": config.default_direct_address_name, } return result
def update_order_name(self, force=False, save=True): if not self.order_name or force: name_parts = self.name.split() config = Configuration.get_solo() self.order_name = (name_parts[0] if config.default_order_name == "first" else name_parts[-1]) if save: self.save()
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields_extra = OrderedDict() fieldsets = [] config = Configuration.get_solo().registration_form or [] data = {entry['name']: entry for entry in config if 'name' in entry} for model, field in self.get_form_fields(): key = '{}__{}'.format(SPECIAL_NAMES.get(model, model.__name__), field.name) entry = data.get(key, {}) verbose_name = field.verbose_name or field.name if model not in SPECIAL_NAMES: verbose_name = '{verbose_name} ({model.__name__})'.format( verbose_name=verbose_name, model=model) fields = OrderedDict() fields['position'] = forms.IntegerField( required=False, label=_("Position in form")) if isinstance(field, models.DateField): fields['default_date'] = forms.ChoiceField( required=False, label=_('Default date'), choices=DefaultDates.choices) if isinstance(field, models.BooleanField): fields['default_boolean'] = forms.ChoiceField( required=False, label=_('Default value'), choices=DefaultBoolean.choices) default_field = self.build_default_field(field, model) if default_field: fields['default'] = default_field for name, form_field in fields.items(): form_field.initial = entry.get(name, form_field.initial) fieldsets.append(( ( # This part is responsible for sorting the model fields: data.get(key, {}).get('position', None) or 998, # Position in form, if set (or 998) SPECIAL_ORDER.index(key) if key in SPECIAL_ORDER else 66, # SPECIAL_ORDER first 0 if model in SPECIAL_NAMES else 1, # SPECIAL_NAMES first ), key, # Fall back to sorting by key, otherwise verbose_name, OrderedDict(( "{key}__{name}".format(key=key, name=name), value ) # TODO: make fields an ordered dict that prepends {key} to every key for more fanciness for name, value in fields.items()))) fieldsets.sort() for _position, key, verbose_name, form_fields in fieldsets: self.fields_extra[key] = (verbose_name, (self[name] for name in form_fields.keys())) self.fields.update(form_fields)
def format_with_currency(value, long_form=False): config = Configuration.get_solo() amount = str(value if config.display_cents else int(value)) currency = config.currency if long_form else config.currency_symbol if currency: parts = [amount, currency] return " ".join(parts if config.currency_postfix else reversed(parts)) else: return amount
def end_membership(self, ms, form, active_buttons): if form.instance.end: if not getattr(form.instance, "member", False): form.instance.member = self.get_object() form.save() form.instance.log(self, ".ended") messages.success( self.request, _("The membership has been terminated. Please check the outbox for the notifications." ), ) form.instance.member.update_liabilites() responses = leave_member.send_robust(sender=form.instance) for module, response in responses: if isinstance(response, Exception): messages.warning( self.request, _("Some post processing steps could not be completed: " ) + str(response), ) config = Configuration.get_solo() if config.leave_member_template: context = { "name": config.name, "contact": config.mail_from, "number": form.instance.member.number, "member_name": form.instance.member.name, "end": form.instance.end, } responses = [ r[1] for r in leave_member_mail_information.send_robust( sender=form.instance) if r ] context["additional_information"] = "\n".join( responses).strip() config.leave_member_template.to_mail( email=form.instance.member.email, context=context) if config.leave_office_template: context = { "member_name": form.instance.member.name, "end": form.instance.end, } responses = [ r[1] for r in leave_member_office_mail_information.send_robust( sender=form.instance) if r ] context["additional_information"] = "\n".join( responses).strip() config.leave_office_template.to_mail( email=config.backoffice_mail, context=context)
def mail(email: str, subject: str, template: Union[str, LazyI18nString], context: Dict[str, Any] = None, locale: str = None, headers: dict = None): headers = headers or {} c = Configuration.get_solo() locale = locale or c.language with override(locale): body = str(template) if context: body = body.format_map(TolerantDict(context)) sender = Configuration.get_solo().mail_from subject = str(subject) body_plain = body return mail_send_task.apply_async(args=([email], subject, body_plain, sender, headers))
def post(self, *args, **kwargs): for form in self.get_forms(): if form.is_valid() and form.has_changed() and form.instance.end: if not getattr(form.instance, 'member', False): form.instance.member = self.get_object() form.save() messages.success( self.request, _('The membership has been terminated. Please check the outbox for the notifications.' )) responses = leave_member.send_robust(sender=form.instance) for module, response in responses: if isinstance(response, Exception): messages.warning( self.request, _('Some post processing steps could not be completed: ' ) + str(response)) config = Configuration.get_solo() if config.leave_member_template: context = { 'name': config.name, 'contact': config.mail_from, 'number': form.instance.member.number, 'member_name': form.instance.member.name, 'end': form.instance.end, } responses = [ r[1] for r in leave_member_mail_information.send_robust( sender=form.instance) if r ] context['additional_information'] = '\n'.join( responses).strip() config.leave_member_template.to_mail( email=form.instance.member.email, context=context) if config.leave_office_template: context = { 'member_name': form.instance.member.name, 'end': form.instance.end, } responses = [ r[1] for r in leave_member_office_mail_information. send_robust(sender=form.instance) if r ] context['additional_information'] = '\n'.join( responses).strip() config.leave_office_template.to_mail( email=config.backoffice_mail, context=context) return redirect(reverse('office:members.leave', kwargs=self.kwargs))
def send(self): if self.sent: raise Exception( "This mail has been sent already. It cannot be sent again.") self.process_special_to() from byro.common.models import Configuration config = Configuration.get_solo() if self.to == "special:all" or not self.to.startswith("special:"): send_tos = [] if self.to == "special:all": for member in (Member.objects.filter( Q(memberships__start__lte=now().date()) & (Q(memberships__end__isnull=True) | Q(memberships__end__gte=now().date()))).filter( email__isnull=False).exclude(email="").all()): send_tos.append([member.email]) self.members.add(member) else: to_addrs = self.to.split(",") send_tos.append(to_addrs) for addr in to_addrs: for member in Member.all_objects.filter( email__iexact=addr.lower()).all(): self.members.add(member) headers = {} if self.reply_to: headers["Reply-To"] = self.reply_to from byro.mails.send import mail_send_task for to_addrs in send_tos: mail_send_task( to=to_addrs, subject=self.subject, body=self.text, sender=config.mail_from, cc=(self.cc or "").split(","), bcc=(self.bcc or "").split(","), attachments=self.attachment_ids, headers=headers, ) self.sent = now() self.save(update_fields=["sent"])
def form_valid(self, form): config = Configuration.get_solo() fints_account = self.get_object() sepa_account = SEPAAccount( **{ name: getattr(fints_account, name) for name in SEPAAccount._fields } ) transfer_log_data = { k: v for k, v in form.cleaned_data.items() if not k in ('pin', 'store_pin') } transfer_log_data['source_account'] = sepa_account._asdict() with self.fints_client(fints_account.login, form) as client: with client: try: response = client.simple_sepa_transfer( sepa_account, form.cleaned_data['iban'], form.cleaned_data['bic'], form.cleaned_data['recipient'], form.cleaned_data['amount'], config.name, form.cleaned_data['purpose'], ) if isinstance(response, TransactionResponse): fints_account.log(self, '.transfer.completed', transfer=transfer_log_data, response_status=response.status, response_messages=response.responses, response_data=response.data) self._show_transaction_messages(response) elif isinstance(response, NeedTANResponse): transfer_uuid = self.pause_for_tan_request(client, response) fints_account.log(self, '.transfer.started', transfer=transfer_log_data, uuid=transfer_uuid, ) return HttpResponseRedirect(reverse('plugins:byro_fints:finance.fints.login.tan_request', kwargs={'pk': fints_account.login.pk, 'uuid': transfer_uuid})) else: fints_account.log(self, '.transfer.internal_error', transfer=transfer_log_data) messages.error(self.request, _("Invalid response: {}".format(response))) except: fints_account.log(self, '.transfer.exception', transfer=transfer_log_data) raise return super().form_valid(form)
def save(self): data = {} for full_name, value in self.cleaned_data.items(): name, key = full_name.rsplit("__", 1) if not (value == "" or value is None): if isinstance(value, Decimal): value = str(value) if key == "default_boolean": value = bool(value == "True") data.setdefault(name, {})[key] = value data = [dict(name=key, **value) for (key, value) in data.items()] config = Configuration.get_solo() config.registration_form = list(data) config.save()
def send(self, immediately=False, text=None, subject=None, email=None): from byro.common.models import Configuration from byro.mails.models import EMail us = Configuration.get_solo().name mail = EMail.objects.create( to=email or self.member.email, text=text or self.template.format(name=self.member.name if self.member else email, association=us), subject=_('[{association}] Your document').format(association=us) ) mail.attachments.add(self) mail.save() if immediately: mail.send() return mail
def byro_information(request): ctx = {'config': Configuration.get_solo()} try: ctx['url_name'] = resolve(request.path_info).url_name except Http404: ctx['url_name'] = '' if settings.DEBUG: ctx['development_warning'] = True with suppress(Exception): import subprocess ctx['byro_version'] = subprocess.check_output( ['git', 'describe', '--always']) return ctx
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for profile in Member.profile_classes: for field in profile._meta.fields: if field.name not in ('id', 'member'): self.fields[ f'{profile.__name__}__{field.name}'] = forms.IntegerField( required=False, label= f'{field.verbose_name or field.name} ({profile.__name__})' ) config = Configuration.get_solo().registration_form or [] for entry in config: field = self.fields.get(entry['name']) if field: field.initial = entry['position']
def get_context_data(self, **kwargs): global_config = Configuration.get_solo() context = super().get_context_data(**kwargs) context['fints_form'] = None if self.fints_interface: debit_meta = self.fints_interface.sepa_debit_init( self.object.additional_data['login_pk']) context['fints_form'] = debit_meta['form'] context['debit_count'] = self.object.payments.count() context['debit_currency'] = global_config.currency context['debit_sum'] = self.object.payments.aggregate( amount=Sum('amount'))['amount'] context['debit_account'] = self.object.additional_data['account_iban'] return context
def form_valid(self, form): self.form = form form.save() messages.success( self.request, _("The member was added, please edit additional details if applicable." ), ) form.instance.log(self, ".created") responses = new_member.send_robust(sender=form.instance) for _receiver, response in responses: if isinstance(response, Exception): messages.warning( self.request, _("Some post processing steps could not be completed: ") + str(response), ) config = Configuration.get_solo() if config.welcome_member_template and form.instance.email: context = { "name": config.name, "contact": config.mail_from, "number": form.instance.number, "member_name": form.instance.name, } responses = [ r[1] for r in new_member_mail_information.send_robust( sender=form.instance) if r ] context["additional_information"] = "\n".join(responses).strip() config.welcome_member_template.to_mail(email=form.instance.email, context=context) if config.welcome_office_template: context = {"member_name": form.instance.name} responses = [ r[1] for r in new_member_office_mail_information.send_robust( sender=form.instance) if r ] context["additional_information"] = "\n".join(responses).strip() config.welcome_office_template.to_mail( email=config.backoffice_mail, context=context) return super().form_valid(form)
def byro_information(request): ctx = { 'config': Configuration.get_solo(), 'pending_mails': EMail.objects.filter(sent__isnull=True).count(), 'pending_transactions': Transaction.objects.unbalanced_transactions().count(), } try: ctx['url_name'] = resolve(request.path_info).url_name except Http404: ctx['url_name'] = '' if settings.DEBUG: ctx['development_warning'] = True with suppress(Exception): import subprocess ctx['byro_version'] = subprocess.check_output(['git', 'describe', '--always']).decode() return ctx
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) config = Configuration.get_solo().registration_form or [] config = sorted( [field for field in config if field['position'] is not None], key=lambda field: field['position']) profiles = { profile.related_model.__name__: profile.related_model for profile in Member._meta.related_objects if isinstance(profile, OneToOneRel) and profile.name.startswith('profile_') } for field in config: self.build_field(field, profiles) if 'member__number' in self.fields: self.fields['member__number'].initial = get_next_member_number()
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) config = Configuration.get_solo().registration_form or [] config = sorted( (field for field in config if field["position"] is not None), key=lambda field: field["position"], ) profiles = { profile.related_model.__name__: profile.related_model for profile in Member._meta.related_objects if isinstance(profile, OneToOneRel) and profile.name.startswith("profile_") } for field in config: self.build_field(field, profiles) if field.get("name", "") == "member__name": self.build_field({"name": "member__direct_address_name"}, []) self.build_field({"name": "member__order_name"}, []) if "member__number" in self.fields: self.fields["member__number"].initial = get_next_member_number()
def byro_information(request): ctx = { 'config': Configuration.get_solo(), 'pending_mails': EMail.objects.filter(sent__isnull=True).count(), 'pending_transactions': Transaction.objects.unbalanced_transactions().count(), 'log_end': LogEntry.objects.get_chain_end(), } try: ctx['url_name'] = resolve(request.path_info).url_name except Http404: ctx['url_name'] = '' if settings.DEBUG: ctx['development_warning'] = True ctx['byro_version'] = get_version() return ctx
def get_initial(self): config = DirectDebitConfiguration.get_solo() global_config = Configuration.get_solo() retval = super().get_initial() retval['debit_date'] = next_debit_date(region=config.creditor_id[:2]) retval['debit_text'] = _('Membership fees for %(organization_name)s' % { 'organization_name': global_config.name, }) retval['own_name'] = global_config.name retval['subject'] = config.debit_notification_template.subject retval['text'] = config.debit_notification_template.text retval['own_account'] = self.selected_account_login_pk if self.do_cor1: retval['cor1'] = True if self.selected_sepa_pain_formats: l = list(e.lower() for e in self.selected_sepa_pain_formats if e.lower() in SUPPORTED_PAIN_FORMATS) if not l: messages.warning(self.request, _("No common supported SEPA PAIN format")) l = self.selected_sepa_pain_formats if l: l.sort() retval['sepa_format'] = l[-1] else: messages.warning(self.request, _("No supported SEPA PAIN format")) if self.selected_account: retval['own_iban'] = str(self.selected_account) retval['own_bic'] = str(self.selected_account.bic) return retval