Exemplo n.º 1
0
def test_transactions(random_transaction, bucket):

    (transaction, account1, account2) = random_transaction

    starting_balance1 = account1.balance()

    starting_balance2 = account2.balance()

    authorisation = Authorisation(resource=transaction.fingerprint(),
                                  testing_key=testing_key,
                                  testing_user_guid=account1.group_name())

    records = Ledger.perform(transaction=transaction,
                             debit_account=account1,
                             credit_account=account2,
                             authorisation=authorisation,
                             is_provisional=False,
                             bucket=bucket)

    assert (len(records) == 1)

    record = records[0]

    ending_balance1 = account1.balance()
    ending_balance2 = account2.balance()

    assert (ending_balance1 == starting_balance1 - transaction)
    assert (ending_balance2 == starting_balance2 + transaction)

    assert (record.debit_account_uid() == account1.uid())
    assert (record.credit_account_uid() == account2.uid())

    debit_note = record.debit_note()
    credit_note = record.credit_note()

    assert (debit_note.account_uid() == account1.uid())
    assert (credit_note.account_uid() == account2.uid())

    assert (not debit_note.is_provisional())
    assert (not credit_note.is_provisional())

    assert (debit_note.value() == transaction.value())
    assert (credit_note.value() == transaction.value())

    now = get_datetime_now()

    assert (debit_note.datetime() < now)
    assert (credit_note.datetime() < now)
    assert (debit_note.datetime() <= credit_note.datetime())

    assert_packable(debit_note)
    assert_packable(credit_note)

    # now test refunding this transaction
    # now receipt a random amount of the transaction
    authorisation = Authorisation(resource=credit_note.fingerprint(),
                                  testing_key=testing_key,
                                  testing_user_guid=account2.group_name())

    refund = Refund(credit_note, authorisation)

    assert (not refund.is_null())
    assert (refund.authorisation() == authorisation)
    assert (refund.value() == transaction.value())
    assert (refund.credit_note() == credit_note)
    assert_packable(refund)

    rrecords = Ledger.refund(refund)

    assert (len(rrecords) == 1)
    rrecord = rrecords[0]

    assert (not rrecord.is_null())
    assert_packable(rrecord)

    assert (not rrecord.is_provisional())
    assert (rrecord.is_direct())
    assert (rrecord.get_refund_info() == refund)
    assert (rrecord.is_refund())
    assert (rrecord.original_transaction() == transaction)

    # the original transaction record has now been updated to
    # say that it has been receipted...
    assert (record.is_direct())
    record.reload()
    assert (record.is_refunded())

    assert (rrecord.original_transaction_record() == record)

    ending_balance1 = account1.balance()
    ending_balance2 = account2.balance()

    assert (ending_balance1.liability() == starting_balance1.liability())
    assert (ending_balance2.receivable() == starting_balance2.receivable())
    assert (starting_balance1.balance() == ending_balance1.balance())
    assert (starting_balance2.balance() == ending_balance2.balance())
    assert (starting_balance2.liability() == ending_balance2.liability())
    assert (starting_balance1.receivable() == ending_balance1.receivable())
Exemplo n.º 2
0
def run(args):
    """This function is called to handle request to cash cheques. This
       will verify that the cheque is valid and will then create
       the debit/credit note pair for the transation. It will return
       the CreditNote to the caller so they can see that the funds have
       been reserved, and can receipt the transaction once goods/services
       have been delivered.

       Args:
            args (dict): information for payment for service

        Returns:
            dict: contains status, status message and credit note if valid

    """

    credit_notes = []

    try:
        cheque = args["cheque"]
    except:
        raise ValueError("You must supply a cheque to be cashed!")

    try:
        cheque = Cheque.from_data(cheque)
    except Exception as e:
        from Acquire.Service import exception_to_string
        raise TypeError("Unable to interpret the cheque.\n\nCAUSE: %s" %
                        exception_to_string(e))

    try:
        spend = args["spend"]
    except:
        spend = None

    if spend is not None:
        try:
            spend = string_to_decimal(spend)
        except Exception as e:
            from Acquire.Service import exception_to_string
            raise TypeError("Unable to interpret the spend.\n\nCause: %s" %
                            exception_to_string(e))

    try:
        resource = str(args["resource"])
    except:
        raise ValueError(
            "You must supply a string representing the resource that will "
            "be paid for using this cheque")

    try:
        account_uid = str(args["account_uid"])
    except:
        raise ValueError("You must supply the UID of the account to which the "
                         "cheque will be cashed")

    try:
        receipt_by = args["receipt_by"]
    except:
        raise ValueError(
            "You must supply the datetime by which you promise to "
            "receipt this transaction")

    try:
        receipt_by = string_to_datetime(receipt_by)
    except Exception as e:
        from Acquire.Service import exception_to_string
        raise TypeError(
            "Unable to interpret the receipt_by date.\n\nCAUSE: %s" %
            exception_to_string(e))

    # now read the cheque - this will only succeed if the cheque
    # is valid, has been signed, has been sent from the right
    # service, and was authorised by the user, the cheque
    # has not expired and we are the
    # service which holds the account from which funds are drawn
    info = cheque.read(resource=resource, spend=spend, receipt_by=receipt_by)

    try:
        description = str(args["description"])
    except:
        description = info["resource"]

    authorisation = info["authorisation"]
    auth_resource = info["auth_resource"]
    user_guid = authorisation.user_guid()

    # the cheque is valid
    bucket = get_service_account_bucket()

    try:
        debit_account = Account(uid=info["account_uid"], bucket=bucket)
    except Exception as e:
        from Acquire.Service import exception_to_string
        raise PaymentError("Cannot find the account associated with the cheque"
                           "\n\nCAUSE: %s" % exception_to_string(e))

    try:
        credit_account = Account(uid=account_uid, bucket=bucket)
    except Exception as e:
        from Acquire.Service import exception_to_string
        raise PaymentError(
            "Cannot find the account to which funds will be creditted:"
            "\n\nCAUSE: %s" % exception_to_string(e))

    # validate that this account is in a group that can be authorised
    # by the user (this should eventually go as the ACLs now allow users
    # to authorised payments from many accounts)
    accounts = Accounts(user_guid=user_guid)
    if not accounts.contains(account=debit_account, bucket=bucket):
        raise PermissionError(
            "The user with UID '%s' cannot authorise transactions from "
            "the account '%s' as they do not own this account." %
            (user_guid, str(debit_account)))

    transaction = Transaction(value=info["spend"], description=description)

    # we have enough information to perform the transaction
    # - this is provisional as the service must receipt everything
    transaction_records = Ledger.perform(transactions=transaction,
                                         debit_account=debit_account,
                                         credit_account=credit_account,
                                         authorisation=authorisation,
                                         authorisation_resource=auth_resource,
                                         is_provisional=True,
                                         receipt_by=receipt_by,
                                         bucket=bucket)

    # extract all of the credit notes to return to the user,
    # and also to record so that we can check if they have not
    # been receipted in time...
    credit_notes = []

    for record in transaction_records:
        credit_notes.append(record.credit_note())

    credit_notes = list_to_string(credit_notes)

    receipt_key = "accounting/cashed_cheque/%s" % info["uid"]
    mutex = Mutex(receipt_key, bucket=bucket)

    try:
        receipted = ObjectStore.get_object_from_json(bucket, receipt_key)
    except:
        receipted = None

    if receipted is not None:
        # we have tried to cash this cheque twice!
        mutex.unlock()
        Ledger.refund(transaction_records, bucket=bucket)
    else:
        info = {"status": "needs_receipt", "creditnotes": credit_notes}
        ObjectStore.set_object_from_json(bucket, receipt_key, info)
        mutex.unlock()

    return {"credit_notes": credit_notes}
