diff --git a/sharpy/parsers.py b/sharpy/parsers.py index 6d4b417..746a289 100644 --- a/sharpy/parsers.py +++ b/sharpy/parsers.py @@ -282,8 +282,31 @@ def parse_invoice(self, invoice_element): invoice['billing_datetime'] = self.parse_datetime(invoice_element.findtext('billingDatetime')) invoice['paid_transaction_id'] = invoice_element.findtext('paidTransactionId') invoice['created_datetime'] = self.parse_datetime(invoice_element.findtext('createdDatetime')) - invoice['charges'] = self.parse_charges(invoice_element.find('charges')) + + invoice['paid_datetime'] = '' + if invoice['paid_transaction_id']: + transactions = invoice_element.find('transactions') + for transaction in transactions: + if transaction.attrib['id'] == invoice['paid_transaction_id']: + invoice['paid_datetime'] = self.parse_datetime(transaction.findtext('transactedDatetime')) + break + + invoice['refunded'] = False + if invoice['paid_transaction_id']: + paid_transaction_amount = Decimal('0') + childeren_of_paid_transaction_sum = Decimal('0') + transactions = invoice_element.find('transactions') + for transaction in transactions: + if transaction.findtext('parentId') == invoice['paid_transaction_id']: + amount = self.parse_decimal(transaction.findtext('amount')) + childeren_of_paid_transaction_sum += amount + elif transaction.attrib['id'] == invoice['paid_transaction_id']: + paid_transaction_amount = self.parse_decimal(transaction.findtext('amount')) + + refunded_equals_received = (childeren_of_paid_transaction_sum.copy_negate() == paid_transaction_amount) + if paid_transaction_amount != 0 and refunded_equals_received: + invoice['refunded'] = True return invoice diff --git a/sharpy/product.py b/sharpy/product.py index cd6c0bc..067d03a 100644 --- a/sharpy/product.py +++ b/sharpy/product.py @@ -47,7 +47,7 @@ def get_plan(self, code): return plans[0] def create_customer(self, code, first_name, last_name, email, plan_code, \ - company=None, is_vat_exempt=None, vat_number=None, \ + coupon_code=None, company=None, is_vat_exempt=None, vat_number=None, \ notes=None, first_contact_datetime=None, \ referer=None, campaign_term=None, \ campaign_name=None, campaign_source=None, \ @@ -61,7 +61,7 @@ def create_customer(self, code, first_name, last_name, email, plan_code, \ cancel_url=None, charges=None, items=None): data = self.build_customer_post_data(code, first_name, last_name, \ - email, plan_code, company, is_vat_exempt, vat_number, \ + email, plan_code, coupon_code, company, is_vat_exempt, vat_number, \ notes, first_contact_datetime, referer, campaign_term, \ campaign_name, campaign_source, campaign_medium, \ campaign_content, meta_data, initial_bill_date, method, \ @@ -90,7 +90,7 @@ def create_customer(self, code, first_name, last_name, email, plan_code, \ def build_customer_post_data(self, code=None, first_name=None,\ last_name=None, email=None, plan_code=None, \ - company=None, is_vat_exempt=None, vat_number=None, \ + coupon_code=None, company=None, is_vat_exempt=None, vat_number=None, \ notes=None, first_contact_datetime=None, \ referer=None, campaign_term=None, \ campaign_name=None, campaign_source=None, \ @@ -119,6 +119,9 @@ def build_customer_post_data(self, code=None, first_name=None,\ if plan_code: data['subscription[planCode]'] = plan_code + + if coupon_code: + data['subscription[couponCode]'] = coupon_code if company: data['company'] = company @@ -240,16 +243,46 @@ def get_customers(self, filter_data=None): return customers - def get_customer(self, code): - + def get_customer(self, code=None, invoice_number=None): + + if code: + parameters = {'code': code} + elif invoice_number: + parameters = {'invoiceNumber': invoice_number} + else: + raise ValueError('code or invoice_number must be provided') + response = self.client.make_request( path='customers/get', - params={'code': code}, + params=parameters, ) customer_parser = CustomersParser() customers_data = customer_parser.parse_xml(response.content) return Customer(product=self, **customers_data[0]) + + def waive_invoice(self, number=None, id=None): + ''' + Adds a charge to an invoice for balancing it to 0. Useful for + getting rid of an old invoice that is now irrelevant but gets + in the way of reactivating a customer account. + + Must call run_outstanding_invoice on customer to get rid of that + invoice completely. + ''' + data = {} + + if id: + data['id'] = id + elif number: + data['number'] = number + else: + raise Exception('Must provide id or number of invoice as argument') + + response = self.client.make_request( + path='invoices/waive', + data=data, + ) def delete_all_customers(self): ''' @@ -414,19 +447,35 @@ def load_data(self, code, first_name, last_name, email, product, id=None,\ if meta_data: for datum in meta_data: self.meta_data[datum['name']] = datum['value'] - subscription_data = subscriptions[0] - subscription_data['customer'] = self - if hasattr(self, 'subscription'): - self.subscription.load_data(**subscription_data) - else: - self.subscription = Subscription(**subscription_data) - + + self.subscriptions = [] + for i, subscription_data in enumerate(subscriptions): + subscription_data['customer'] = self + if i == 0: + if hasattr(self, 'subscription'): + self.subscription.load_data(**subscription_data) + else: + self.subscription = Subscription(**subscription_data) + + self.subscriptions.append(self.subscription) + else: + self.subscriptions.append(Subscription(**subscription_data)) + def load_data_from_xml(self, xml): customer_parser = CustomersParser() customers_data = customer_parser.parse_xml(xml) customer_data = customers_data[0] self.load_data(product=self.product, **customer_data) - + + def get_data(self): + response = self.product.client.make_request( + path='customers/get', + params={'code': self.code}, + ) + customer_parser = CustomersParser() + customers_data = customer_parser.parse_xml(response.content) + return customers_data[0] + def update(self, first_name=None, last_name=None, email=None, \ company=None, is_vat_exempt=None, vat_number=None, \ notes=None, first_contact_datetime=None, \ @@ -438,12 +487,12 @@ def update(self, first_name=None, last_name=None, email=None, \ cc_card_code=None, cc_first_name=None, \ cc_last_name=None, cc_company=None, cc_email=None,\ cc_country=None, cc_address=None, cc_city=None, \ - cc_state=None, cc_zip=None, plan_code=None, bill_date=None, + cc_state=None, cc_zip=None, plan_code=None, coupon_code=None, bill_date=None, return_url=None, cancel_url=None,): data = self.product.build_customer_post_data( first_name=first_name, last_name=last_name, email=email, plan_code=plan_code, - company=company, is_vat_exempt=is_vat_exempt, + coupon_code=coupon_code, company=company, is_vat_exempt=is_vat_exempt, vat_number=vat_number, notes=notes, referer=referer, campaign_term=campaign_term, campaign_name=campaign_name, @@ -504,6 +553,26 @@ def charge(self, code, each_amount, quantity=1, description=None): ) return self.load_data_from_xml(response.content) + def run_outstanding_invoice(self, cc_card_code=None, remote_address=None): + ''' + Execute an outstanding invoice + ''' + data = {} + + if cc_card_code: + data['ccCardCode'] = cc_card_code + + if remote_address: + data['remoteAddress'] = remote_address + + response = self.product.client.make_request( + path='customers/run-outstanding', + params={'code': self.code}, + data=data, + method='POST', + ) + return self.load_data_from_xml(response.content) + def create_one_time_invoice(self, charges): ''' Charges should be a list of charges to execute immediately. Each