Ejemplo n.º 1
0
class Transfers(object):

    __conn = base.IuguRequests()
    __urn = "/v1/transfers"

    def __init__(self, **kwargs):
        self.id = kwargs.get("id")
        self.created_at = kwargs.get("created_at")
        self.amount_cents = kwargs.get("amount_cents")
        self.amount_localized = kwargs.get("amount_localized")
        self.receiver = kwargs.get("receiver")
        self.sender = kwargs.get("sender")

    def send(self, receiver_id, amount_cents):
        """
        To send amount_cents to receiver_id
        """
        data = []
        data.append(("receiver_id", receiver_id))
        data.append(("amount_cents", amount_cents))
        response = self.__conn.post(self.__urn, data)
        return Transfers(**response)

    @classmethod
    def getitems(self):
        """
        Gets sent and received transfers for use in API_KEY
        """
        response = self.__conn.get(self.__urn, [])
        sent = response["sent"]
        received = response["received"]
        transfers = []

        for t in sent:
            transfer_obj = Transfers(**t)
            transfers.append(transfer_obj)

        for r in received:
            transfer_obj = Transfers(**r)
            transfers.append(transfer_obj)

        return transfers
Ejemplo n.º 2
0
    def __init__(self, customer, item_type="credit_card", **kwargs):
        assert isinstance(customer, IuguCustomer), "Customer invalid."
        _data = kwargs.get('data')
        self.customer_id = kwargs.get(
            'customer_id')  # useful create Payment by customer ID
        self.customer = customer
        self.description = kwargs.get('description')
        self.item_type = item_type  # support only credit_card
        if _data:
            self.token = _data.get('token')  # data credit card token
            self.display_number = _data.get('display_number')
            self.brand = _data.get('brand')
            self.holder_name = _data.get('holder_name')
        # self.set_as_default = kwargs.get('set_as_default')
        self.id = kwargs.get('id')

        # constructor payment
        data = kwargs.get('data')
        if data and isinstance(data, dict):
            self.payment_data = PaymentTypeCreditCard(**data)
        else:
            self.payment_data = PaymentTypeCreditCard()

        self.__conn = base.IuguRequests()
