def transfer(source, destination, amount, parent=None, user=None, merchant_reference=None, description=None): """ Transfer funds between source and destination accounts. Will raise a accounts.exceptions.AccountException if anything goes wrong. :source: Account to debit :destination: Account to credit :amount: Amount to transfer :parent: Parent transfer to reference :user: Authorising user :merchant_reference: An optional merchant ref associated with this transfer :description: Description of transaction """ if source.id == destination.id: raise exceptions.AccountException( "The source and destination accounts for a transfer " "must be different.") msg = "Transfer of %.2f from account #%d to account #%d" % ( amount, source.id, destination.id) if user: msg += " authorised by user #%d (%s)" % (user.id, user.get_username()) if description: msg += " '%s'" % description try: transfer = Transfer.objects.create(source, destination, amount, parent, user, merchant_reference, description) for transaction in transfer.transactions.all(): AccountDispatcher().send_email_for_transaction( transaction=transaction, extra_context=None) except exceptions.AccountException as e: logger.warning("%s - failed: '%s'", msg, e) raise except Exception as e: logger.error("%s - failed: '%s'", msg, e) raise exceptions.AccountException("Unable to complete transfer: %s" % e) else: logger.info("%s - successful, transfer: %s", msg, transfer.reference) return transfer
def reverse(transfer, user=None, merchant_reference=None, description=None): """ Reverse a previous transfer, returning the money to the original source. """ msg = "Reverse of transfer #%d" % transfer.id if user: msg += " authorised by user #%d (%s)" % (user.id, user.get_username()) if description: msg += " '%s'" % description try: transfer = Transfer.objects.create( source=transfer.destination, destination=transfer.source, amount=transfer.amount, user=user, merchant_reference=merchant_reference, description=description) except exceptions.AccountException as e: logger.warning("%s - failed: '%s'", msg, e) raise except Exception as e: logger.error("%s - failed: '%s'", msg, e) raise exceptions.AccountException("Unable to reverse transfer: %s" % e) else: logger.info("%s - successful, transfer: %s", msg, transfer.reference) return transfer
def verify_transfer(self, source, destination, amount, user=None): """ Test whether the proposed transaction is permitted. Raise an exception if not. """ if amount <= 0: raise exceptions.InvalidAmount("Debits must use a positive amount") if not source.is_open(): raise exceptions.ClosedAccount("Source account has been closed") if not source.can_be_authorised_by(user): raise exceptions.AccountException( "This user is not authorised to make transfers from " "this account" ) if not destination.is_open(): raise exceptions.ClosedAccount("Destination account has been closed") if not source.is_debit_permitted(amount): msg = "Unable to debit %.2f from account #%d:" raise exceptions.InsufficientFunds(msg % (amount, source.id))