from finance.models import Account, Transaction, Transfer from .base import DomainError, identity, lock, money, save, write from .posting import record def match(outgoing_id, incoming_id): ids = [identity(outgoing_id), identity(incoming_id)] if ids[0] == ids[1]: raise DomainError("invalid_transfer_legs") rows = {obj.pk: obj for obj in Transaction.objects.select_for_update().filter(pk__in=ids).order_by("pk")} if len(rows) != 2: raise DomainError("not_found") outgoing, incoming = rows[ids[0]], rows[ids[1]] if outgoing.kind != "TRANSFER" or incoming.kind != "TRANSFER" or outgoing.amount >= 0 or incoming.amount <= 0 or outgoing.amount != incoming.amount.copy_negate(): raise DomainError("invalid_transfer_legs") if outgoing.account_id == incoming.account_id or outgoing.account.currency != incoming.account.currency: raise DomainError("transfer_account_or_currency_mismatch") if Transfer.objects.filter(outgoing=outgoing).exists() or Transfer.objects.filter(incoming=incoming).exists(): raise DomainError("already_paired") return save(Transfer(outgoing=outgoing, incoming=incoming)) @write("match_transfer") def match_transfer(*, outgoing_id, incoming_id): return match(outgoing_id, incoming_id), ["outgoing", "incoming"] @write("create_transfer") def create_transfer(*, outgoing_account_id, incoming_account_id, amount, outgoing_date, incoming_date): amount = money(amount) if amount <= 0: raise DomainError("transfer_amount_must_be_positive") ids = [identity(outgoing_account_id), identity(incoming_account_id)] if ids[0] == ids[1]: raise DomainError("transfer_account_or_currency_mismatch") accounts = {obj.pk: obj for obj in Account.objects.select_for_update().filter(pk__in=ids).order_by("pk")} if len(accounts) != 2: raise DomainError("not_found") if any(obj.archived for obj in accounts.values()): raise DomainError("archived_reference") if accounts[ids[0]].currency != accounts[ids[1]].currency: raise DomainError("transfer_account_or_currency_mismatch") outgoing = record(account_id=ids[0], amount=amount.copy_negate(), posting_date=outgoing_date, kind="TRANSFER") incoming = record(account_id=ids[1], amount=amount, posting_date=incoming_date, kind="TRANSFER") return match(outgoing.pk, incoming.pk), ["outgoing", "incoming"]