Migrate to new bookkeeping data model

This commit is contained in:
Henryk Plötz 2018-07-08 22:07:59 +02:00 committed by Tobias Kunze
parent 00d0a268a9
commit 9b5d431800
3 changed files with 70 additions and 36 deletions

View File

@ -1,11 +1,11 @@
from django.db.models import Q from os import path
from django.conf import settings from django.conf import settings
from django.core.management import BaseCommand from django.core.management import BaseCommand
from django.db.models import Q
from django.template.loader import get_template from django.template.loader import get_template
from django.utils import timezone from django.utils import timezone
from os import path
class Command(BaseCommand): class Command(BaseCommand):
@ -34,4 +34,3 @@ class Command(BaseCommand):
with open(path.join(settings.BASE_DIR, f'authorized_keys.{task}'), 'w') as f: with open(path.join(settings.BASE_DIR, f'authorized_keys.{task}'), 'w') as f:
f.write(content) f.write(content)

View File

@ -24,18 +24,18 @@ def bank_transaction_csv_file():
@pytest.mark.django_db @pytest.mark.django_db
def test_bank_import(bank_transaction_csv_file): 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) process_bank_csv(bank_transaction_csv_file, None)
bank_transaction_csv_file.refresh_from_db() 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 @pytest.mark.django_db
def test_bank_import_no_duplicates(bank_transaction_csv_file): 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) process_bank_csv(bank_transaction_csv_file, None)
bank_transaction_csv_file.refresh_from_db() 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) process_bank_csv(bank_transaction_csv_file, None)
bank_transaction_csv_file.refresh_from_db() bank_transaction_csv_file.refresh_from_db()
assert bank_transaction_csv_file.transactions.count() == 6 assert bank_transaction_csv_file.bookings.count() == 6

View File

@ -4,17 +4,16 @@ import re
from datetime import datetime from datetime import datetime
from decimal import Decimal from decimal import Decimal
from django.conf import settings
from django.dispatch import receiver from django.dispatch import receiver
from django.utils.timezone import now from django.utils.timezone import now
from django.conf import settings
from byro.bookkeeping.models import ( from byro.bookkeeping.models import (
Account, AccountCategory, RealTransaction, Account, AccountCategory, Booking, Transaction,
TransactionChannel, VirtualTransaction,
)
from byro.bookkeeping.signals import (
derive_virtual_transactions, process_csv_upload,
) )
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 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) filename = os.path.join(settings.MEDIA_ROOT, source.source_file.name)
reader = csv.DictReader(open(filename, encoding='iso-8859-1'), delimiter=';', quotechar='"') reader = csv.DictReader(open(filename, encoding='iso-8859-1'), delimiter=';', quotechar='"')
booking_timestamp = now() booking_timestamp = now()
account = SpecialAccounts.bank
for line in reader: for line in reader:
if not line: if not line:
@ -33,45 +33,80 @@ def process_bank_csv(sender, signal, **kwargs):
if key.startswith('VWZ'): if key.startswith('VWZ'):
reference += line[key] + ' ' reference += line[key] + ' '
RealTransaction.objects.get_or_create( amount = Decimal(line.get('Betrag').replace('.', '').replace(',', '.'))
channel=TransactionChannel.BANK, if amount < 0:
value_datetime=datetime.strptime(line.get('Buchungstag'), '%d.%m.%Y'), amount = -amount
amount=Decimal(line.get('Betrag').replace('.', '').replace(',', '.')), booking_type = 'c'
purpose=reference, else:
originator=line.get('Auftraggeber/Empfänger', '<leer>'), booking_type = 'd'
params = dict(
memo=reference,
amount=amount,
importer='shack_bank_csv_importer', 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', '<leer>')),
}
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 return True
@receiver(derive_virtual_transactions) @receiver(process_transaction)
def match_transaction(sender, signal, **kwargs): def match_transaction(sender, signal, **kwargs):
transaction = sender 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 member = None
try: try:
member = Member.objects.get(number=uid) member = Member.objects.get(number=uid)
except Member.DoesNotExist: except Member.DoesNotExist:
return return False
account = Account.objects.get(account_category=AccountCategory.MEMBER_FEES) balances = transaction.balances
data = { data = {
'amount': transaction.amount, 'amount': abs(balances['debit'] - balances['credit']),
'destination_account': account, 'account': SpecialAccounts.fees_receivable,
'value_datetime': transaction.value_datetime,
'member': member, 'member': member,
} }
virtual_transaction = VirtualTransaction.objects.filter(**data).first()
if virtual_transaction and virtual_transaction.real_transaction != transaction: if balances['debit'] > balances['credit']:
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)) transaction.credit(**data)
if not virtual_transaction: else:
data['real_transaction'] = transaction transaction.debit(**data)
virtual_transaction = VirtualTransaction.objects.create(**data)
return [virtual_transaction] return True
def reference_parser(reference): def reference_parser(reference):
if not reference:
return (False, 99)
reference = reference.lower() reference = reference.lower()
regexes = ( regexes = (