Exemplo n.º 3
0
def test_transactions(random_transaction, bucket):
    (transaction, account1, account2) = random_transaction

    starting_balance1 = account1.balance()
    starting_liability1 = account1.liability()
    starting_receivable1 = account1.receivable()

    starting_balance2 = account2.balance()
    starting_liability2 = account2.liability()
    starting_receivable2 = account2.receivable()

    record = Ledger.perform(transaction,
                            account1,
                            account2,
                            Authorisation(),
                            is_provisional=False,
                            bucket=bucket)

    ending_balance1 = account1.balance()
    ending_liability1 = account1.liability()
    ending_receivable1 = account1.receivable()

    ending_balance2 = account2.balance()
    ending_liability2 = account2.liability()
    ending_receivable2 = account2.receivable()

    assert (ending_balance1 == starting_balance1 - transaction.value())
    assert (ending_balance2 == starting_balance2 + transaction.value())

    assert (ending_liability1 == starting_liability1)
    assert (starting_liability2 == ending_liability2)
    assert (starting_receivable1 == ending_receivable1)
    assert (starting_receivable2 == ending_receivable2)

    assert (record.debit_account_uid() == account1.uid())
    assert (record.credit_account_uid() == account2.uid())

    debit_note = record.debit_note()
    credit_note = record.credit_note()

    assert (debit_note.account_uid() == account1.uid())
    assert (credit_note.account_uid() == account2.uid())

    assert (not debit_note.is_provisional())
    assert (not credit_note.is_provisional())

    assert (debit_note.value() == transaction.value())
    assert (credit_note.value() == transaction.value())

    now = datetime.datetime.now()

    assert (debit_note.timestamp() < now.timestamp())
    assert (credit_note.timestamp() < now.timestamp())
    assert (debit_note.timestamp() <= credit_note.timestamp())

    assert_packable(debit_note)
    assert_packable(credit_note)

    # now test refunding this transaction
    # now receipt a random amount of the transaction
    auth = Authorisation()

    refund = Refund(credit_note, auth)

    assert (not refund.is_null())
    assert (refund.authorisation() == auth)
    assert (refund.value() == transaction.value())
    assert (refund.credit_note() == credit_note)
    assert_packable(refund)

    rrecord = Ledger.refund(refund)

    assert (not rrecord.is_null())
    assert_packable(rrecord)

    assert (not rrecord.is_provisional())
    assert (rrecord.is_direct())
    assert (rrecord.get_refund_info() == refund)
    assert (rrecord.is_refund())
    assert (rrecord.original_transaction() == transaction)

    # the original transaction record has now been updated to
    # say that it has been receipted...
    assert (record.is_direct())
    record.reload()
    assert (record.is_refunded())

    assert (rrecord.original_transaction_record() == record)

    ending_balance1 = account1.balance()
    ending_liability1 = account1.liability()
    ending_receivable1 = account1.receivable()

    ending_balance2 = account2.balance()
    ending_liability2 = account2.liability()
    ending_receivable2 = account2.receivable()

    assert (ending_liability1 == starting_liability1)
    assert (ending_receivable2 == starting_receivable2)
    assert (starting_balance1 == ending_balance1)
    assert (starting_balance2 == ending_balance2)
    assert (starting_liability2 == ending_liability2)
    assert (starting_receivable1 == ending_receivable1)