Migrate to new bookkeeping data model
This commit is contained in:
parent
00d0a268a9
commit
9b5d431800
@ -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)
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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 = (
|
||||||
|
Loading…
Reference in New Issue
Block a user