Ejemplo n.º 3
0
class IuguSubscription(base.IuguApi):
    """

    This class allows handling subscriptions an CRUD with create, get, set,
    save and remove plus add-ons as getitems, suspend, activate, change_plan,
    is_credit_based.

    :attribute class data: is a description it carries rules of data to API
    """

    _conn = base.IuguRequests()

    def __init__(self, **kwargs):
        super(IuguSubscription, self).__init__(**kwargs)
        self.id = kwargs.get("id")
        # required
        self.customer_id = kwargs.get("customer_id")
        # optionals
        self.plan_identifier = kwargs.get(
            "plan_identifier")  # only credits_based subscriptions
        self.expires_at = kwargs.get("expires_at")
        # self.only_on_charge_success = kwargs.get("only_on_charge_success") # if exist payment method for client
        self._subitems = kwargs.get("subitems")
        self.subitems = []  # of items
        self.custom_variables = kwargs.get("custom_variables")
        self._data = None
        self.suspended = kwargs.get("suspended")
        self.price_cents = kwargs.get("price_cents")
        self.currency = kwargs.get("currency")
        # created by api
        self.created_at = kwargs.get("created_at")
        self.updated_at = kwargs.get("updated_at")
        self.customer_name = kwargs.get("customer_name")
        self.customer_email = kwargs.get("customer_email")
        self.cycled_at = kwargs.get("cycled_at")
        self.plan_name = kwargs.get("plan_name")
        self.customer_ref = kwargs.get("customer_ref")
        self.plan_ref = kwargs.get("plan_ref")
        self.active = kwargs.get("active")
        self.in_trial = kwargs.get("in_trial")
        self.recent_invoices = kwargs.get(
            "recent_invoices")  # only resume of invoice
        self.logs = kwargs.get("logs")
        self._type = kwargs.get(
            "_type")  # facilities to verify if credit_base or general

        if isinstance(self._subitems, list):
            for item in self._subitems:
                obj_item = merchant.Item(**item)
                self.subitems.append(obj_item)

    @staticmethod
    def is_credit_based(response):
        # Checks if HTTP response of API subscription is credit_based type
        if "credits_based" in response and response["credits_based"] == True:
            return True
        return False

    @property
    def data(self):
        return self._data

    @data.setter
    def data(self, kwargs):
        """
        Body data for request send
        """
        data = []
        self.id = kwargs.get("sid")
        self.customer_id = kwargs.get("customer_id")
        self.plan_identifier = kwargs.get("plan_identifier")
        self.expires_at = kwargs.get("expires_at")
        self.only_on_charge_success = kwargs.get("only_on_charge_success")
        self.subitems = kwargs.get("subitems")
        self.custom_variables = kwargs.get("custom_data")
        self.credits_based = kwargs.get("credits_based")
        self.credits_min = kwargs.get("credits_min")
        self.credits_cycle = kwargs.get("credits_cycle")
        self.price_cents = kwargs.get("price_cents")
        self.suspended = kwargs.get("suspended")
        self.skip_charge = kwargs.get("skip_charge")

        if self.id:
            data.append(("id", self.id))

        if self.customer_id:
            data.append(("customer_id", self.customer_id))

        if self.plan_identifier:
            data.append(("plan_identifier", self.plan_identifier))

        if self.expires_at:
            data.append(("expires_at", self.expires_at))

        if self.only_on_charge_success:
            value_charge_success = str(self.only_on_charge_success)
            value_charge_success = value_charge_success.lower()
            data.append(("only_on_charge_success", value_charge_success))

        if self.subitems:
            if isinstance(self.subitems, list):
                for item in self.subitems:
                    data.extend((item.to_data(is_subscription=True)))
            else:
                raise errors.IuguSubscriptionsException("The subitems must be " \
                    "a list of obj Item")

        if self.custom_variables:  # TODO: to create test
            data.extend(self.custom_variables)

        # credit based subscriptions
        if self.credits_based is not None:
            value_credits_based = str(self.credits_based)
            value_credits_based = value_credits_based.lower()
            data.append(("credits_based", value_credits_based))

        if self.credits_min:
            data.append(("credits_min", self.credits_min))

        if self.credits_cycle:
            data.append(("credits_cycle", self.credits_cycle))

        if self.price_cents:
            data.append(("price_cents", self.price_cents))

        if self.suspended is not None:
            value_suspended = str(self.suspended)
            value_suspended = value_suspended.lower()
            data.append(("suspended", value_suspended))

        if self.skip_charge is not None:
            value_skip_charge = str(self.skip_charge)
            value_skip_charge = value_skip_charge.lower()
            data.append(("skip_charge", value_skip_charge))

        self._data = data

    @data.deleter
    def data(self):
        del self._data

    def create(self,
               customer_id,
               plan_identifier,
               expires_at=None,
               only_on_charge_success=False,
               subitems=None,
               custom_variables=None):
        """
        Creates new subscription

        :param customer_id: the ID of an existent customer
        :param plan_identifier: the identifier of a plan (it's not ID)
        :param expires_at: a string with expiration date and next charge (e.g
        "DD/MM/YYYY" or "31/12/2014")
        :param only_on_charge_success: creates the subscriptions if charged
        with success. It's supported if customer already have payment method
        inserted
        :param subitems: items of subscriptions

          => http://iugu.com/referencias/api#criar-uma-assinatura
        """
        urn = "/v1/subscriptions"
        if custom_variables:
            assert isinstance(custom_variables, dict), "Required a dict"
            custom_data = self.custom_variables_list(custom_variables)
        kwargs_local = locals().copy()
        kwargs_local.pop('self')
        self.data = kwargs_local
        response = self._conn.post(urn, self.data)
        return IuguSubscription(**response)

    def set(self,
            sid,
            plan_identifier=None,
            expires_at=None,
            subitems=None,
            suspended=None,
            skip_charge=None,
            custom_variables=None,
            customer_id=None):
        """
        Changes a subscriptions with based arguments and Returns modified
        subscription of type no credit_based.

        :param sid: ID of an existent subscriptions in API
        :param customer_id: ID of customer
        :param expires_at: expiration date and date of next charge
        :param subitems: subitems
        :param suspended: boolean to change status of subscription
        :param skip_charge: ignore charge. Bit explanation and obscure in API
        :param custom_variables: a dictionary {'key': 'value'}

        IMPORTANT 1: Removed parameter customer_id. Iugu's support (number 782)
        says that to change only customer_id isn't supported by API.
        """
        urn = "/v1/subscriptions/{sid}".format(sid=sid)
        if custom_variables:
            assert isinstance(custom_variables, dict), "Required a dict"
            custom_data = self.custom_variables_list(custom_variables)
        kwargs_local = locals().copy()
        kwargs_local.pop('self')
        self.data = kwargs_local
        response = self._conn.put(urn, self.data)
        response["_type"] = "general"
        return IuguSubscription(**response)

    def save(self):
        """Saves an instance of subscription and return own class instance
        modified"""

        if self.id:
            sid = self.id
        else:
            raise errors.IuguSubscriptionsException(value="Save is support "\
                        "only to returned API object.")

        kwargs = {}
        # TODO: to improve this ineffective approach
        # Currently this check if the required set's parameters was passed
        # If changes occurs in set() to revise this k in if used to mount kwargs
        for k, v in self.__dict__.items():
            if v is not None:
                if  k == "plan_identifier" or \
                    k == "expires_at" or k == "subitems" or \
                    k == "suspended" or k == "skip_charge" or \
                                k == "custom_variables":
                    kwargs[k] = v
                    last_valid_k = k

                if isinstance(v, list) and len(v) == 0 and last_valid_k:
                    # solves problem with arguments of empty lists
                    del kwargs[last_valid_k]

        return self.set(sid, **kwargs)

    @classmethod
    def get(self, sid):
        """
        Fetch one subscription based in ID and returns one of two's types of
        subscriptions: credit_based or no credit_based

        :param sid: ID of an existent subscriptions in API
        """
        urn = "/v1/subscriptions/{sid}".format(sid=sid)
        response = self._conn.get(urn, [])

        if self.is_credit_based(response):
            response["_type"] = "credit_based"
            return SubscriptionCreditsBased(**response)

        response["_type"] = "general"
        return IuguSubscription(**response)

    @classmethod
    def getitems(self,
                 limit=None,
                 skip=None,
                 created_at_from=None,
                 created_at_to=None,
                 query=None,
                 updated_since=None,
                 sort=None,
                 customer_id=None):
        """
        Gets subscriptions by API default limited 100.
        """
        data = []
        urn = "/v1/subscriptions/"

        # Set options
        if limit:
            data.append(("limit", limit))
        if skip:
            data.append(("start", skip))
        if created_at_from:
            data.append(("created_at_from", created_at_from))
        if created_at_to:
            data.append(("created_at_to", created_at_to))
        if updated_since:
            data.append(("updated_since", updated_since))
        if query:
            data.append(("query", query))
        if customer_id:
            data.append(("customer_id", customer_id))

        # TODO: sort not work fine. Waiting support of API providers
        if sort:
            assert sort is not str, "sort must be string as -name or name"

            if sort.startswith("-"):
                sort = sort[1:]
                key = "sortBy[{field}]".format(field=sort)
                data.append((key, "desc"))
            else:
                key = "sortBy[{field}]".format(field=sort)
                data.append((key, "asc"))

        subscriptions = self._conn.get(urn, data)
        subscriptions_objs = []
        for s in subscriptions["items"]:
            # add items in list but before verifies if credit_based
            if self.is_credit_based(s):
                s["_type"] = "credit_based"
                obj_subscription = SubscriptionCreditsBased(**s)
            else:
                s["_type"] = "general"
                obj_subscription = IuguSubscription(**s)

            subscriptions_objs.append(obj_subscription)

        return subscriptions_objs

    def remove(self, sid=None):
        """
        Removes a subscription given id or instance

        :param sid: ID of an existent subscriptions in API
        """
        if not sid:
            if self.id:
                sid = self.id
            else:
                raise errors.IuguSubscriptionsException(
                    value="ID (sid) can't be empty")

        urn = "/v1/subscriptions/{sid}".format(sid=sid)
        self._conn.delete(urn, [])

    def suspend(self, sid=None):
        """
        Suspends an existent subscriptions

        :param sid: ID of an existent subscriptions in API
        """
        if not sid:
            if self.id:
                sid = self.id
            else:
                raise errors.IuguSubscriptionsException(
                    value="ID (sid) can't be empty")

        urn = "/v1/subscriptions/{sid}/suspend".format(sid=sid)
        response = self._conn.post(urn, [])

        if self.is_credit_based(response):
            response["_type"] = "credit_based"
            return SubscriptionCreditsBased(**response)

        response["_type"] = "general"
        return IuguSubscription(**response)

    def activate(self, sid=None):
        """
        Activates an existent subscriptions

        :param sid: ID of an existent subscriptions in API

        NOTE: This option not work fine by API
        """
        if not sid:
            if self.id:
                sid = self.id
            else:
                raise errors.IuguSubscriptionsException(
                    value="ID (sid) can't be empty")

        urn = "/v1/subscriptions/{sid}/activate".format(sid=sid)
        response = self._conn.post(urn, [])

        if self.is_credit_based(response):
            response["_type"] = "credit_based"
            return SubscriptionCreditsBased(**response)

        response["_type"] = "general"
        return IuguSubscription(**response)

    def change_plan(self, plan_identifier, sid=None):
        """
        Changes the plan for existent subscriptions

        :param sid: ID of an existent subscriptions in API
        :param plan_identifier: the identifier of a plan (it's not ID)
        """
        if not sid:
            if self.id:
                # short-circuit
                if "credits_based" in self.__dict__ and self.credits_based:
                    raise errors.\
                        IuguSubscriptionsException(value="Instance must be " \
                                        "object of IuguSubscriptionsException")
                sid = self.id
            else:
                raise errors.IuguSubscriptionsException(
                    value="ID (sid) can't be empty")

        urn = "/v1/subscriptions/{sid}/change_plan/{plan_identifier}"\
                .format(sid=sid, plan_identifier=plan_identifier)
        response = self._conn.post(urn, [])

        if self.is_credit_based(response):
            response["_type"] = "credit_based"
            return SubscriptionCreditsBased(**response)

        response["_type"] = "general"
        return IuguSubscription(**response)
