def test_bic_model_field(self): valid = { 'DEUTDEFF': 'DEUTDEFF', 'NEDSZAJJXXX': 'NEDSZAJJXXX', 'DABADKKK': 'DABADKKK', 'UNCRIT2B912': 'UNCRIT2B912', 'DSBACNBXSHA': 'DSBACNBXSHA' } invalid = { 'NEDSZAJJXX': ['BIC codes have either 8 or 11 characters.'], 'CIBCJJH2': ['JJ is not a valid country code.'], 'D3UTDEFF': ['D3UT is not a valid institution code.'] } self.assertFieldOutput(BICFormField, valid=valid, invalid=invalid) bic_model_field = BICField() # Test valid inputs for model field. for input, output in valid.items(): self.assertEqual(bic_model_field.clean(input, None), output) self.assertIsNone(bic_model_field.to_python(None)) # Invalid inputs for model field. for input, errors in invalid.items(): with self.assertRaises(ValidationError) as context_manager: bic_model_field.clean(input, None) self.assertEqual(errors, context_manager.exception.messages)
def test_bic_model_field(self): valid = { "DEUTDEFF": "DEUTDEFF", "NEDSZAJJXXX": "NEDSZAJJXXX", "DABADKKK": "DABADKKK", "UNCRIT2B912": "UNCRIT2B912", "DSBACNBXSHA": "DSBACNBXSHA", } invalid = { "NEDSZAJJXX": ["BIC codes have either 8 or 11 characters."], "CIBCJJH2": ["JJ is not a valid country code."], "D3UTDEFF": ["D3UT is not a valid institution code."], } self.assertFieldOutput(BICFormField, valid=valid, invalid=invalid) bic_model_field = BICField() # Test valid inputs for model field. for input, output in valid.items(): self.assertEqual(bic_model_field.clean(input, None), output) self.assertIsNone(bic_model_field.to_python(None)) # Invalid inputs for model field. for input, errors in invalid.items(): with self.assertRaises(ValidationError) as context_manager: bic_model_field.clean(input, None) self.assertEqual(errors, context_manager.exception.messages)
class SepaRoute(models.Model): """Represent an available route to a SEPA destination.""" # FIXME: Could terminology here be aligned with BGP4? # FIXME: We must store here the source of where the data comes # from. Which directory and which version of the directory. class ReachabilityChoice(models.TextChoices): DIRECT = 'direct', 'Direct Participant' INDIRECT = 'indirect', 'Indirect Participant' UNKNOWN = 'unknown', 'Unknown' uuid = models.UUIDField(default=uuid.uuid4, editable=False) scheme = models.CharField(max_length=70, blank=False, choices=PaymentScheme.choices) external_key = models.CharField(max_length=16, blank=True) bic = BICField(blank=False) psp_name = models.CharField(max_length=140, blank=False) psp_city = models.CharField(max_length=35, blank=True) psp_country = CountryField(blank=True) reachable_via = models.CharField(max_length=4, blank=True, choices=SepaCsm.choices) reachability_type = models.CharField(max_length=16, blank=False, choices=ReachabilityChoice.choices) intermediary_bic = BICField(blank=True) preferred_route = models.BooleanField(default=False) valid_from = models.DateTimeField(null=True, blank=True) valid_to = models.DateTimeField(null=True, blank=True) class Meta: verbose_name = 'SEPA route'
class Application(models.Model): applicant = models.ForeignKey('Applicant') offer_year = models.ForeignKey('base.OfferYear') creation_date = models.DateTimeField(auto_now=True) application_type = models.CharField( max_length=20, choices=application_type.APPLICATION_TYPE_CHOICES) coverage_access_degree = models.CharField( max_length=30, blank=True, null=True, choices=coverage_access_degree.COVERAGE_ACCESS_DEGREE_CHOICES) valuation_possible = models.NullBooleanField(default=None) started_similar_studies = models.NullBooleanField(default=None) credits_to_value = models.NullBooleanField(default=None) applied_to_sameprogram = models.NullBooleanField(default=None) resident = models.NullBooleanField(default=None) raffle_number = models.CharField(max_length=50, blank=True, null=True) study_grant = models.BooleanField(default=False) study_grant_number = models.CharField(max_length=50, blank=True, null=True) deduction_children = models.BooleanField(default=False) scholarship = models.BooleanField(default=False) scholarship_organization = models.TextField(blank=True, null=True) sport_membership = models.BooleanField(default=False) culture_membership = models.BooleanField(default=False) solidarity_membership = models.BooleanField(default=False) bank_account_iban = IBANField(include_countries=IBAN_SEPA_COUNTRIES, blank=True, null=True) bank_account_bic = BICField(blank=True, null=True) bank_account_name = models.CharField(max_length=255, blank=True, null=True) def __str__(self): return u"%s %s" % (self.applicant, self.offer_year)
class MemberSepa(Auditable, models.Model): member = AutoOneToOneField( to='members.Member', related_name='profile_sepa', on_delete=models.PROTECT, ) iban = IBANField(null=True, blank=True, verbose_name="IBAN") bic = BICField(null=True, blank=True, verbose_name="BIC") institute = models.CharField( max_length=255, null=True, blank=True, verbose_name="IBAN Institute") issue_date = models.DateField( null=True, blank=True, verbose_name="IBAN Issue Date", help_text="The issue date of the direct debit mandate. (1970-01-01 means there is no issue date in the database )") fullname = models.CharField( null=True, blank=True, max_length=255, verbose_name="IBAN full name", help_text="Full name for IBAN account owner") address = models.CharField( null=True, blank=True, max_length=255, verbose_name="IBAN address", help_text="Address line (e.g. Street / House Number)") zip_code = models.CharField( null=True, blank=True, max_length=20, verbose_name="IBAN zip code", help_text="ZIP Code") city = models.CharField( null=True, blank=True, max_length=255, verbose_name="IBAN City",) country = models.CharField( null=True, blank=True, max_length=255, default="Deutschland", verbose_name="IBAN Country",) mandate_reference = models.CharField( null=True, blank=True, max_length=255, verbose_name="IBAN Mandate Reference",) mandate_reason = models.CharField( null=True, blank=True, max_length=255, verbose_name="IBAN Mandate Reason",) form_title = _('SEPA information') @property def is_usable(self): return bool(self.iban and self.bic and self.mandate_reference)
class DirectDebitMandate(models.Model): customer = models.ForeignKey('customer.Customer', on_delete=models.CASCADE, verbose_name=_('customer'), related_name='direct_debit_mandate_set') reference = models.CharField(_('mandate reference'), max_length=35, unique=True, blank=True, null=True) name = models.CharField(max_length=100) street = models.CharField(max_length=100) postal_code = models.CharField(max_length=20) city = models.CharField(max_length=100) country = CountryField(default='DE') iban = IBANField('IBAN', help_text='International Bank Account Number') bic = BICField('BIC', help_text='Bank Identifier Code') bank_name = models.CharField(max_length=50, blank=True) created = models.DateTimeField(_('created'), auto_now_add=True) updated = models.DateTimeField(_('updated'), auto_now=True) signed = models.DateField(blank=True, null=True) revoked = models.DateField(_('revoked'), blank=True, null=True) last_used = models.DateField(_('last_used'), blank=True, null=True) TYPE_CORE = 'CORE' TYPE_COR1 = 'COR1' TYPE_B2B = 'B2B' TYPE_CHOICES = ( (TYPE_CORE, 'CORE'), (TYPE_COR1, 'COR1'), (TYPE_B2B, 'B2B'), ) type = models.CharField(max_length=4, choices=TYPE_CHOICES) #document = models.ForeignKey('documents.Document', # on_delete=models.CASCADE, # verbose_name=_('signed document'), # blank=True, null=True) class Meta: verbose_name = _('SEPA direct debit mandate') verbose_name_plural = _('SEPA direct debit mandates') def __str__(self): if self.reference: return self.reference else: return '<no reference>' @property def address_lines(self): return [ self.name, self.street, self.postal_code + ' ' + self.city, self.get_country_display() ] @property def anonymized_iban(self): return anonymize_iban(self.iban)
class BillingProfile(BillingInfo): user = models.OneToOneField(settings.AUTH_USER_MODEL, models.CASCADE) bic = BICField(blank=True) iban = IBANField(blank=True) @receiver(post_save, sender=settings.AUTH_USER_MODEL) def create_profile(sender, instance, created=False, **kwargs): if created: BillingProfile.objects.create(user=instance)
class Expense(models.Model): name = models.CharField(ugettext_lazy('Name'), max_length=255, validators=validators['name']) email = models.EmailField(ugettext_lazy('Email address'), validators=validators['email']) cc_email = models.EmailField(ugettext_lazy('CC email address'), blank=True, null=True, help_text=ugettext_lazy('Copy of the expense will be sent to the email.'), validators=validators['email']) phone = models.CharField(ugettext_lazy('Phone'), max_length=255, validators=validators['phoneno']) address = models.CharField(ugettext_lazy('Address'), max_length=255, validators=validators['address']) iban = IBANField(ugettext_lazy('Bank account no')) swift_bic = BICField(ugettext_lazy('BIC no'), blank=True, null=True) personno = models.CharField(ugettext_lazy('Person number'), max_length=11, validators=validators['hetu_or_businessid'], help_text=ugettext_lazy('If you apply for an expense reimbursement for a local group, enter the group’s business ID here. Kilometric allowances and daily subsistence allowances can not be applied for with a business ID.')) user = models.ForeignKey(User,on_delete=models.PROTECT) description = models.CharField(ugettext_lazy('Purpose'), max_length=255) memo = models.TextField(ugettext_lazy('Info'), help_text=ugettext_lazy('Eg. Names of the additional passengers, people in the meeting, cost centre or activity sector.'), blank=True, null=True) organisation = models.ForeignKey(Organisation, on_delete=models.PROTECT) status = models.IntegerField(ugettext_lazy('Status'), choices=APPLICATION_STATUSES, default=0) katre_status = models.IntegerField(ugettext_lazy('Katre status'), choices=KATRE_STATUSES, default=0) created_at = models.DateTimeField(ugettext_lazy('Sent'), auto_now_add=True) updated_at = models.DateTimeField(ugettext_lazy('Edited'), auto_now=True) def amount(self): sum = 0 lines = ExpenseLine.objects.filter(expense=self) for line in lines: sum+= line.sum() return sum def __unicode__(self): return self.name + ': ' + self.description + ' (' + str(self.amount()) + ' e)' def finvoice(self): expense = self expenselines = ExpenseLine.objects.filter(expense=expense) return createFinvoice(expense, expenselines) def needsKatre(self): expense = self expenselines = ExpenseLine.objects.filter(expense=expense) for line in expenselines: if line.expensetype_type in ['FPD', 'PPD', 'FOPD', 'MA', 'T']: return True return False def katre(self): expense = self expenselines = ExpenseLine.objects.filter(expense=expense) return createKatreReport(expense, expenselines)
class MangoPayBankAccount(models.Model): mangopay_user = models.ForeignKey(MangoPayUser, related_name="mangopay_bank_accounts") mangopay_id = models.PositiveIntegerField(null=True, blank=True) address = models.CharField(max_length=254) account_type = models.CharField(max_length=2, choices=BANK_ACCOUNT_TYPE_CHOICES, default=BANK_ACCOUNT_TYPE_CHOICES.iban) iban = IBANField(blank=True, null=True) bic = BICField(blank=True, null=True) country = CountryField(null=True, blank=True) account_number = models.CharField(max_length=15, null=True, blank=True) # BA_US type only fields aba = models.CharField(max_length=9, null=True, blank=True) deposit_account_type = models.CharField(max_length=8, choices=DEPOSIT_CHOICES, default=DEPOSIT_CHOICES.checking) def get_bank_account(self): bank_account = BankAccount( id=self.mangopay_id, owner_name=self.mangopay_user.user.get_full_name(), owner_address=Address(address_line_1=self.address), user=self.mangopay_user.get_user(), type=self.account_type) if self.account_type == BANK_ACCOUNT_TYPE_CHOICES.iban: bank_account.iban = self.iban elif self.account_type == BANK_ACCOUNT_TYPE_CHOICES.us: bank_account.aba = self.aba bank_account.deposit_account_type = self.deposit_account_type bank_account.account_number = self.account_number elif self.account_type == BANK_ACCOUNT_TYPE_CHOICES.other: bank_account.account_number = self.account_number else: raise NotImplementedError( "Bank Account Type ({0}) not implemented.".format( self.account_type)) return bank_account def create(self): bank_account = self.get_bank_account() bank_account.save() self.mangopay_id = bank_account.get_pk() self.save()
class Person(models.Model): user = models.OneToOneField(User, on_delete=models.PROTECT) phone = models.CharField(ugettext_lazy('Phone'), max_length=255, blank=True, null=True, validators=validators['phoneno']) address = models.CharField(ugettext_lazy('Address'), max_length=255, blank=True, null=True, validators=validators['address']) iban = IBANField(ugettext_lazy('Bank account no'), blank=True, null=True) swift_bic = BICField(ugettext_lazy('BIC no'), blank=True, null=True) personno = models.CharField(ugettext_lazy('Person number'), max_length=11, blank=True, null=True, validators=validators['hetu_or_businessid'], help_text=ugettext_lazy('Person number is required for every expense application for annual announcements to the tax authority. If you don\'t want to save it here, you can enter it to each expense application separately.')) type = models.IntegerField(ugettext_lazy('Type'), choices=PERSONTYPE_CHOICES, default=1) language = models.CharField(ugettext_lazy('Site language'), max_length=6, blank=True, null=True, choices=settings.LANGUAGES) def name(self): return self.user.first_name + ' ' + self.user.last_name def __unicode__(self): return self.name()
class BankAccount(RulesModel): class Meta: rules_permissions = { "add": pred.is_super_admin | pred.is_admin | pred.is_author | pred.is_editor | pred.is_member, "view": pred.is_super_admin | pred.is_admin | pred.is_user_of_bank_account, "change": pred.is_super_admin | pred.is_admin | pred.is_user_of_bank_account, "delete": pred.is_super_admin, } account_holder = models.CharField(max_length=100) iban = IBANField(include_countries=IBAN_SEPA_COUNTRIES) bic = BICField() def belongs_to_host(self, host): if hasattr(self, 'host') and self.host == host: return True elif hasattr(self, 'user') and self.user.host == host: return True else: return False def type(self): if hasattr(self, 'host'): return 'host' if hasattr(self, 'user'): return 'user' def __str__(self): return self.account_holder def get_hosts(self): if self.host: return self.host elif self.user: return self.user.hosts else: return None
class Organization(models.Model): name = models.CharField(_('organization name'), max_length=150) street = models.CharField(_('street'), max_length=150, blank=True, default='') city = models.CharField(_('city'), max_length=150, blank=True, default='') postal_code = PostalCodeField(_('postal code'), blank=True, default='') email = EmailField(_('email address'), blank=True, default='') phone = models.CharField(_('phone'), max_length=30, blank=True, default='') company_num = models.CharField(_('company number'), max_length=8, blank=True, default='') vat_number = models.CharField(_('VAT number'), max_length=12, blank=True, default='') iban = IBANField(_('IBAN'), blank=True, default='') bic = BICField(_('BIC (SWIFT)'), blank=True, default='') class Meta: app_label = 'leprikon' verbose_name = _('organization') verbose_name_plural = _('organizations') def __str__(self): return f'{self.name} ({self.bank_account})' @cached_property def address(self): return ', '.join( filter(bool, (self.street, self.city, self.postal_code))) address.short_description = _('address') @cached_property def bank_account(self): return self.iban and BankAccount(self.iban) bank_account.short_description = _('bank account')
class Player(BasePlayer): iban = IBANField(verbose_name="IBAN") bic = BICField(verbose_name="BIC") bankname = models.CharField(verbose_name="Nachname:", max_length=50) bankvname = models.CharField(verbose_name="Vorname:", max_length=50) street = models.CharField(verbose_name="Straße / Hausnr.:", max_length=50) city = models.CharField(verbose_name="Stadt:", max_length=50) zipcode = models.IntegerField(verbose_name="PLZ:") user_agent = models.CharField() window_height = models.CharField() window_width = models.CharField() ibanmsgseen = models.PositiveIntegerField(default=0)
def test_default_form(self): bic_model_field = BICField() self.assertEqual(type(bic_model_field.formfield()), type(BICFormField()))
class Company(UUIDModel): name = models.CharField(_("Company name"), max_length=255) address = models.TextField(_("Address"), blank=True) zip_code = models.CharField(_("Postal code"), max_length=10, blank=True) city = models.CharField(_("City"), max_length=255, blank=True) country = CountryField(_("Country"), blank=True, null=True) phone = PhoneNumberField(_("Phone number"), blank=True) email = models.EmailField(_("Email"), blank=True) website = models.URLField(_("Web site"), blank=True) vat_id = models.CharField(_("VAT ID"), blank=True, max_length=20) name_for_bank = models.CharField(_("Bank account name"), max_length=255, blank=True) bank = models.TextField(_("Bank"), blank=True) bic = BICField(_("BIC"), blank=True) iban = IBANField(_("IBAN"), blank=True) logo = models.ImageField(_("Logo"), blank=True, null=True) contrast_color = ColorField(_("Contrast color"), default="#3B4451") signature_text = models.CharField(_("Signature as text"), max_length=100, blank=True) signature_image = models.ImageField(_("Signature as image"), null=True, blank=True) email_signature = models.TextField(_("Email signature"), blank=True) from_email = models.CharField(_("From email"), max_length=255, default='{} <{}>'.format( settings.ADMINS[0][0], settings.ADMINS[0][1])) bcc_email = models.EmailField( _("Copy of invoices"), help_text=_( "Email address that will receive every sent invoice in bcc"), blank=True) thanks = models.TextField( _("Thanks"), blank=True, help_text= _("Thanks at bottom of invoice. If set, this will be on every invoice, regardless of language" )) class Meta: ordering = ('name', 'created') verbose_name = _("Company") verbose_name_plural = _("Companies") def __str__(self): return self.name @property def all_invoices_api_url(self): return reverse('api:company-invoices', kwargs={'company_pk': self.pk}) @property def bank_account_name(self): return self.name_for_bank or self.name @property def delete_url(self): return reverse('companies:delete', kwargs={'pk': self.pk}) @property def earnings_api_url(self): return reverse('api:earnings-per-company', kwargs={'company_pk': self.pk}) @property def detail_url(self): return reverse('companies:detail', kwargs={'pk': self.pk}) @property def edit_url(self): return reverse('companies:update', kwargs={'pk': self.pk}) @property def has_signature(self): return bool(self.signature_text or self.signature_image) @property def open_invoices_api_url(self): return reverse('api:open-invoices-per-company', kwargs={'company_pk': self.pk}) @property def single_line_address(self): output = [] if self.address: output.append(self.address) if self.zip_code: output.append(f'{self.zip_code} {self.city}') if self.country: output.append(self.country.name) return ', '.join(output) def open_invoices(self): from beyondtheadmin.invoices.models import Invoice return Invoice.visible.filter(company=self).select_related('client') def latest_open_invoices(self): return self.open_invoices().order_by('-due_date')[:5] def get_absolute_url(self): return self.detail_url
class Member(models.Model): class Meta: ordering = ('-created', ) member_id = models.IntegerField(unique=True, help_text="Membership ID") comment = models.TextField(blank=True, null=True) name = models.CharField(max_length=255, help_text="First/Given Name") surname = models.CharField(max_length=255, help_text="Last/Family Name") nickname = models.CharField(max_length=255, blank=True, null=True) form_of_address = models.CharField( choices=(('F', 'Frau'), ('H', 'Herr')), max_length=10, default="H", help_text="How to formally address this person") is_underaged = models.BooleanField(default=False, help_text="Member is not 18+.") date_of_birth = models.DateField( blank=True, null=True, ) address1 = models.CharField( max_length=255, help_text="Address line 1 (e.g. Street / House Number)") address2 = models.CharField(max_length=255, blank=True, null=True, help_text="Address line 2 (optional)") zip_code = models.CharField(max_length=20, help_text="ZIP Code") city = models.CharField(max_length=255, ) country = models.CharField( max_length=255, default="Deutschland", ) email = models.EmailField(max_length=255, ) phone_number = models.CharField(max_length=32, blank=True, null=True) join_date = models.DateField( help_text= "Member joined on this date. The date is forced to the begin of given month." ) leave_date = models.DateField( null=True, blank=True, help_text= "Member left on this date. The date is forced to the end of given month" ) mailing_list_initial_mitglieder = models.BooleanField( default=True, help_text="Member should be subscribed on the Mitglieder mailing list") mailing_list_initial_mitglieder_announce = models.BooleanField( default=True, help_text= "Member should be subscribed on the Mitglieder-announce mailing list") is_active = models.BooleanField(default=True, ) is_cancellation_confirmed = models.BooleanField(default=False) payment_type = models.CharField(choices=(('SEPA', 'Lastschrift'), ('transfer', 'Überweisung')), default="SEPA", max_length=20) iban = IBANField(null=True, blank=True, verbose_name="IBAN") bic = BICField(null=True, blank=True, verbose_name="BIC") iban_institute = models.CharField(max_length=255, null=True, blank=True, verbose_name="IBAN Institute") iban_issue_date = models.DateField( null=True, blank=True, verbose_name="IBAN Issue Date", help_text= "The issue date of the direct debit mandate. (1970-01-01 means there is no issue date in the database )" ) iban_fullname = models.CharField( null=True, blank=True, max_length=255, verbose_name="IBAN full name", help_text="Full name for IBAN account owner") iban_address = models.CharField( null=True, blank=True, max_length=255, verbose_name="IBAN address", help_text="Address line (e.g. Street / House Number)") iban_zip_code = models.CharField(null=True, blank=True, max_length=20, verbose_name="IBAN zip code", help_text="ZIP Code") iban_city = models.CharField( null=True, blank=True, max_length=255, verbose_name="IBAN City", ) iban_country = models.CharField( null=True, blank=True, max_length=255, default="Deutschland", verbose_name="IBAN Country", ) is_welcome_mail_sent = models.BooleanField(default=False) is_registration_to_mailinglists_sent = models.BooleanField(default=False) is_cancellation_mail_sent_to_cashmaster = models.BooleanField( default=False) is_revoke_memberspecials_mail_sent = models.BooleanField(default=False) objects = MemberManager() modified = models.DateTimeField(auto_now=True) created = models.DateTimeField(auto_now_add=True) created_by = models.ForeignKey(settings.AUTH_USER_MODEL, ) def __str__(self): memberships = ', '.join([ "{ms.valid_from.year}-{ms.valid_from.month:02d} {ms.membership_fee_monthly}/{ms.membership_fee_interval}" .format(ms=membership) for membership in Membership.objects.filter( member=self).order_by('valid_from') ]) if self.nickname: return "{}, {} ({}) [ID: {}] {{{}}}".format( self.surname, self.name, self.nickname, self.member_id, memberships) return "{}, {} [ID: {}] {{{}}}".format(self.surname, self.name, self.member_id, memberships) def save(self, *args, **kwargs): if not self.member_id: self.member_id = (Member.objects.aggregate( models.Max('member_id')).get('member_id__max') or 0) + 1 if self.join_date and not self.join_date.day == 1: self.join_date = self.join_date.replace(day=1) if self.leave_date: from .utils import last_day_of_month self.leave_date = last_day_of_month(self.leave_date) if self.is_cancellation_confirmed: # if the membership is cancelled the member isn't active anymore self.is_active = False if not self.is_cancellation_mail_sent_to_cashmaster and not self.is_active and self.payment_type == 'SEPA': from .views import send_cancellation_mail_to_cashmaster ret = send_cancellation_mail_to_cashmaster(self) if ret: self.is_cancellation_mail_sent_to_cashmaster = True # if not self.is_registration_to_mailinglists_sent and self.is_active: # from .utils import add_to_mailman # add_to_mailman(self.email, self.mailing_list_initial_mitglieder) # self.is_registration_to_mailinglists_sent = True is_welcome_mail_sent = self.is_welcome_mail_sent self.is_welcome_mail_sent = True and self.is_active is_revoke_memberspecials_mail_sent = self.is_revoke_memberspecials_mail_sent self.is_revoke_memberspecials_mail_sent = not self.is_active ret = super().save(*args, **kwargs) if not self.is_active and not is_revoke_memberspecials_mail_sent: from .views import send_revoke_memberspecials_mail send_revoke_memberspecials_mail(self) if not is_welcome_mail_sent and self.is_active: from .views import send_welcome_email send_welcome_email(self) return ret def get_ssh_public_key(self): if hasattr(self, 'memberspecials'): return self.memberspecials.ssh_public_key return None def get_nickname(self): if self.nickname: return self.nickname return "{} {}.".format(self.name, self.surname[:2]) def get_mandate_reference(self): return "SHACKEVBEITRAGID{:04d}".format(self.member_id) def get_mandate_reason(self): return "shack e.V. Mitgliedsbeitrag ID {:d}".format(self.member_id) def get_postal_address(self): postal_address = "{} {}\n{}\n{}\n{} {}".format( self.name or "", self.surname or "", self.address1 or "", self.address2 or "", self.zip_code or "", self.city or "") postal_address = "\n".join( [line for line in postal_address.splitlines() if line.strip()]) return postal_address def get_payed_membership_fee(self, year): sum_fee = AccountTransaction.objects \ .filter(member=self, due_date__year=year, transaction_type='membership fee') \ .filter(Q(booking_type='deposit') | Q(booking_type='charge back')) \ .aggregate(models.Sum('amount')).get('amount__sum') or 0 return sum_fee def send_email(self, email_type="generic", **kwargs): from django.core.mail import EmailMessage email_parameter = dict(kwargs) email_parameter['to'] = [self.email] email = EmailMessage(**email_parameter) ret = email.send() Memberlog.objects.create(member=self, action="email_{}".format(email_type), data=email_parameter) return ret
class Event(models.Model): name = models.CharField(max_length=100) slug = models.SlugField(_("short name"), help_text=_("Short name for the URL, only use alphanumeric characters and dashes, e.g. 'arizona-muni-event-2021'"), unique=True) begin_date = models.DateTimeField(_("begin date")) end_date = models.DateTimeField(_("end date")) description = models.TextField(_("description")) logo = models.ImageField(_("logo"), upload_to="logos", blank=True, null=True) is_open = models.BooleanField(default=True, help_text=_("Registration is currently open")) contact_email = models.EmailField(_("e-mail contact")) contact_name = models.CharField(_("name"), max_length=100) host = models.CharField(help_text=_("name of the host"), max_length=100) paypal = models.EmailField(_("PayPal address"), help_text=_("Payments are sent to this address"), blank=True) account_holder = models.CharField(max_length=100, blank=True) bic = BICField(_("BIC"), blank=True) iban = IBANField(_("IBAN"), blank=True) address_is_required = models.BooleanField( _("address is required"), help_text=_("Does the post address need to be entered upon registration?"), default=False ) phone_is_required = models.BooleanField( _("phone is required"), help_text=_("Does an emergency phone number need to be provided upon registration?"), default=False ) sex_is_required = models.BooleanField( _("sex is required"), help_text=_("Does the sex need to be specified upon registration?"), default=False ) food_is_included = models.BooleanField( _("food is included"), help_text=_("Food is offered and included in the booking fee"), default=True ) vegetarian = models.BooleanField( _("vegetarian"), help_text=_("Vegetarian meals can be selected"), default=True ) vegan = models.BooleanField( _("vegan"), help_text=_("There's a vegan choice available"), default=True ) vegan_breakfast_only = models.BooleanField( _("Only vegan breakfast"), help_text="Der Benutzer wird bei der Buchung darauf hingewiesen, dass nur das Frühstück vegan ist.", default=False ) admin = models.ForeignKey("auth.User", on_delete=models.CASCADE, help_text="Dieser Benutzer kann das Event verwalten") def get_absolute_url(self): return reverse("convention:seite", args=[self.slug]) def __str__(self): return self.name @property def arrival(self): return self.days.filter(arrival=True) @property def departure(self): return self.days.filter(departure=True) @property def rates_available(self): return self.rates.filter(is_active=True) class Meta: verbose_name = _("event") verbose_name_plural = _("events") ordering = ("-begin_date", "-end_date")
class MemberSepa(Auditable, models.Model): member = AutoOneToOneField( to="members.Member", related_name="profile_sepa", on_delete=models.PROTECT ) iban = IBANField(null=True, blank=True, verbose_name="IBAN") bic = BICField(null=True, blank=True, verbose_name="BIC") mandate_state = models.CharField( choices=[ ("inactive", _("Inactive")), ("active", _("Active")), ("bounced", _("Bounced")), ("rescinded", _("Rescinded")), ], default="active", max_length=10, blank=False, null=False, verbose_name=_("Mandate state"), ) institute = models.CharField( max_length=255, null=True, blank=True, verbose_name=_("IBAN Institute") ) issue_date = models.DateField( null=True, blank=True, verbose_name=_("IBAN Issue Date"), help_text=_( "The issue date of the direct debit mandate. (1970-01-01 means there is no issue date in the database )" ), ) fullname = models.CharField( null=True, blank=True, max_length=255, verbose_name=_("IBAN full name"), help_text=_("Full name for IBAN account owner"), ) address = models.CharField( null=True, blank=True, max_length=255, verbose_name=_("IBAN address"), help_text=_("Address line (e.g. Street / House Number)"), ) zip_code = models.CharField( null=True, blank=True, max_length=20, verbose_name=_("IBAN zip code"), help_text=_("ZIP Code"), ) city = models.CharField( null=True, blank=True, max_length=255, verbose_name=_("IBAN City") ) country = models.CharField( null=True, blank=True, max_length=255, default="Deutschland", verbose_name=_("IBAN Country"), ) mandate_reference = models.CharField( null=True, blank=True, max_length=255, verbose_name=_("IBAN Mandate Reference") ) mandate_reason = models.CharField( null=True, blank=True, max_length=255, verbose_name=_("IBAN Mandate Reason") ) form_title = _("SEPA information") @property def is_usable(self): return self.sepa_direct_debit_state == SepaDirectDebitState.OK @property def iban_parsed(self): with suppress(ValueError): if self.iban: return IBAN(self.iban) return None @property def bic_autocomplete(self): if self.bic: return self.bic iban_parsed = self.iban_parsed if not iban_parsed: return None with suppress(ValueError): return str(iban_parsed.bic) return None @property def bic_parsed(self): with suppress(ValueError): bic = self.bic_autocomplete if bic: return BIC(bic) return None @property def sepa_direct_debit_state(self): if not self.iban: return SepaDirectDebitState.NO_IBAN if not self.iban_parsed: return SepaDirectDebitState.INVALID_IBAN if self.mandate_state == "rescinded": return SepaDirectDebitState.RESCINDED if self.mandate_state == "bounced": return SepaDirectDebitState.BOUNCED if self.mandate_state == "inactive": return SepaDirectDebitState.INACTIVE if not self.bic_autocomplete: return SepaDirectDebitState.NO_BIC bic = self.bic_parsed if not bic: return SepaDirectDebitState.INVALID_BIC if bic.country_code == "DE" and not bic.exists: # PBNKDEFF and PBNKDEFFXXX should be the same b_ = BIC(str(bic) + "XXX") if not b_.exists: return SepaDirectDebitState.INVALID_BIC if not self.mandate_reference: return SepaDirectDebitState.NO_MANDATE_REFERENCE return SepaDirectDebitState.OK
class DirectDebitBatch(models.Model): ''' This model is used to process multiple SEPA DD transactions together. This is typically achieved by generating a SEPA XML file. ''' uuid = models.UUIDField(_('UUID'), default=uuid.uuid4) creditor_id = models.CharField(_('creditor id'), max_length=20, default=get_default_creditor_id) creditor_name = models.CharField(_('creditor name'), max_length=70, default=get_default_creditor_name) creditor_country = models.CharField(_('creditor country'), max_length=2, default=get_default_creditor_country) creditor_iban = IBANField(_('creditor IBAN'), default=get_default_creditor_iban) creditor_bic = BICField(_('creditor BIC'), default=get_default_creditor_bic) due_date = models.DateField( _('due date'), help_text= _('Must be min. 5 TARGET dates in the future for the first transaction and 2 target days in the future for recurring transactions.' )) mandate_type = models.CharField(_('mandate type'), max_length=4, choices=DirectDebitMandate.TYPE_CHOICES) SEQUENCE_TYPE_FRST = "FRST" SEQUENCE_TYPE_RCUR = "RCUR" SEQUENCE_TYPE_CHOICES = ( (SEQUENCE_TYPE_FRST, 'FRST'), (SEQUENCE_TYPE_RCUR, 'RCUR'), ) sequence_type = models.CharField(_('sequence type'), max_length=4, choices=SEQUENCE_TYPE_CHOICES) created = models.DateTimeField(_('created'), auto_now_add=True) executed = models.DateTimeField(_('executed'), blank=True, null=True) @property def transactions(self): return list(self.directdebittransaction_set.all()) def render_sepa_xml(self): ''' Create SEPA XML document according to ISO20222. ''' dd = sepaxml.DirectDebit( id=self.uuid.hex, creditor_id=self.creditor_id, creditor_name=self.creditor_name, creditor_country=self.creditor_country, creditor_iban=self.creditor_iban, creditor_bic=self.creditor_bic, due_date=self.due_date, mandate_type=self.mandate_type, sequence_type=self.sequence_type, transactions=[ sepaxml.Transaction(debitor_name=txn.mandate.name, debitor_country=txn.mandate.country, debitor_iban=txn.mandate.iban, debitor_bic=txn.mandate.bic, reference=settings.SHARK['SEPA'] ['TRANSACTION_REFERENCE_PREFIX'] + txn.reference, amount=txn.amount, mandate_id=txn.mandate.reference, mandate_date=txn.mandate.signed) for txn in self.directdebittransaction_set.all() ]) return dd.render_xml()
class BankAccount(models.Model): """ Describes a bank account """ id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) created_at = models.DateTimeField(_("created at"), default=timezone.now) last_used = models.DateField( verbose_name=_("last used"), blank=True, null=True, ) owner = models.ForeignKey( to="members.Member", verbose_name=_("owner"), related_name="bank_accounts", on_delete=models.SET_NULL, blank=False, null=True, ) initials = models.CharField( verbose_name=_("initials"), max_length=20, ) last_name = models.CharField( verbose_name=_("last name"), max_length=255, ) iban = IBANField( verbose_name=_("IBAN"), include_countries=IBAN_SEPA_COUNTRIES, ) bic = BICField( verbose_name=_("BIC"), blank=True, null=True, help_text=_("This field is optional for Dutch bank accounts."), ) valid_from = models.DateField( verbose_name=_("valid from"), blank=True, null=True, ) valid_until = models.DateField( verbose_name=_("valid until"), blank=True, null=True, ) signature = models.TextField( verbose_name=_("signature"), blank=True, null=True, ) mandate_no = models.CharField( verbose_name=_("mandate number"), max_length=255, blank=True, null=True, unique=True, ) def clean(self): super().clean() errors = {} if self.bic is None and self.iban[0:2] != "NL": errors.update({ "bic": _("This field is required for foreign bank accounts.") }) if not self.owner: errors.update({"owner": _("This field is required.")}) mandate_fields = [ ("valid_from", self.valid_from), ("signature", self.signature), ("mandate_no", self.mandate_no), ] if any(not field[1] for field in mandate_fields) and any( field[1] for field in mandate_fields): for field in mandate_fields: if not field[1]: errors.update({ field[0]: _("This field is required " "to complete the mandate.") }) if self.valid_from and self.valid_until and self.valid_from > self.valid_until: errors.update({ "valid_until": _("This date cannot be before the from date.") }) if self.valid_until and not self.valid_from: errors.update( {"valid_until": _("This field cannot have a value.")}) if errors: raise ValidationError(errors) @property def name(self): return f"{self.initials} {self.last_name}" @property def valid(self): if self.valid_from is not None and self.valid_until is not None: return self.valid_from <= timezone.now().date() < self.valid_until return self.valid_from is not None and self.valid_from <= timezone.now( ).date() def __str__(self): return f"{self.iban} - {self.name}" class Meta: ordering = ("created_at", )
class MangoPayBankAccount(models.Model): mangopay_user = models.ForeignKey(MangoPayUser, related_name="mangopay_bank_accounts") mangopay_id = models.PositiveIntegerField(null=True, blank=True) address = models.CharField(max_length=254) account_type = models.CharField( max_length=2, choices=MANGOPAY_BANKACCOUNT_TYPE, default=BA_BIC_IBAN) # Defaults to BIC/IBAN type iban = IBANField(blank=True, null=True) bic = BICField(blank=True, null=True) country = CountryField(null=True, blank=True) account_number = models.CharField(max_length=15, null=True, blank=True) # BA_US type only fields aba = models.CharField(max_length=9, null=True, blank=True) deposit_account_type = models.CharField( choices=BA_US_DEPOSIT_ACCOUNT_TYPES, max_length=8, null=True, blank=True, ) def create(self): client = get_mangopay_api_client() mangopay_bank_account = BankAccount() mangopay_bank_account.UserId = self.mangopay_user.mangopay_id mangopay_bank_account.OwnerName = \ self.mangopay_user.user.get_full_name() mangopay_bank_account.OwnerAddress = unicode(self.address) if self.account_type == BA_BIC_IBAN: # BIC / IBAN type requires setting IBAN and BIC codes only mangopay_bank_account.Details = BankAccountDetailsIBAN() mangopay_bank_account.Details.Type = "IBAN" mangopay_bank_account.Details.IBAN = self.iban elif self.account_type == BA_US: mangopay_bank_account.Details = BankAccountDetailsUS() mangopay_bank_account.Details.Type = "US" mangopay_bank_account.Details.ABA = self.aba mangopay_bank_account.Details.DepositAccountType = \ self.deposit_account_type mangopay_bank_account.Details.AccountNumber = self.account_number elif self.account_type == BA_OTHER: # OTHER type requires setting Details object with Account number # country and BIC code. mangopay_bank_account.Details = BankAccountDetailsOTHER() mangopay_bank_account.Details.Type = "OTHER" mangopay_bank_account.Details.AccountNumber = self.account_number else: if self.account_type in BA_NOT_IMPLEMENTED: raise NotImplementedError( "Bank Account Type ({0}) not implemeneted.".format( self.account_type)) else: raise Exception("Bank Account Type ({0}) is not valid.".format( self.account_type)) # Shared Details for IBAN and Other mangopay_bank_account.Details.BIC = self.bic if self.country: mangopay_bank_account.Details.Country = self.country.code else: if self.account_type != BA_BIC_IBAN: raise Exception("Country is required for Bank Accounts of " "types other than BIC/IBAN") created_bank_account = client.users.CreateBankAccount( str(self.mangopay_user.mangopay_id), mangopay_bank_account) self.mangopay_id = created_bank_account.Id self.save()