2 from django.db import models
3 from django.utils.timezone import now
4 from django.utils.translation import gettext_lazy as _
5 from .bank import parse_export_feedback, parse_payment_feedback
8 class Campaign(models.Model):
9 name = models.CharField(_('name'), max_length=255, unique=True)
10 description = models.TextField(_('description'), blank=True)
13 verbose_name = _('campaign')
14 verbose_name_plural = _('campaigns')
20 class Fundraiser(models.Model):
21 name = models.CharField(_('name'), max_length=255, unique=True)
24 verbose_name = _('fundraiser')
25 verbose_name_plural = _('fundraisers')
31 class DirectDebit(models.Model):
32 first_name = models.CharField(_('first name'), max_length=255, blank=True)
33 last_name = models.CharField(_('last name'), max_length=255, blank=True)
34 sex = models.CharField(_('sex'), max_length=1, blank=True, choices=[
38 date_of_birth = models.DateField(_('date of birth'), null=True, blank=True)
39 street = models.CharField(_('street'), max_length=255, blank=True)
40 building = models.CharField(_('building'), max_length=255, blank=True)
41 flat = models.CharField(_('flat'), max_length=255, blank=True)
42 town = models.CharField(_('town'), max_length=255, blank=True)
43 postal_code = models.CharField(_('postal code'), max_length=255, blank=True)
44 phone = models.CharField(_('phone'), max_length=255, blank=True)
45 email = models.CharField(_('e-mail'), max_length=255, blank=True)
46 iban = models.CharField(_('IBAN'), max_length=255, blank=True)
47 iban_valid = models.BooleanField(_('IBAN valid'), default=False, null=True)
48 is_consumer = models.BooleanField(_('is a consumer'), default=True)
49 payment_id = models.CharField(_('payment identifier'), max_length=255, blank=True, unique=True)
50 agree_fundraising = models.BooleanField(_('agree fundraising'), default=False)
51 agree_newsletter = models.BooleanField(_('agree newsletter'), default=False)
53 acquisition_date = models.DateField(_('acquisition date'), help_text=_('Date from the form'), null=True, blank=True)
54 submission_date = models.DateField(_('submission date'), null=True, blank=True, help_text=_('Date the fundaiser submitted the form'))
55 bank_submission_date = models.DateField(_('bank submission date'), null=True, blank=True, help_text=_('Date when the form data is submitted to the bank'))
56 bank_acceptance_date = models.DateField(_('bank accepted date'), null=True, blank=True, help_text=_('Date when bank accepted the form'))
58 fundraiser = models.ForeignKey(Fundraiser, models.PROTECT, blank=True, null=True, verbose_name=_('fundraiser'))
59 fundraiser_commission = models.IntegerField(_('fundraiser commission'), null=True, blank=True)
60 fundraiser_bonus = models.IntegerField(_('fundraiser bonus'), null=True, blank=True)
61 fundraiser_bill = models.CharField(_('fundaiser bill number'), max_length=255, blank=True)
63 amount = models.IntegerField(_('amount'), null=True, blank=True)
65 notes = models.TextField(_('notes'), blank=True)
67 needs_redo = models.BooleanField(_('needs redo'), default=False)
68 cancelled_at = models.DateTimeField(_('cancelled at'), null=True, blank=True)
69 optout = models.BooleanField(_('optout'), default=False)
71 campaign = models.ForeignKey(Campaign, models.PROTECT, null=True, blank=True, verbose_name=_('campaign'))
73 latest_status = models.CharField(max_length=255, blank=True)
76 verbose_name = _('direct debit')
77 verbose_name_plural = _('direct debits')
80 return "{} {}".format(self.payment_id, self.latest_status)
82 def get_latest_status(self):
83 line = self.bankexportfeedbackline_set.order_by('-feedback__created_at').first()
84 if line is None: return ""
87 def save(self, **kwargs):
88 self.iban_valid = not self.iban_warning() if self.iban else None
89 self.latest_status = self.get_latest_status()
90 super().save(**kwargs)
93 def get_next_payment_id(cls):
94 # Find the last object added.
95 last = cls.objects.order_by('-id').first()
98 match = re.match(r'^(.*?)(\d+)$', last.payment_id)
101 prefix = match.group(1)
102 number = int(match.group(2))
103 number_length = len(match.group(2))
106 payment_id = f'{prefix}{number:0{number_length}}'
107 if not cls.objects.filter(payment_id=payment_id).exists():
113 return ' '.join((self.first_name, self.last_name)).strip()
116 def street_address(self):
117 street_addr = self.street
119 street_addr += ' ' + self.building
121 street_addr += ' m. ' + self.flat
122 street_addr = street_addr.strip()
125 def iban_warning(self):
128 if len(self.iban) != 26:
129 return 'Bad IBAN length'
130 if int(self.iban[2:] + '2521' + self.iban[:2]) % 97 != 1:
131 return 'This IBAN number looks invalid'
133 iban_warning.short_description = ''
136 class BankExportFeedback(models.Model):
137 created_at = models.DateTimeField(auto_now_add=True)
138 csv = models.FileField(upload_to='pz/feedback/')
140 def save(self, **kwargs):
141 super().save(**kwargs)
143 self.save_payment_items()
144 except AssertionError:
145 self.save_export_feedback_items()
147 def save_payment_items(self):
148 for payment_id, booking_date, is_dd, realised, reject_code in parse_payment_feedback(self.csv.open()):
149 debit = DirectDebit.objects.get(payment_id = payment_id)
150 b, created = self.payment_set.get_or_create(
153 'booking_date': booking_date,
155 'realised': realised,
156 'reject_code': reject_code,
160 b.booking_date = booking_date
162 b.realised = realised
163 b.reject_code = reject_code
166 def save_export_feedback_items(self):
167 for payment_id, status, comment in parse_export_feedback(self.csv.open()):
168 debit = DirectDebit.objects.get(payment_id = payment_id)
169 b, created = self.bankexportfeedbackline_set.get_or_create(
180 if status == 1 and not debit.bank_acceptance_date:
181 debit.bank_acceptance_date = now().date()
185 class BankExportFeedbackLine(models.Model):
186 feedback = models.ForeignKey(BankExportFeedback, models.CASCADE)
187 debit = models.ForeignKey(DirectDebit, models.CASCADE)
188 status = models.SmallIntegerField()
189 comment = models.CharField(max_length=255)
192 class Payment(models.Model):
193 feedback = models.ForeignKey(BankExportFeedback, models.CASCADE)
194 debit = models.ForeignKey(DirectDebit, models.CASCADE)
195 booking_date = models.DateField()
196 is_dd = models.BooleanField()
197 realised = models.BooleanField()
198 reject_code = models.CharField(max_length=128, blank=True)
202 class BankOrder(models.Model):
203 payment_date = models.DateField(null=True, blank=True)
204 sent = models.DateTimeField(null=True, blank=True)
205 debits = models.ManyToManyField(DirectDebit, blank=True)