Ejemplo n.º 4
0
class IuguPlan(object):
    """

    This class allows handling plans. Basically contains a CRUD

    :attribute data: is a descriptor and their setters carries the rules

      => http://iugu.com/referencias/api#criar-um-plano

    """

    __conn = base.IuguRequests()

    def __init__(self, **kwargs):
        self.id = kwargs.get("id")
        self.name = kwargs.get("name")
        self.identifier = kwargs.get("identifier")
        self.interval = kwargs.get("interval")
        self.interval_type = kwargs.get("interval_type")
        self.created_at = kwargs.get("created_at")
        self.updated_at = kwargs.get("updated_at")
        self.currency = kwargs.get("currency")  # API move it to prices scope
        self.value_cents = kwargs.get(
            "value_cents")  # API move it to prices scope
        self._data = None
        self._prices = kwargs.get("prices")
        self.prices = []
        self._features = kwargs.get("features")
        self.features = []

        if isinstance(self._prices, list):
            for price in self._prices:
                obj_price = Price(**price)
                self.prices.append(obj_price)

        if isinstance(self._features, list):
            for feature in self._features:
                obj_feature = Feature(**feature)
                self.features.append(obj_feature)

    def is_valid(self):
        """Checks required fields to send to API.

        IMPORTANT: Only to use before send request for API. The fields currency
        and value_cents will saved in prices scope. Because not to use validate
        with returned data by API.
        """

        if self.name and self.identifier and self.interval and \
            self.interval_type and self.currency and self.value_cents:
            return True
        else:
            return False

    @property
    def data(self):
        return self._data

    @data.setter
    def data(self, kwargs):
        """Defines data and validates required fields to send to API.
        Returns data as list for urlencoded.
        """
        data = []

        # required fields
        self.name = kwargs.get("name")
        self.identifier = kwargs.get("identifier")
        self.interval = kwargs.get("interval")
        self.interval_type = kwargs.get("interval_type")
        self.currency = kwargs.get("currency")
        self.value_cents = kwargs.get("value_cents")
        # optional fields
        self.prices = kwargs.get("prices")
        self.features = kwargs.get("features")

        # required fields. if not passed the API return an exception
        if self.name:
            data.append(("name", self.name))

        if self.identifier:
            data.append(("identifier", self.identifier))

        if self.interval:
            data.append(("interval", self.interval))

        if self.interval_type:
            data.append(("interval_type", self.interval_type))

        if self.currency:
            if self.currency == "BRL":
                data.append(("currency", self.currency))
            else:
                raise errors.IuguPlansException(value="Only BRL supported")

        if self.value_cents:
            data.append(("value_cents", self.value_cents))

        # optional fields
        if self.prices:
            if isinstance(self.prices, list):
                # each prices items must be instance's Price class
                for price in self.prices:
                    data.extend(price.to_data())
            else:
                raise errors.IuguPlansException(value="The fields prices must "\
                 "be a list of obj Price")

        if self.features:
            if isinstance(self.features, list):
                for feature in self.features:
                    data.extend(feature.to_data())
            else:
                raise errors.IuguPlansException(value="The fields features " \
                    "must be a list of obj Feature")

        self._data = data

    @data.deleter
    def data(self):
        del self._data

    def create(self,
               name=None,
               identifier=None,
               interval=None,
               interval_type=None,
               currency=None,
               value_cents=None,
               features=None,
               prices=None):
        """
        Creates a new plans in API and returns an IuguPlan's instance. The
        fields required are name, identifier, interval, interval_type and
        values_cents.

        :param name: name of a plan
        :param identifier: unique name identifier in API plan context
        :param interval: an integer that define duration (e.g 12 to one year)
        :param interval_type: a string with "weeks" or "months"
        :param currency: only support BRL. If different raise exception
        :param value_cents: an integer with price in cents (e.g 1000 > 10.00)
        :param prices: a list of prices. The definition in API is obscure
        :param features: details with features that must be a list with
        instance of Features
        """
        urn = "/v1/plans"

        if not name:
            if self.name:
                name = self.name
            else:
                raise errors.IuguPlansException(value="Name is required")

        if not identifier:
            if self.identifier:
                identifier = self.identifier
            else:
                raise errors.IuguPlansException(value="identifier is required")

        if not interval:
            if self.interval:
                interval = self.interval
            else:
                raise errors.IuguPlansException(value="interval is required")

        if not interval_type:
            if self.interval_type:
                interval_type = self.interval_type
            else:
                raise errors.IuguPlansException(
                    value="interval_type is required")

        if not features:
            if self.features:
                features = self.features

        if not prices:
            if self.prices:
                prices = self.prices

        if not value_cents:
            if self.value_cents:
                value_cents = self.value_cents
            else:
                raise errors.IuguPlansException(
                    value="value_cents is required")

        if not currency:
            if self.currency:
                currency = self.currency

        kwargs_local = locals().copy()
        kwargs_local.pop('self')  # prevent error of multiple value for args
        self.data = kwargs_local
        response = self.__conn.post(urn, self.data)

        return IuguPlan(**response)

    def set(self,
            plan_id,
            name=None,
            identifier=None,
            interval=None,
            interval_type=None,
            currency=None,
            value_cents=None,
            features=None,
            prices=None):
        """
        Edits/changes existent plan and returns IuguPlan's instance

        :param plan_id: ID number of a existent plan
        """
        urn = "/v1/plans/{plan_id}".format(plan_id=plan_id)
        kwargs_local = locals().copy()
        kwargs_local.pop('self')
        self.data = kwargs_local
        response = self.__conn.put(urn, self.data)
        return IuguPlan(**response)

    def save(self):
        """Saves an instance of IuguPlan and return own class instance
        modified"""
        urn = "/v1/plans/{plan_id}".format(plan_id=self.id)
        self.data = self.__dict__
        response = self.__conn.put(urn, self.data)
        return IuguPlan(**response)

    @classmethod
    def get(self, plan_id):
        """Gets one plan based in ID and returns an instance"""
        data = []
        urn = "/v1/plans/{plan_id}".format(plan_id=plan_id)
        response = self.__conn.get(urn, data)
        return IuguPlan(**response)

    @classmethod
    def get_by_identifier(self, identifier):
        """Gets one plan based in identifier and returns an instance

        :param identifier: it's an unique identifier plan in API
        """
        data = []
        urn = "/v1/plans/identifier/{identifier}".format(identifier=identifier)
        response = self.__conn.get(urn, data)
        return IuguPlan(**response)

    @classmethod
    def getitems(self,
                 limit=None,
                 skip=None,
                 query=None,
                 updated_since=None,
                 sort=None):
        """
        Gets plans by API default limited 100.

        :param limit: limits the number of plans returned by API (default
        and immutable of API is 100)
        :param skip: skips a numbers of plans where more recent insert
        ordering. Useful to pagination.
        :param query: filters based in value (case insensitive)
        :param sort: sorts based in field. Use minus signal to determine the
        direction DESC or ASC (e.g sort="-email"). IMPORTANT: not work by API
        :return: list of IuguPlan's instances
        """
        data = []
        urn = "/v1/plans/"

        # Set options
        if limit:
            data.append(("limit", limit))

        if skip:
            data.append(("start", skip))

        if updated_since:
            data.append(("updated_since", updated_since))

        if query:
            data.append(("query", query))

        # TODO: sort not work fine. Waiting support of API providers
        if sort:
            assert sort is not str, "sort must be string as -name or name"

            if sort.startswith("-"):
                sort = sort[1:]
                key = "sortBy[{field}]".format(field=sort)
                data.append((key, "desc"))
            else:
                key = "sortBy[{field}]".format(field=sort)
                data.append((key, "asc"))

        plans = self.__conn.get(urn, data)
        plans_objects = []
        for plan_item in plans["items"]:
            obj_plan = IuguPlan(**plan_item)
            plans_objects.append(obj_plan)

        return plans_objects

    def remove(self, plan_id=None):
        """
        Removes an instance or passing a plan_id
        """
        if plan_id:
            to_remove = plan_id
        else:
            to_remove = self.id

        if not to_remove:
            raise errors.IuguPlansException(
                value="Instance or plan id is required")

        urn = "/v1/plans/{plan_id}".format(plan_id=to_remove)
        response = self.__conn.delete(urn, [])
        # check if result can to generate instance of IuguPlan
        obj = IuguPlan(**response)

        if obj:
            for k, v in self.__dict__.items():
                self.__dict__[k] = None
