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
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()
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)
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
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
def __init__(self, **kwargs): super(IuguMerchant, self).__init__(**kwargs) self.__conn = base.IuguRequests()
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