def test_ReversalAdjustment(self): """ reversing incorrect accounting event """ #setUpLowPay() # what initially posted usage_event = Usage(Unit.KWH.amount(50), date(1999, 10, 1), self.acm) # must read http://martinfowler.com/eaaDev/AgreementDispatcher.html event_list = EventList() event_list.add(usage_event) event_list.process() self.assertReallyEqual(Money(500), self.acm.balance_for(ACCOUNT_TYPE.BASE_USAGE)) self.assertReallyEqual(Money(27.5), self.acm.balance_for(ACCOUNT_TYPE.TAX)) # adjust adjustment1 = Usage(Unit.KWH.amount(70), date(1999, 10, 1), adjusted_event=usage_event) #adjustment1 = ReversalAdjustment(usage_event.event_type, date(1999, 11, 1), # adjusted_event=usage_event) event_list.add(adjustment1) event_list.process() self.assertReallyEqual(Money(700), self.acm.balance_for(ACCOUNT_TYPE.BASE_USAGE)) self.assertReallyEqual(Money(38.50), self.acm.balance_for(ACCOUNT_TYPE.TAX))
class ShippingRuleFactory(factory.Factory): FACTORY_FOR = ShippingRule profile = factory.SubFactory(ShippingProfileFactory) rule_price = Money(10, money.CURRENCY["GBP"]) rule_price_extra = Money(20, money.CURRENCY["GBP"]) despatch_time = 2 delivery_time = 5 delivery_time_max = 10
def clean_balance(self): value = self.cleaned_data['balance'] try: money_val = Money() money_val.from_string(value) if money_val.currency.code == 'XXX' and get_def_currency(): money_val.currency = CURRENCY[get_def_currency()] except: raise forms.ValidationError("Invalid money value") return money_val
def test_balanceUsingTransactions(self): """ page 48 """ revenue = self.acm.account_for(ACCOUNT_TYPE.REVENUE) deferred = self.acm.account_for(ACCOUNT_TYPE.RECEIVABLES) receivables = self.acm.account_for(ACCOUNT_TYPE.DEFERRED) revenue.withdraw(Money(500), receivables, date(1999, 1, 4)) revenue.withdraw(Money(200), deferred, date(1999, 1, 4)) self.assertReallyEqual(Money(500), receivables.balance()) self.assertReallyEqual(Money(200), deferred.balance()) self.assertReallyEqual(Money(-700), revenue.balance())
def testSaveBigAmount(self): big_money = Money(2500000, "USD") payment = PaymentFactory.build(money=big_money, money_currency=big_money.currency) payment.save() self.assertEqual(big_money, Payment.objects.get(pk=payment.pk).money)
def prepare_data(self, money, return_url, cancel_url, receivers, ipn_url=None, **kwargs): """Prepare data for Pay API call""" if (not money or not isinstance(money, Money) or money <= Money('0.00', money.currency)): raise ValueError("amount must be a positive instance of Money") if (not isinstance(receivers, ReceiverList) or len(receivers) < 1): raise ValueError("receivers must be an instance of ReceiverList") data = { 'actionType': 'PAY', 'currencyCode': money.currency.code, 'returnUrl': return_url, 'cancelUrl': cancel_url } receiverList = {'receiver': receivers.to_dict()} data.update({'receiverList': receiverList}) if ipn_url: data.update({'ipnNotificationUrl': ipn_url}) if kwargs: data.update(**kwargs) return data
def testMismatchedAmounts(self): """Test mismatching amounts""" wrong_amount = str(Money("1337.23", 'SEK')) data = { 'status': 'COMPLETED', 'transaction_type': 'Adaptive Payment PAY', 'transaction[0].id': 1, 'transaction[0].amount': wrong_amount, 'transaction[0].status': 'COMPLETED', 'transaction[1].id': 2, 'transaction[1].amount': wrong_amount, 'transaction[1].status': 'COMPLETED', } self.mock_ipn_call(data) payment = self.get_payment() self.assertEqual(payment.status, 'error') self.assertEqual( payment.status_detail, "IPN amounts didn't match. Payment requested %s. " "Payment made %s" % (payment.money, wrong_amount))
def test_usage(self): """ page 37 """ usage_evt = Usage(Unit.KWH.amount(50), date(1999, 10, 1), self.acm) usage_evt.process() # Entry getEntry(Customer, AccountType) usage_entry = self.acm.account_for(ACCOUNT_TYPE.BASE_USAGE).entries[0] tax_entry = self.acm.account_for(ACCOUNT_TYPE.TAX).entries[0] self.assertEqual(Money(500), usage_entry.get_amount()) self.assertEqual(ACCOUNT_TYPE.BASE_USAGE, usage_entry.get_entry_type()) self.assertEqual(Money(27.5), tax_entry.get_amount()) self.assertEqual(ACCOUNT_TYPE.TAX, tax_entry.get_entry_type()) self.assertIn(usage_entry, usage_evt.resulting_entries) self.assertIn(tax_entry, usage_evt.get_all_resulting_entries())
def balance(self, date=None, period=None): if date is None and period is None: date = datetime.date.today() period = DateRange.upto(date) result = Money(0, self.currency) for entry in self.entries: if period.includes(entry.get_date()): result += entry.get_amount() return result
def total_gmv(self): """Total Gross Merchandise Value of user purchases NOTE: This is the customer livetime value!!! See also: Stall.total_gmv """ orders = self.user.orders paid_orders = orders.filter(Q(is_joomla_order=True) | ~Q(payment=None)) return Money(0, DEFAULT_CURRENCY) + sum( [obj.total() for obj in paid_orders])
def sub_total(self): """Returns the total price of all products in the cart for a stall. TODO: add delivery. """ amount = Money("0", DEFAULT_CURRENCY) for cart_product in self.cart_products.all(): amount += cart_product.total return amount
def setUp(self): self.preapproval = Preapproval() self.preapproval.money = Money(100, "SEK") self.preapproval.save() self.preapproval = Preapproval.objects.all()[0] self.payment = Payment() self.payment.money = Money(100, "USD") self.payment.save() self.payment = Payment.objects.all()[0] george = Receiver(amount=10, email=settings.settings.PAYPAL_EMAIL, primary=False) allen = Receiver(amount=90, email="*****@*****.**", primary=True) self.recievers = ReceiverList([george, allen])
def setUp(self): self.acm = Customer("Acme Coffee Makers") standard = ServiceAgreement() standard.set_rate(10) standard.add_posting_rule(EVENT_TYPE.USAGE, MultiplyByRatePR(ACCOUNT_TYPE.BASE_USAGE), date(1999, 10, 1)) standard.add_posting_rule(EVENT_TYPE.TAX, AmountFormulaPR(0.055, Money(0), ACCOUNT_TYPE.TAX), date(1999, 10, 1)) self.acm.set_service_agreement(standard)
def balance(self): if len(self.entries) == 0: # XXX: this should be currency neutral 0. or should this class # keep Currency? return Money(0, 'USD') result = None for entry in self.entries: if result is None: result = entry.get_amount() continue result += entry.get_amount() return result
def test_DifferenceAdjustment(self): usage1 = Usage(Unit.KWH.amount(20), date(1999, 10, 1), self.acm) usage2 = Usage(Unit.KWH.amount(20), date(1999, 10, 2), self.acm) usage3 = Usage(Unit.KWH.amount(30), date(1999, 10, 3), self.acm) event_list = EventList() event_list.add(usage1) event_list.add(usage2) event_list.add(usage3) event_list.process() # total 700kw, $700, $38.5 tax self.assertReallyEqual(Money(700), self.acm.balance_for(ACCOUNT_TYPE.BASE_USAGE)) self.assertReallyEqual(Money(38.50), self.acm.balance_for(ACCOUNT_TYPE.TAX)) # adjustment events new1 = Usage(Unit.KWH.amount(10), date(1999, 10, 1), self.acm) new2 = Usage(Unit.KWH.amount(10), date(1999, 10, 2), self.acm) new3 = Usage(Unit.KWH.amount(15), date(1999, 10, 3), self.acm) adj_date = date(2000, 1, 12) adj = DifferenceAdjustment(adj_date, self.acm) adj.add_old_event(usage1) adj.add_old_event(usage2) adj.add_old_event(usage3) adj.add_new_event(new1) adj.add_new_event(new2) adj.add_new_event(new3) event_list.add(adj) event_list.process() self.assertReallyEqual(Money(350), self.acm.balance_for(ACCOUNT_TYPE.BASE_USAGE)) self.assertReallyEqual(Money(19.25), self.acm.balance_for(ACCOUNT_TYPE.TAX))
def process_money(cls, money_str): """ Paypal sends money in the form "XXX 0.00" where XXX is the currency code and 0.00 is the amount """ if money_str: money_args = str(money_str).split(' ', 1) money_args.reverse() if money_args and len(money_args) == 2: return Money(*money_args) return None
class OrderFactory(factory.Factory): @classmethod def _prepare(cls, create, **kwargs): if "address" not in kwargs: if create: address = ShippingAddressFactory(user=kwargs["user"]) else: address = ShippingAddressFactory.build(user=kwargs["user"]) kwargs["address"] = address return super(OrderFactory, cls)._prepare(create, **kwargs) user = factory.SubFactory(UserFactory) delivery_charge = Money(10, money.CURRENCY["GBP"]) stall = factory.SubFactory(StallFactory)
def testMismatchedAmounts(self): """Test mismatching amounts""" wrong_amount = Money("1337.23", 'SEK') data = self.get_valid_IPN_call(wrong_amount) self.mock_ipn_call(data) preapproval = self.get_preapproval() self.assertEqual(preapproval.status, 'error') self.assertEqual(preapproval.status_detail, ("IPN amounts didn't match. Preapproval " "requested %s. Preapproval made %s" % (preapproval.money, wrong_amount)))
def test_balance_using_multi_legged_transaction(self): revenue = self.acm.account_for(ACCOUNT_TYPE.REVENUE) deferred = self.acm.account_for(ACCOUNT_TYPE.RECEIVABLES) receivables = self.acm.account_for(ACCOUNT_TYPE.DEFERRED) multi = Transaction(date(2000, 1, 4)) multi.add(Money(-700), revenue) multi.add(Money(500), receivables) multi.add(Money(200), deferred) multi.post() self.assertReallyEqual(Money(500), receivables.balance()) self.assertReallyEqual(Money(200), deferred.balance()) self.assertReallyEqual(Money(-700), revenue.balance())
def discount_amount(self): """ Calculates a discount for a product. The two reasons for a discount are: - The customer added a coupon code - There is free shipping for a product @return: Money object representing the discount """ if self.shipping_discount.amount > 0: return self.shipping_discount elif self.coupon_code: try: discount = _get_discount(self.cart, self.coupon_code) except (Discount.DoesNotExist, CouponAlreadyInCart, CouponAlreadyUsed): # Zero discount discount = Discount() else: discount = Discount() return Money(discount.calculate(self.pre_discount_total()), DEFAULT_CURRENCY)
def get_def_money(): currency = get_def_currency() if currency: return Money(0, currency) return None
def withdrawals(self, period): result = Money(0, self.currency) for entry in self.entries: if period.includes(entry.date()) and entry.amount().is_negative(): result += entry.amount() return result
def deposits(self, period): result = Money(0, self.currency) for entry in self.entries: if period.includes(entry.date()) and entry.amount().is_positive(): result += entry.amount() return result
class PaypalAdaptiveFactory(factory.DjangoModelFactory): FACTORY_FOR = PaypalAdaptive money = Money(1400, 'SEK') money_currency = 'SEK' created_date = datetime.datetime.now()
def __init__(self, *args, **kwargs): self.shipping_discount = Money("0", DEFAULT_CURRENCY) super(CartStall, self).__init__(*args, **kwargs)
def delivery_total(self): """Returns the delivery charges for this stall, considering all products in the cart. """ if self.address: country = self.address.country else: country = self.get_country() # This is a little complex so some explanation is in order. # # We loop through all the products, for each product if there is a # rule which matches the selected country we add the price of the # _extra_ items to the total. We also keep track of the largest # initial price. After we have been through all the products we # add the price of the largest initial item and subtract the extra # price of the largest initial item rule because otherwise it will # be charged twice, once for the initial and once for the already # counted extra. # This is the rule with the largest initial item price. We need to # store it to add to the total later. We also need the extra price # because we need to subtract that from the title. # # Ideally this would be a single variable rule object, unfortunately # the shipping rule for the rest of the world is represented as fields # on the shipping profile rather than a rule so we need both fields # here. largest_initial = None largest_initial_extra = None largest_shipping_discount = None largest_shipping_discout_extra = None total_shipping_price = money.Money(0, DEFAULT_CURRENCY) total_shipping_discount = Money(0, DEFAULT_CURRENCY) for cart_product in self.cart_products.all().prefetch_related( 'product__shipping_profile__shipping_rules__countries'): product = cart_product.product #Find the a shipping rule for the selected country shipping_price, shipping_price_extra = product.get_shipping_prices( self.speculative_country) shipping_discount, shipping_discout_extra = product.get_shipping_discounts( self.speculative_country) if largest_initial is None or shipping_price > largest_initial: largest_initial = shipping_price largest_shipping_discount = shipping_discount largest_initial_extra = shipping_price_extra largest_shipping_discout_extra = shipping_discout_extra total_shipping_price += shipping_price_extra * cart_product.quantity total_shipping_discount += shipping_discout_extra * cart_product.quantity if largest_initial is not None: total_shipping_price += largest_initial if largest_initial_extra is not None: total_shipping_price -= largest_initial_extra if largest_shipping_discount is not None: total_shipping_discount += largest_shipping_discount if largest_shipping_discout_extra is not None: total_shipping_discount -= largest_shipping_discout_extra self.shipping_discount = total_shipping_discount return total_shipping_price
def delivery(self): """ TODO: Pull from db. """ return Money("0", DEFAULT_CURRENCY)
class PaymentFactory(factory.Factory): status = Payment.STATUS_CREATED amount = Money(10, money.CURRENCY["GBP"]) pay_key = "some-pay-key" purchaser = factory.SubFactory(UserFactory)
def calculate_amount(cls, usage_event): return Money( usage_event.get_amount().get_amount() * usage_event.get_rate(), 'USD')
def __init__(self, request): # verify that the request is paypal's url = '%s?cmd=_notify-validate' % settings.PAYPAL_PAYMENT_HOST post_data = {} for k, v in request.POST.copy().iteritems(): post_data[k] = unicode(v).encode('utf-8') data = urllib.urlencode(post_data) verify_request = UrlRequest().call(url, data=data) # check code if verify_request.code != 200: raise IpnError('PayPal response code was %s' % verify_request.code) # check response raw_response = verify_request.response if raw_response != 'VERIFIED': raise IpnError('PayPal response was "%s"' % raw_response) # check transaction type raw_type = request.POST.get('transaction_type', '') allowed_types = [ IPN_TYPE_PAYMENT, IPN_TYPE_ADJUSTMENT, IPN_TYPE_PREAPPROVAL ] if raw_type in allowed_types: self.type = raw_type else: raise IpnError('Unknown transaction_type received: %s' % raw_type) self.process_transactions(request) try: # payments and adjustments define these self.status = request.POST.get('status', None) self.sender_email = request.POST.get('sender_email', None) self.action_type = request.POST.get('action_type', None) self.payment_request_date = IPN.process_date( request.POST.get('payment_request_date', None)) self.reverse_all_parallel_payments_on_error = request.POST.get( 'reverse_all_parallel_payments_on_error', 'false') == 'true' self.return_url = request.POST.get('return_url', None) self.cancel_url = request.POST.get('cancel_url', None) self.ipn_notification_url = request.POST.get( 'ipn_notification_url', None) self.pay_key = request.POST.get('pay_key', None) self.memo = request.POST.get('memo', None) self.fees_payer = request.POST.get('fees_payer', None) self.trackingId = request.POST.get('trackingId', None) self.preapproval_key = request.POST.get('preapproval_key', None) self.reason_code = request.POST.get('reason_code', None) # preapprovals define these self.approved = request.POST.get('approved', 'false') == 'true' self.current_number_of_payments = IPN.process_int( request.POST.get('current_number_of_payments', None)) self.current_total_amount_of_all_payments = IPN.process_money( request.POST.get('current_total_amount_of_all_payments', None)) self.current_period_attempts = IPN.process_int( request.POST.get('current_period_attempts', None)) self.currency_code = Currency( request.POST.get('currency_code', None)) self.date_of_month = IPN.process_int( request.POST.get('date_of_month', None)) self.day_of_week = IPN.process_int( request.POST.get('day_of_week', None), None) self.starting_date = IPN.process_date( request.POST.get('starting_date', None)) self.ending_date = IPN.process_date( request.POST.get('ending_date', None)) self.max_total_amount_of_all_payments = Money( request.POST.get('max_total_amount_of_all_payments', None), request.POST.get('currency_code', None)) self.max_amount_per_payment = IPN.process_money( request.POST.get('max_amount_per_payment', None)) self.max_number_of_payments = IPN.process_int( request.POST.get('max_number_of_payments', None)) self.payment_period = request.POST.get('payment_period', None) self.pin_type = request.POST.get('pin_type', None) except Exception, e: logger.error('Could not parse request') raise e