def balance(loan_id):
        if re.match(loan_id_re, loan_id) is None:
            response = jsonify({"error": "Specified loan id is not valid"})
            response.status_code = 400
            return response

        if len(request.args) != 1:
            return generate_error_message("Invalid number of arguments", 400)

        date = request.args.get('date')

        if date is None:
            return generate_error_message("Invalid query parameter", 400)

        transformed_date = ciso8601.parse_datetime(date)

        if transformed_date is None:
            return generate_error_message("Specified date doesn't follow the ISO8601 norm", 400)

        if transformed_date.tzinfo is None or transformed_date.tzinfo.utcoffset(transformed_date) is None:
            transformed_date = transformed_date.replace(tzinfo=pytz.UTC)

        db_loan = Loan.get_loan(loan_id)

        if db_loan is None:
            return generate_error_message("Specified loan id doesn't exist", 400)

        total_to_pay = db_loan.amount

        db_loan_payments = Payment.get_loan_payments(loan_id)

        for payment in db_loan_payments:
            if payment.payment == "made" and payment.date <= transformed_date:
                total_to_pay -= payment.amount

        response = jsonify({"balance": float(round(total_to_pay, 2))})
        response.status_code = 200
        return response
    def payments(loan_id):
        request_data = request.get_json()

        if re.match(loan_id_re, loan_id) is None:
            response = jsonify({"error": "Specified loan id is not valid"})
            response.status_code = 400
            return response

        try:
            validate(request_data, schemas["payment"])
        except ValidationError as validation_error:
            return generate_error_message(validation_error.message, 400)

        date = ciso8601.parse_datetime(request_data["date"])

        if date is None:
            return generate_error_message("Specified date doesn't follow the ISO8601 norm", 400)

        if date.tzinfo is None or date.tzinfo.utcoffset(date) is None:
            date = date.replace(tzinfo=pytz.UTC)

        db_loan = Loan.get_loan(loan_id)

        if db_loan is None:
            return generate_error_message("Specified loan id doesn't exist", 400)

        db_loan_installment = Loan.calculate_loan_payment(db_loan.rate, db_loan.term, db_loan.amount)

        if request_data["amount"] != db_loan_installment:
            return generate_error_message(
                "Specified amount is not equal to monthly installment of {}".format(db_loan_installment), 400)

        db_loan_payments = Payment.get_loan_payments(loan_id)

        if len(db_loan_payments) != 0:
            made_payments = []

            for payment in db_loan_payments:
                if payment.payment == "made":
                    made_payments.append(payment)

            if len(made_payments) == db_loan.term:
                return generate_error_message("This loan has already been paid", 409)

            db_loan_last_payment = db_loan_payments[-1]
            months = relativedelta.relativedelta(date, db_loan_last_payment.date).months
            if abs(months) < 1:
                return generate_error_message(
                    "Payments work in a monthly base. Since last payment {} one month hasn't passed".format(
                        db_loan_last_payment.date), 409)

        request_data = request.get_json()

        payment = request_data["payment"]
        amount = request_data["amount"]
        payment = Payment(id=str(uuid.uuid4()), loan_id=loan_id, payment=payment, date=date, amount=amount)
        payment.save()

        response = jsonify({"message": "Payment successful"})
        response.status_code = 201
        return response