Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lookup missing BIC when returning bank account activities #739

Merged
merged 1 commit into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion pycroft/lib/finance/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@
get_pid_csv,
)
from .retransfer import (
get_activities_to_return,
attribute_activities_as_returned,
generate_activities_return_sepaxml,
get_activities_to_return,
)
from .transaction_crud import (
simple_transaction,
Expand Down
31 changes: 29 additions & 2 deletions pycroft/lib/finance/retransfer.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
from collections.abc import Sequence
from datetime import datetime, timedelta

from schwifty import IBAN
from sepaxml import SepaTransfer
from sqlalchemy import select
from sqlalchemy.orm import joinedload, Session
from sqlalchemy.orm import Session, joinedload

from pycroft import config
from pycroft.helpers.utc import ensure_tz
from pycroft.model.finance import BankAccountActivity
from pycroft.model.user import User

from .transaction_crud import simple_transaction


def get_activities_to_return(session: Session) -> Sequence[BankAccountActivity]:
Expand All @@ -33,10 +37,11 @@ def generate_activities_return_sepaxml(activities: list[BankAccountActivity]) ->
sepa = SepaTransfer(transfer_config, clean=False)

for activity in activities:
bic = activity.other_routing_number or IBAN(activity.other_account_number).bic.compact
payment = {
"name": activity.other_name,
"IBAN": activity.other_account_number,
"BIC": activity.other_routing_number,
"BIC": bic,
"amount": int(activity.amount * 100),
"execution_date": datetime.now().date(),
"description": f"Rücküberweisung nicht zuordenbarer Überweisung vom {activity.posted_on} mit Referenz {activity.reference}"[
Expand All @@ -46,3 +51,25 @@ def generate_activities_return_sepaxml(activities: list[BankAccountActivity]) ->
sepa.add_payment(payment)

return sepa.export()


def attribute_activities_as_returned(
session: Session, activities: list[BankAccountActivity], author: User
) -> None:
for activity in activities:
debit_account = config.non_attributable_transactions_account
credit_account = activity.bank_account.account

transaction = simple_transaction(
description=activity.reference,
debit_account=debit_account,
credit_account=credit_account,
amount=activity.amount,
author=author,
valid_on=activity.valid_on,
confirmed=False,
)
activity.split = next(
split for split in transaction.splits if split.account_id == credit_account.id
)
session.add(activity)
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""add account for non-attributable transfers to config

Revision ID: 2d7e4df39a3b
Revises: bc0e0dd480d4
Create Date: 2024-06-06 20:21:16.972195

"""

import sqlalchemy as sa
from alembic import op

# revision identifiers, used by Alembic.
revision = "b64618e97415"
down_revision = "5234d7ac2b4a"
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column(
"config",
sa.Column(
"non_attributable_transactions_account_id",
sa.Integer(),
nullable=False,
server_default="33200",
),
)
op.create_foreign_key(
None, "config", "account", ["non_attributable_transactions_account_id"], ["id"]
)


def downgrade():
op.drop_constraint(None, "config", type_="foreignkey")
op.drop_column("config", "non_attributable_transactions_account_id")
op.create_index(
"bank_account_activity_imported_at", "bank_account_activity", ["imported_at"], unique=False
)
5 changes: 5 additions & 0 deletions pycroft/model/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ class Config(IntegerIdModel):
foreign_keys=[membership_fee_bank_account_id]
)

non_attributable_transactions_account_id: Mapped[int] = col(ForeignKey(Account.id))
non_attributable_transactions_account: Mapped[Account] = relationship(
foreign_keys=[non_attributable_transactions_account_id]
)
FestplattenSchnitzel marked this conversation as resolved.
Show resolved Hide resolved

fints_product_id: Mapped[str | None]

__table_args__ = (CheckConstraint("id = 1"),)
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ dependencies = [
"python-dotenv ~= 0.21.0",
"reportlab ~= 4.2.5", # usersheet generation
"rich ~= 13.8.0",
"schwifty ~= 2024.9.0",
"sentry-sdk[Flask] ~= 1.29.2",
"simplejson ~= 3.11.1", # decimal serialization
"SQLAlchemy >= 2.0.1",
Expand Down
12 changes: 12 additions & 0 deletions requirements.dev.txt

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions requirements.prod.txt

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions requirements.txt

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions tests/factories/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@ class Meta:
# `Account`s
membership_fee_account = SubFactory(AccountFactory, type="REVENUE")
membership_fee_bank_account = SubFactory(BankAccountFactory)
non_attributable_transactions_account = SubFactory(AccountFactory, type="REVENUE")
20 changes: 15 additions & 5 deletions web/blueprints/finance/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
match_activities,
get_activities_to_return,
generate_activities_return_sepaxml,
attribute_activities_as_returned,
get_all_bank_accounts,
get_unassigned_bank_account_activities,
get_all_mt940_errors,
Expand Down Expand Up @@ -751,12 +752,21 @@ def bank_account_activities_return_do() -> ResponseReturnValue:

form: t.Any = _create_form(field_list)()

if form.validate_on_submit():
selected_activities: list[BankAccountActivity] = [
activity for activity in activities_to_return if form[str(activity.id)].data
]
if not form.validate_on_submit():
return render_template(
"finance/bank_account_activities_return.html",
form=form(),
activities=activities_to_return,
)

sepa_xml: bytes = generate_activities_return_sepaxml(selected_activities)
selected_activities: list[BankAccountActivity] = [
activity for activity in activities_to_return if form[str(activity.id)].data
]

sepa_xml: bytes = generate_activities_return_sepaxml(selected_activities)

attribute_activities_as_returned(session, selected_activities, current_user)
session.commit()

return send_file(
BytesIO(sepa_xml),
Expand Down
2 changes: 1 addition & 1 deletion web/templates/finance/bank_account_activities_return.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

<div class="row">
<div class="col-md-12">
<button type="submit" class="btn btn-primary">SEPA-XML generieren</button>
<button type="submit" class="btn btn-primary">Überweisungen unbestätigt als unzuordenbar buchen und SEPA-XML exportieren</button>
</div>
</div>
</form>
Expand Down
Loading