Ejemplo n.º 5
0
class IuguCustomer(base.IuguApi):

    __conn = base.IuguRequests()

    def __init__(self, **options):
        """
        This class is a CRUD for customers in API

        :param **options: receives dictionary load by JSON with fields of API

          => http://iugu.com/referencias/api#clientes
        """
        super(IuguCustomer, self).__init__(**options)
        self.id = options.get("id")
        self.email = options.get("email")
        self.default_payment_method_id = options.get(
            "default_payment_method_id")
        self.name = options.get("name")
        self.notes = options.get("notes")
        # TODO: convert str date in date type
        # year, month, day = map(int, string_date.split('-'))
        # date_converted = Date(day, month, year)
        self.created_at = options.get("created_at")
        # TODO: convert str date in date type
        self.updated_at = options.get("updated_at")
        self.custom_variables = options.get("custom_variables")
        self.payment = IuguPaymentMethod(self)

    def create(self, name=None, notes=None, email=None, custom_variables=None):
        """Creates a customer and return an IuguCustomer's instance

        :param name: customer name
        :param notes: field to post info's of an user
        :param email: required data of an user
        :param custom_variables: a dict {'key':'value'}
        """
        data = []
        urn = "/v1/customers"

        if name:
            data.append(("name", name))

        if notes:
            data.append(("notes", notes))

        if email:
            self.email = email

        if self.email:
            data.append(("email", self.email))
        else:
            raise errors.IuguGeneralException(value="E-mail required is empty")

        if custom_variables:
            custom_data = self.custom_variables_list(custom_variables)
            data.extend(custom_data)
        customer = self.__conn.post(urn, data)
        instance = IuguCustomer(**customer)

        return instance

    def set(self, customer_id, name=None, notes=None, custom_variables=None):
        """ Updates/changes a customer that already exists

        :param custom_variables: is a dict {'key':'value'}
        HINT: Use method save() at handling an instance
        """
        data = []
        urn = "/v1/customers/{customer_id}".format(
            customer_id=str(customer_id))

        if name:
            data.append(("name", name))

        if notes:
            data.append(("notes", notes))

        if custom_variables:
            custom_data = self.custom_variables_list(custom_variables)
            data.extend(custom_data)

        customer = self.__conn.put(urn, data)

        return IuguCustomer(**customer)

    def save(self):
        """Save updating a customer's instance"""
        return self.set(self.id, name=self.name, notes=self.notes)

    @classmethod
    def get(self, customer_id):
        """Gets one customer based in iD and returns an instance"""
        data = []
        urn = "/v1/customers/{customer_id}".format(
            customer_id=str(customer_id))
        customer = self.__conn.get(urn, data)
        instance = IuguCustomer(**customer)

        return instance

    @classmethod
    def getitems(self,
                 limit=None,
                 skip=None,
                 created_at_from=None,
                 created_at_to=None,
                 query=None,
                 updated_since=None,
                 sort=None):
        """
        Get a list of customers and return a list of IuguCustomer's instances.

        :param limit: limits the number of customers returned by API (default
        and immutable of API is 100)
        :param skip: skips a numbers of customers where more recent insert
        ordering. Useful to pagination
        :param query: filters based in value (case insensitive)
        :param sort: sorts based in field. Use minus signal to determine the
        direction DESC or ASC (e.g sort="-email"). IMPORTANT: not work by API
        :return: list of IuguCustomer instances
        """
        data = []
        urn = "/v1/customers/"

        # Set options
        if limit:
            data.append(("limit", limit))
        if skip:
            data.append(("start", skip))
        if created_at_from:
            data.append(("created_at_from", created_at_from))
        if created_at_to:
            data.append(("created_at_to", created_at_to))
        if updated_since:
            data.append(("updated_since", updated_since))
        if query:
            data.append(("query", query))

        # TODO: sort not work fine. Waiting support of API providers
        if sort:
            assert sort is not str, "sort must be string as -name or name"

            if sort.startswith("-"):
                sort = sort[1:]
                key = "sortBy[{field}]".format(field=sort)
                data.append((key, "desc"))
            else:
                key = "sortBy[{field}]".format(field=sort)
                data.append((key, "asc"))

        customers = self.__conn.get(urn, data)

        #TODO: list comprehensions
        customers_objects = []
        for customer in customers["items"]:
            obj_customer = IuguCustomer(**customer)
            customers_objects.append(obj_customer)

        return customers_objects

    def delete(self, customer_id=None):
        """Deletes a customer of instance or by id.
        And return the removed object"""
        data = []

        if self.id:
            # instance of class (customer already exist)
            _customer_id = self.id
        else:
            if customer_id:
                _customer_id = customer_id
            else:
                # instance of class (not saved)
                raise TypeError("It's not instance of object returned or " \
                                "customer_id is empty.")

        urn = "/v1/customers/" + str(_customer_id)
        customer = self.__conn.delete(urn, data)

        return IuguCustomer(**customer)

    remove = delete  # remove for keep the semantic of API
