From 9b5d431800260420f4a6df344bb1861db0eec36b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henryk=20Pl=C3=B6tz?= Date: Sun, 8 Jul 2018 22:07:59 +0200 Subject: [PATCH] Migrate to new bookkeeping data model --- .../management/commands/export_keyholder.py | 7 +- byro_shackspace/tests/test_bank_import.py | 10 +-- byro_shackspace/utils.py | 89 +++++++++++++------ 3 files changed, 70 insertions(+), 36 deletions(-) diff --git a/byro_shackspace/management/commands/export_keyholder.py b/byro_shackspace/management/commands/export_keyholder.py index c24059f..451c0d3 100644 --- a/byro_shackspace/management/commands/export_keyholder.py +++ b/byro_shackspace/management/commands/export_keyholder.py @@ -1,11 +1,11 @@ -from django.db.models import Q +from os import path + from django.conf import settings from django.core.management import BaseCommand +from django.db.models import Q from django.template.loader import get_template from django.utils import timezone -from os import path - class Command(BaseCommand): @@ -34,4 +34,3 @@ class Command(BaseCommand): with open(path.join(settings.BASE_DIR, f'authorized_keys.{task}'), 'w') as f: f.write(content) - diff --git a/byro_shackspace/tests/test_bank_import.py b/byro_shackspace/tests/test_bank_import.py index 10830b8..4e962ad 100644 --- a/byro_shackspace/tests/test_bank_import.py +++ b/byro_shackspace/tests/test_bank_import.py @@ -24,18 +24,18 @@ def bank_transaction_csv_file(): @pytest.mark.django_db def test_bank_import(bank_transaction_csv_file): - assert bank_transaction_csv_file.transactions.count() == 0 + assert bank_transaction_csv_file.bookings.count() == 0 process_bank_csv(bank_transaction_csv_file, None) bank_transaction_csv_file.refresh_from_db() - assert bank_transaction_csv_file.transactions.count() == 6 + assert bank_transaction_csv_file.bookings.count() == 6 @pytest.mark.django_db def test_bank_import_no_duplicates(bank_transaction_csv_file): - assert bank_transaction_csv_file.transactions.count() == 0 + assert bank_transaction_csv_file.bookings.count() == 0 process_bank_csv(bank_transaction_csv_file, None) bank_transaction_csv_file.refresh_from_db() - assert bank_transaction_csv_file.transactions.count() == 6 + assert bank_transaction_csv_file.bookings.count() == 6 process_bank_csv(bank_transaction_csv_file, None) bank_transaction_csv_file.refresh_from_db() - assert bank_transaction_csv_file.transactions.count() == 6 + assert bank_transaction_csv_file.bookings.count() == 6 diff --git a/byro_shackspace/utils.py b/byro_shackspace/utils.py index d517323..874a120 100644 --- a/byro_shackspace/utils.py +++ b/byro_shackspace/utils.py @@ -4,17 +4,16 @@ import re from datetime import datetime from decimal import Decimal +from django.conf import settings from django.dispatch import receiver from django.utils.timezone import now -from django.conf import settings from byro.bookkeeping.models import ( - Account, AccountCategory, RealTransaction, - TransactionChannel, VirtualTransaction, -) -from byro.bookkeeping.signals import ( - derive_virtual_transactions, process_csv_upload, + Account, AccountCategory, Booking, Transaction, ) +from byro.bookkeeping.signals import process_csv_upload, process_transaction +from byro.bookkeeping.special_accounts import SpecialAccounts +from byro.common.models import Configuration from byro.members.models import Member @@ -24,6 +23,7 @@ def process_bank_csv(sender, signal, **kwargs): filename = os.path.join(settings.MEDIA_ROOT, source.source_file.name) reader = csv.DictReader(open(filename, encoding='iso-8859-1'), delimiter=';', quotechar='"') booking_timestamp = now() + account = SpecialAccounts.bank for line in reader: if not line: @@ -33,45 +33,80 @@ def process_bank_csv(sender, signal, **kwargs): if key.startswith('VWZ'): reference += line[key] + ' ' - RealTransaction.objects.get_or_create( - channel=TransactionChannel.BANK, - value_datetime=datetime.strptime(line.get('Buchungstag'), '%d.%m.%Y'), - amount=Decimal(line.get('Betrag').replace('.', '').replace(',', '.')), - purpose=reference, - originator=line.get('Auftraggeber/Empfänger', ''), + amount = Decimal(line.get('Betrag').replace('.', '').replace(',', '.')) + if amount < 0: + amount = -amount + booking_type = 'c' + else: + booking_type = 'd' + + params = dict( + memo=reference, + amount=amount, importer='shack_bank_csv_importer', - defaults={'source': source, 'booking_datetime': booking_timestamp, 'data': line}, ) + data = { + 'csv_line': line, + 'other_party': "{}".format(line.get('Auftraggeber/Empfänger', '')), + } + + if booking_type == 'c': + params['credit_account'] = account + else: + params['debit_account'] = account + + booking = account.bookings.filter( + transaction__value_datetime=datetime.strptime(line.get('Buchungstag'), '%d.%m.%Y'), + **params + ).first() + + if not booking: + t = Transaction.objects.create( + value_datetime=datetime.strptime(line.get('Buchungstag'), '%d.%m.%Y'), + ) + Booking.objects.create( + transaction=t, + booking_datetime=booking_timestamp, + source=source, + data=data, + **params + ) return True -@receiver(derive_virtual_transactions) +@receiver(process_transaction) def match_transaction(sender, signal, **kwargs): transaction = sender - uid, score = reference_parser(reference=transaction.purpose) + if transaction.is_read_only: + return False + if transaction.is_balanced: + return False + + uid, score = reference_parser(reference=transaction.find_memo()) member = None try: member = Member.objects.get(number=uid) except Member.DoesNotExist: - return + return False - account = Account.objects.get(account_category=AccountCategory.MEMBER_FEES) + balances = transaction.balances data = { - 'amount': transaction.amount, - 'destination_account': account, - 'value_datetime': transaction.value_datetime, + 'amount': abs(balances['debit'] - balances['credit']), + 'account': SpecialAccounts.fees_receivable, 'member': member, } - virtual_transaction = VirtualTransaction.objects.filter(**data).first() - if virtual_transaction and virtual_transaction.real_transaction != transaction: - raise Exception('RealTransaction {transaction.id} cannot be matched! There is already a VirtualTransaction ({virtual_transaction.id}) that is too similar. It is matched to RealTransaction {virtual_transaction.real_transaction.id}.'.format(transaction=transaction, virtual_transaction=virtual_transaction)) - if not virtual_transaction: - data['real_transaction'] = transaction - virtual_transaction = VirtualTransaction.objects.create(**data) - return [virtual_transaction] + + if balances['debit'] > balances['credit']: + transaction.credit(**data) + else: + transaction.debit(**data) + + return True def reference_parser(reference): + if not reference: + return (False, 99) reference = reference.lower() regexes = (