2 from django.db import models
3 from django.utils.timezone import now
4 from .bank import parse_export_feedback, parse_payment_feedback
7 class Campaign(models.Model):
8 name = models.CharField('nazwa', max_length=255, unique=True)
9 description = models.TextField('opis', blank=True)
12 verbose_name = 'kampania'
13 verbose_name_plural = 'kampanie'
19 class Fundraiser(models.Model):
20 name = models.CharField('imię i nazwisko', max_length=255, unique=True)
23 verbose_name = 'fundraiser'
24 verbose_name_plural = 'fundraiserki i fundraiserzy'
30 class DirectDebit(models.Model):
31 first_name = models.CharField('imię', max_length=255, blank=True)
32 last_name = models.CharField('nazwisko', max_length=255, blank=True)
33 sex = models.CharField('płeć', max_length=1, blank=True, choices=[
37 date_of_birth = models.DateField('data urodzenia', null=True, blank=True)
38 street = models.CharField('ulica', max_length=255, blank=True)
39 building = models.CharField('nr domu', max_length=255, blank=True)
40 flat = models.CharField('nr mieszkania', max_length=255, blank=True)
41 town = models.CharField('miejscowość', max_length=255, blank=True)
42 postal_code = models.CharField('kod pocztowy', max_length=255, blank=True)
43 phone = models.CharField('telefon', max_length=255, blank=True)
44 email = models.CharField('e-mail', max_length=255, blank=True)
45 iban = models.CharField('nr rachunku', max_length=255, blank=True)
46 iban_valid = models.BooleanField('prawidłowy IBAN', default=False, null=True)
47 is_consumer = models.BooleanField('konsument', default=True)
48 payment_id = models.CharField('identyfikator płatności', max_length=255, blank=True, unique=True)
49 agree_fundraising = models.BooleanField('zgoda na kontakt fundraisingowy', default=False)
50 agree_newsletter = models.BooleanField('zgoda na newsletter', default=False)
52 acquisition_date = models.DateField(
53 'data pozyskania', help_text='Data z formularza',
54 null=True, blank=True)
55 submission_date = models.DateField(
56 'data dostarczenia', null=True, blank=True,
57 help_text='Data złożenia formularza przez fundraisera')
58 bank_submission_date = models.DateField(
59 'data złożenia do banku', null=True, blank=True,
60 help_text='Data przesłania danych z formularza do banku')
61 bank_acceptance_date = models.DateField(
62 'data akceptacji przez bank', null=True, blank=True,
63 help_text='Data kiedy bank przekazał informację o akceptacji danych z formularza')
65 fundraiser = models.ForeignKey(Fundraiser, models.PROTECT, blank=True, null=True, verbose_name='fundraiser')
66 fundraiser_commission = models.IntegerField('prowizja fundraisera', null=True, blank=True)
67 fundraiser_bonus = models.IntegerField('bonus fundraisera', null=True, blank=True)
68 fundraiser_bill = models.CharField('nr rachunku wystawionego przez fundraisera', max_length=255, blank=True)
70 amount = models.IntegerField('kwota', null=True, blank=True)
72 notes = models.TextField('uwagi', blank=True)
74 needs_redo = models.BooleanField('do powtórki', default=False)
75 cancelled_at = models.DateTimeField('anulowane', null=True, blank=True)
76 optout = models.BooleanField('optout', default=False)
78 campaign = models.ForeignKey(Campaign, models.PROTECT, null=True, blank=True, verbose_name='kampania')
80 latest_status = models.CharField(max_length=255, blank=True)
82 nosignature = models.BooleanField('Bez podpisu', default=False)
85 verbose_name = 'polecenie zapłaty'
86 verbose_name_plural = 'polecenia zapłaty'
89 return "{} {}".format(self.payment_id, self.latest_status)
91 def get_latest_status(self):
92 line = self.bankexportfeedbackline_set.order_by('-feedback__created_at').first()
93 if line is None: return ""
96 def save(self, **kwargs):
97 self.iban_valid = not self.iban_warning() if self.iban else None
98 self.latest_status = self.get_latest_status()
99 super().save(**kwargs)
102 def get_next_payment_id(cls):
103 # Find the last object added.
104 last = cls.objects.order_by('-id').first()
107 match = re.match(r'^(.*?)(\d+)$', last.payment_id)
110 prefix = match.group(1)
111 number = int(match.group(2))
112 number_length = len(match.group(2))
115 payment_id = f'{prefix}{number:0{number_length}}'
116 if not cls.objects.filter(payment_id=payment_id).exists():
122 return ' '.join((self.first_name, self.last_name)).strip()
125 def street_address(self):
126 street_addr = self.street
128 street_addr += ' ' + self.building
130 street_addr += ' m. ' + self.flat
131 street_addr = street_addr.strip()
134 def iban_warning(self):
137 if len(self.iban) != 26:
138 return 'Bad IBAN length'
139 if int(self.iban[2:] + '2521' + self.iban[:2]) % 97 != 1:
140 return 'This IBAN number looks invalid'
142 iban_warning.short_description = ''
145 class BankExportFeedback(models.Model):
146 created_at = models.DateTimeField(auto_now_add=True)
147 csv = models.FileField(upload_to='pz/feedback/')
149 def save(self, **kwargs):
150 super().save(**kwargs)
152 self.save_payment_items()
153 except AssertionError:
154 self.save_export_feedback_items()
156 def save_payment_items(self):
157 for payment_id, booking_date, is_dd, realised, reject_code in parse_payment_feedback(self.csv.open()):
158 debit = DirectDebit.objects.get(payment_id = payment_id)
159 b, created = self.payment_set.get_or_create(
162 'booking_date': booking_date,
164 'realised': realised,
165 'reject_code': reject_code,
169 b.booking_date = booking_date
171 b.realised = realised
172 b.reject_code = reject_code
175 def save_export_feedback_items(self):
176 for payment_id, status, comment in parse_export_feedback(self.csv.open()):
177 debit = DirectDebit.objects.get(payment_id = payment_id)
178 b, created = self.bankexportfeedbackline_set.get_or_create(
189 if status == 1 and not debit.bank_acceptance_date:
190 debit.bank_acceptance_date = now().date()
194 class BankExportFeedbackLine(models.Model):
195 feedback = models.ForeignKey(BankExportFeedback, models.CASCADE)
196 debit = models.ForeignKey(DirectDebit, models.CASCADE)
197 status = models.SmallIntegerField()
198 comment = models.CharField(max_length=255)
201 class Payment(models.Model):
202 feedback = models.ForeignKey(BankExportFeedback, models.CASCADE)
203 debit = models.ForeignKey(DirectDebit, models.CASCADE)
204 booking_date = models.DateField()
205 is_dd = models.BooleanField()
206 realised = models.BooleanField()
207 reject_code = models.CharField(max_length=128, blank=True)
211 class BankOrder(models.Model):
212 payment_date = models.DateField(null=True, blank=True)
213 sent = models.DateTimeField(null=True, blank=True)
214 debits = models.ManyToManyField(DirectDebit, blank=True)