Ejemplo n.º 6
0
 def __init__(self, **kwargs):
     super(IuguMerchant, self).__init__(**kwargs)
     self.__conn = base.IuguRequests()
Ejemplo n.º 7
0
class IuguInvoice(base.IuguApi):
    """

    This class allows handling invoices. The invoice is used to customers to
    make payments.

    :attribute class data: is a descriptor that carries rules of API fields.
    Only fields not None or not Blank can be sent.
    :attribute status: Accept two option: draft and pending, but can be
    draft, pending, [paid and canceled (internal use)]
    :attribute logs: is instanced a dictionary like JSON
    :attribute bank_slip: is instanced a dictionary like JSON

      => http://iugu.com/referencias/api#faturas
    """

    __conn = base.IuguRequests()

    def __init__(self, item=None, **kwargs):
        super(IuguInvoice, self).__init__(**kwargs)
        self.id = kwargs.get("id")
        self.due_date = kwargs.get("due_date")
        self.currency = kwargs.get("currency")
        self.discount_cents = kwargs.get("discount_cents")
        # self.customer_email = kwargs.get("customer_email")
        self.email = kwargs.get("email")  # customer email
        self.items_total_cents = kwargs.get("items_total_cents")
        self.notification_url = kwargs.get("notification_url")
        self.return_url = kwargs.get("return_url")
        self.status = kwargs.get(
            "status")  # [draft,pending] internal:[paid,canceled]
        self.expiration_url = kwargs.get("expiration_url")
        self.tax_cents = kwargs.get("tax_cents")
        self.updated_at = kwargs.get("updated_at")
        self.total_cents = kwargs.get("total_cents")
        self.paid_at = kwargs.get("paid_at")
        self.secure_id = kwargs.get("secure_id")
        self.secure_url = kwargs.get("secure_url")
        self.customer_id = kwargs.get("customer_id")
        self.user_id = kwargs.get("user_id")
        self.total = kwargs.get("total")
        self.created_at = kwargs.get("created_at")
        self.taxes_paid = kwargs.get("taxes_paid")
        self.interest = kwargs.get("interest")
        self.discount = kwargs.get("discount")
        self.refundable = kwargs.get("refundable")
        self.installments = kwargs.get("installments")
        self.bank_slip = kwargs.get(
            "bank_slip")  # TODO: create a class/object.
        self.logs = kwargs.get("logs")  # TODO: create a class/object
        # TODO: descriptors (getter/setter) for items
        _items = kwargs.get("items")
        self.items = None

        if _items:
            # TODO: list comprehensions
            _list_items = []
            for i in _items:
                obj_item = merchant.Item(**i)
                _list_items.append(obj_item)
            self.items = _list_items
        else:
            if item:
                assert isinstance(
                    item, merchant.Item), "item must be instance of Item"
                self.items = item

        self.variables = kwargs.get("variables")
        self.logs = kwargs.get("logs")
        self.custom_variables = kwargs.get("custom_variables")
        self._data = None

    # constructor of data descriptors
    def data_get(self):
        return self._data

    def data_set(self, kwargs):
        draft = kwargs.get("draft")
        return_url = kwargs.get("return_url")
        expired_url = kwargs.get("expired_url")
        notification_url = kwargs.get("notification_url")
        tax_cents = kwargs.get("tax_cents")
        discount_cents = kwargs.get("discount_cents")
        customer_id = kwargs.get("customer_id")
        ignore_due_email = kwargs.get("ignore_due_email")
        subscription_id = kwargs.get("subscription_id")
        due_date = kwargs.get("due_date")
        credits = kwargs.get("credits")
        items = kwargs.get("items")
        email = kwargs.get("email")
        custom_data = kwargs.get("custom_data")

        data = []

        if draft:
            data.append(("status", "draft"))  # default is pending

        # data will posted and can't null, None or blank
        if return_url:
            self.return_url = return_url

        if self.return_url:
            data.append(("return_url", self.return_url))

        if expired_url:
            self.expiration_url = expired_url

        if self.expiration_url:
            data.append(("expired_url", self.expiration_url))

        if notification_url:
            self.notification_url = notification_url

        if self.notification_url:
            data.append(("notification_url", self.notification_url))

        if tax_cents:
            self.tax_cents = tax_cents

        data.append(("tax_cents", self.tax_cents))

        if discount_cents:
            self.discount_cents = discount_cents

        data.append(("discount_cents", self.discount_cents))

        if customer_id:
            self.customer_id = customer_id

        if self.customer_id:
            data.append(("customer_id", self.customer_id))

        if credits:
            data.append(("credits", credits))

        if ignore_due_email:
            data.append(("ignore_due_email", True))

        if subscription_id:
            data.append(("subscription_id", subscription_id))

        if due_date:
            self.due_date = due_date

        if self.due_date:
            data.append(("due_date", self.due_date))

        if isinstance(items, list):
            for item in items:
                data.extend(item.to_data())
        else:
            if items is not None:
                data.extend(items.to_data())

        if email:
            self.email = email

        if self.email:
            data.append(("email", self.email))

        if custom_data:
            data.extend(custom_data)

        self._data = data

    def data_del(self):
        del self._data

    data = property(data_get, data_set, data_del, "data property set/get/del")

    def create(self,
               draft=False,
               return_url=None,
               email=None,
               expired_url=None,
               notification_url=None,
               tax_cents=None,
               discount_cents=None,
               customer_id=None,
               ignore_due_email=False,
               subscription_id=None,
               credits=None,
               due_date=None,
               items=None,
               custom_variables=None):
        """
        Creates an invoice and returns owns class

        :param subscription_id: must be existent subscription from API
        :param customer_id: must be API customer_id (existent customer)
        :param items: must be item instance of merchant.Item()
        :para custom_variables: a dict {'key':'value'}

          => http://iugu.com/referencias/api#faturas
        """
        urn = "/v1/invoices"

        # handling required fields
        if not due_date:
            if self.due_date:
                # due_date is required. If it not passed in args, it must to
                # exist at least in instance object
                due_date = self.due_date  # "force" declaring locally
            else:
                raise errors.IuguInvoiceException(value="Required due_date is" \
                                                " empty.")

        if not items:
            if self.items:
                # items are required. If it not passed as args,
                # it must to exist at least in instance object
                items = self.items  # "force" declaring locally
            else:
                raise errors.IuguInvoiceException(value="Required items is" \
                                            " empty.")

        if not email:
            if self.email:
                # email is required. If it not passed as args,
                # it must to exist at least in instance object
                email = self.email  # "force" declaring locally
            else:
                raise errors.IuguInvoiceException(value="Required customer" \
                                    " email is empty.")

        if custom_variables:
            custom_data = self.custom_variables_list(custom_variables)
        # to declare all variables local before calling locals().copy()
        kwargs_local = locals().copy()
        kwargs_local.pop('self')
        self.data = kwargs_local
        response = self.__conn.post(urn, self.data)
        invoice = IuguInvoice(**response)
        return invoice

    def set(self,
            invoice_id,
            email=None,
            due_date=None,
            return_url=None,
            expired_url=None,
            notification_url=None,
            tax_cents=None,
            discount_cents=None,
            customer_id=None,
            ignore_due_email=False,
            subscription_id=None,
            credits=None,
            items=None,
            custom_variables=None):
        """ Updates/changes a invoice that already exists

        :param custom_variables: a dict {'key', value}. If previously values
        exist the variable is edited rather is added

        IMPORTANT: Only invoices with status "draft" can be changed all fields
        otherwise (if status pending, cancel or paid) only the field logs
        can to change.
        """
        urn = "/v1/invoices/{invoice_id}".format(invoice_id=invoice_id)

        if items is not None:
            assert isinstance(items,
                              merchant.Item), "item must be instance of Item"

        if custom_variables:
            custom_data = self.custom_variables_list(custom_variables)
        # to declare all variables local before calling locals().copy()
        kwargs_local = locals().copy()
        kwargs_local.pop('self')
        self.data = kwargs_local
        response = self.__conn.put(urn, self.data)

        return IuguInvoice(**response)

    def save(self):
        """Save updating a invoice's instance. To add/change custom_variables
        keywords to use create() or set()

        IMPORTANT: Only invoices with status "draft" can be changed
        """
        self.data = self.__dict__
        urn = "/v1/invoices/{invoice_id}".format(invoice_id=self.id)
        response = self.__conn.put(urn, self.data)

        return IuguInvoice(**response)

    @classmethod
    def get(self, invoice_id):
        """Gets one invoice with base in invoice_id and returns instance"""
        data = []
        urn = "/v1/invoices/{invoice_id}".format(invoice_id=invoice_id)
        response = self.__conn.get(urn, data)

        return IuguInvoice(**response)

    @classmethod
    def getitems(self,
                 limit=None,
                 skip=None,
                 created_at_from=None,
                 created_at_to=None,
                 query=None,
                 updated_since=None,
                 sort=None,
                 customer_id=None):
        """
        Gets a list of invoices where the API default is limited 100. Returns
        a list of IuguInvoice

        :param limit: limits the number of invoices returned by API
        :param skip: skips a numbers of invoices where more recent insert
        ordering. Useful to pagination.
        :param query: filters based in value (case insensitive)
        :param sort: sorts based in field. Use minus signal to determine the
        direction DESC or ASC (e.g sort="-email"). IMPORTANT: not work by API
        :return: list of IuguInvoice instances
        """
        data = []
        urn = "/v1/invoices/"

        # Set options
        if limit:
            data.append(("limit", limit))
        if skip:
            data.append(("start", skip))
        if created_at_from:
            data.append(("created_at_from", created_at_from))
        if created_at_to:
            data.append(("created_at_to", created_at_to))
        if updated_since:
            data.append(("updated_since", updated_since))
        if query:
            data.append(("query", query))
        if customer_id:
            data.append(("customer_id", customer_id))

        # TODO: sort not work fine. Waiting support of API providers
        if sort:
            assert sort is not str, "sort must be string as -name or name"

            if sort.startswith("-"):
                sort = sort[1:]
                key = "sortBy[{field}]".format(field=sort)
                data.append((key, "desc"))
            else:
                key = "sortBy[{field}]".format(field=sort)
                data.append((key, "asc"))

        invoices = self.__conn.get(urn, data)
        #TODO: list comprehensions
        invoices_objects = []
        for invoice_item in invoices["items"]:
            obj_invoice = IuguInvoice(**invoice_item)
            invoices_objects.append(obj_invoice)

        return invoices_objects

    def remove(self, invoice_id=None):
        """
        Removes an invoice by id or instance and returns None
        """
        invoice_id = invoice_id if invoice_id else self.id
        if invoice_id is None:
            raise errors.IuguSubscriptionsException(
                value="ID (invoice_id) can't be empty")

        urn = "/v1/invoices/{invoice_id}".format(invoice_id=invoice_id)
        response = self.__conn.delete(urn, [])
        obj = IuguInvoice(**response)
        # TODO: list comprehensions ?
        if obj:
            for k, v in self.__dict__.items():
                self.__dict__[k] = None

    def cancel(self):
        """Cancels an instance of invoice and returns own invoice with status
        canceled"""
        urn = "/v1/invoices/{invoice_id}/cancel".format(invoice_id=self.id)

        # This below if to avoid a request because the API not allow this operation
        # but all API can to change theirs behaviors so to allow to cancel
        # invoices with status difference of "pending".
        # The approach without if also to raise exception with error from directly
        # API responses but here the focus is less requests.
        if self.status == "pending":
            response = self.__conn.put(urn, [])
            obj = IuguInvoice(**response)
        else:
            raise errors.IuguGeneralException(value="Cancel operation support only " \
                "invoices with status: pending.")

        return obj

    @classmethod
    def to_cancel(self, invoice_id):
        """Cancels an invoice with base in invoice ID and returns own
        invoice with status canceled

          => http://iugu.com/referencias/api#cancelar-uma-fatura
        """
        urn = "/v1/invoices/{invoice_id}/cancel".format(invoice_id=invoice_id)
        response = self.__conn.put(urn, [])
        obj = IuguInvoice(**response)

        return obj

    def refund(self):
        """Makes refund of an instance of invoice

          => http://iugu.com/referencias/api#reembolsar-uma-fatura
        """
        urn = "/v1/invoices/{invoice_id}/refund".format(invoice_id=self.id)

        # This below if to avoid a request because the API not allow this operation
        # but all API can to change theirs behaviors so to allow to refund
        # invoices with status difference of "paid".
        # The approach without if also to raise exception with error from directly
        # API responses but here the focus is less requests.
        if self.status == "paid":
            response = self.__conn.post(urn, [])
            obj = IuguInvoice(**response)
        else:
            raise errors.IuguGeneralException(value="Refund operation support only " \
                "invoices with status: paid.")

        return obj