def get(self, request, format=None): fl = self.request.user.freelancer jrs = JobRequest.objects.filter(bookings__freelancer=fl) # TODO: Foreign currencies are ignored for now result = defaultdict(lambda: Money(0, "GBP")) for j in filter(lambda x: x.status in set(['CP', 'CF']), jrs): result[j.status] += j.client_pay_per_hour * j.duration # Converting for serialization for st in result.keys(): result[st] = { "amount": result[st].amount, "currency": result[st].currency.code } return Response(result, status=status.HTTP_200_OK)
def test_create_payment(self, get_current_host): self.init_projects() order = OrderFactory.create() DonationFactory.create(amount=Money(2000, NGN), order=order) order_payment = OrderPaymentFactory.create(payment_method='interswitchWebpay', order=order) adapter = InterswitchPaymentAdapter(order_payment) self.assertEqual(adapter.payment.amount, 200000) # Check generated payload payload = adapter._get_payload() self.assertEqual(payload['product_id'], '1234') self.assertEqual(payload['amount'], 200000) tenant = connection.tenant self.assertEqual(payload['txn_ref'], '{0}-{1}'.format(tenant.name, order_payment.id))
def test_format_money(self): # Two decimal places by default assert format_money(self.one_million_bucks) == 'US$1,000,000.00' # No decimal point without fractional part assert format_money(self.one_million_bucks, decimal_places=0) == 'US$1,000,000' # locale == pl_PL one_million_pln = Money('1000000', 'PLN') # Two decimal places by default assert format_money(one_million_pln, locale='pl_PL') == '1 000 000,00 zł' # overriden sign/format locale display default sign with locale group parameter assert format_money(self.one_million_bucks, locale='pl_PL') == 'US$1 000 000,00' # non overriden sign/format locale display default money sign with default group parameter assert format_money(self.one_million_bucks, locale='fr_FR') == 'US$1,000,000.00' # No decimal point without fractional part assert format_money(one_million_pln, locale='pl_PL', decimal_places=0) == '1 000 000 zł' # add different sign for money USD in locale pl_PL _sign('pl_PL', moneyed.USD, prefix='$') assert format_money(self.one_million_bucks, locale='pl_PL') == '$1 000 000,00' # default locale display correct money sign with default group parameter assert format_money(self.one_million_euros) == '1,000,000.00 €' # non overriden sign/format locale display default money sign with default group parameter assert format_money(self.one_million_euros, locale='fr_FR') == '1,000,000.00 €' # overriden sign/locale locale display default money sign with locale group parameter assert format_money(self.one_million_euros, locale='en_US') == '1,000,000.00 €' # add format for fr_FR locale _format("fr_FR", group_size=3, group_separator=" ", decimal_point=",", positive_sign="", trailing_positive_sign="", negative_sign="-", trailing_negative_sign="", rounding_method=ROUND_HALF_EVEN) # overriden format locale display correct sign with locale group parameter assert format_money(self.one_million_euros, locale='fr_FR') == '1 000 000,00 €'
def test_account_balance_after_out_of_order_ids_on_same_day(self): src = self.account() dst = self.account() # A more complex version of the above test_account_balance_after_out_of_order_ids() # Here we require a mix of ordering by pk and by date because some # transactions are dated on the same day, yet we still have to infer a deterministic order # from somewhere, so we use the pk src.transfer_to(dst, Money(50, "EUR"), date="2000-01-15") dst.transfer_to(src, Money(70, "EUR"), date="2000-01-15") src.transfer_to(dst, Money(110, "EUR"), date="2000-01-05") src.transfer_to(dst, Money(100, "EUR"), date="2000-01-05") legs = Leg.objects.filter( account=dst).order_by("transaction__date").all() self.assertEqual(legs[0].account_balance_after(), Balance("110", "EUR")) self.assertEqual(legs[1].account_balance_after(), Balance("210", "EUR")) self.assertEqual(legs[2].account_balance_after(), Balance("260", "EUR")) self.assertEqual(legs[3].account_balance_after(), Balance("190", "EUR"))
def to_python(self, value): if not isinstance(value, tuple): raise Exception("Invalid money input, expected sum and currency.") amount = super(MoneyField, self).to_python(value[0]) if amount is None: return amount currency = value[1] if not currency: raise forms.ValidationError(_(u'Currency is missing')) currency = currency.upper() if not CURRENCIES.get(currency, False) or currency == DEFAULT_CURRENCY_CODE: raise forms.ValidationError(_(u"Unrecognized currency type '%s'." % currency)) return Money(amount=amount, currency=currency)
def test_format_money(self): # Two decimal places by default assert format_money(self.one_million_bucks) == 'US$1,000,000.00' # No decimal point without fractional part assert format_money(self.one_million_bucks, decimal_places=0) == 'US$1,000,000' # Locale format not included, should fallback to DEFAULT assert format_money(self.one_million_bucks, locale='es_ES') == 'US$1,000,000.00' # locale == pl_PL one_million_pln = Money('1000000', 'PLN') # Two decimal places by default assert format_money(one_million_pln, locale='pl_PL') == '1 000 000,00 zł' assert format_money(self.one_million_bucks, locale='pl_PL') == '1 000 000,00 USD' # No decimal point without fractional part assert format_money(one_million_pln, locale='pl_PL', decimal_places=0) == '1 000 000 zł'
def __set__(self, obj, value): if isinstance(value, tuple): value = Money(amount=value[0], currency=value[1]) if isinstance(value, Money): obj.__dict__[self.field.name] = value.amount setattr(obj, self.currency_field_name, smart_unicode(value.currency)) elif isinstance(value, ExpressionNode): if isinstance(value.children[1], Money): value.children[1] = value.children[1].amount obj.__dict__[self.field.name] = value else: if value: value = str(value) obj.__dict__[self.field.name] = self.field.to_python(value)
def test_update_payment(self, get_current_host): """ Play some posts that Vitepay might fire at us. """ order_payment = VitepayOrderPaymentFactory.create(amount=Money(2000, XOF)) payment = VitepayPaymentFactory.create(order_id='opc-1', order_payment=order_payment) authenticity = '69E78BC6C64D43DA76DEB90F911AF213DA9DE89D' update_view = reverse('vitepay-status-update') data = { 'success': 1, 'order_id': payment.order_id, 'authenticity': authenticity } response = self.client.post(update_view, data, format='multipart') self.assertEqual(response.content, '{"status": "1"}')
def test_donation_total_stats_convert_currencies(self): self.some_project.status = self.campaign_status self.some_project.save() self.order1 = OrderFactory.create(user=self.another_user, status=StatusDefinition.SUCCESS) self.donation1 = DonationFactory.create(amount=Money(1000, 'EUR'), order=self.order1, project=self.some_project, fundraiser=None) self.order2 = OrderFactory.create(user=None, status=StatusDefinition.SUCCESS) self.donation2 = DonationFactory.create(amount=Money(1000, 'USD'), order=self.order2, project=self.some_project, fundraiser=None) self.assertEqual(self.stats.donated_total, Money(2500, 'EUR')) # People involved: # - campaigner # - donator (another_user) # - donator (anon) self.assertEqual(self.stats.people_involved, 3)
def test_new_format_money_fr(self): # locale == fr_FR one_million_eur = Money('1000000', 'EUR') one_million_cad = Money('1000000', 'CAD') assert (new_format_money(one_million_eur, locale='fr_FR').replace( '\u202f', ' ').replace('\xa0', ' ') == '1 000 000,00 €') assert (new_format_money(self.one_million_bucks, locale='fr_FR').replace( '\u202f', ' ').replace('\xa0', ' ') == '1 000 000,00 $US') assert (new_format_money(one_million_cad, locale='fr_FR').replace( '\u202f', ' ').replace('\xa0', ' ') == '1 000 000,00 $CA') # No decimal point without fractional part assert (new_format_money(one_million_eur, locale='fr_FR', decimal_places=0).replace( '\u202f', ' ').replace('\xa0', ' ') == '1 000 000 €') # locale == fr_CA assert (new_format_money(one_million_cad, locale='fr_CA').replace( '\u202f', ' ').replace('\xa0', ' ') == '1 000 000,00 $') assert (new_format_money(self.one_million_bucks, locale='fr_CA').replace( '\u202f', ' ').replace('\xa0', ' ') == '1 000 000,00 $US') assert (new_format_money(one_million_eur, locale='fr_CA').replace( '\u202f', ' ').replace('\xa0', ' ') == '1 000 000,00 €')
def test_format_money(self): # Two decimal places by default assert format_money(self.one_million_bucks) == 'US$1,000,000.00' # No decimal point without fractional part assert format_money(self.one_million_bucks, decimal_places=0) == 'US$1,000,000' # locale == pl_PL one_million_pln = Money('1000000', 'PLN') # Two decimal places by default assert format_money(one_million_pln, locale='pl_PL') == '1 000 000,00 zł' assert format_money(self.one_million_bucks, locale='pl_PL') == '1 000 000,00 USD' # No decimal point without fractional part assert format_money(one_million_pln, locale='pl_PL', decimal_places=0) == '1 000 000 zł'
def __rmod__(self, other): """ Calculate percentage of an amount. The left-hand side of the operator must be a numeric value. Example: >>> money = Money(200, 'USD') >>> 5 % money USD 10.00 """ if isinstance(other, Money): raise TypeError('Invalid __rmod__ operation') else: return Money( amount=(Decimal(str(other)) * self.amount / 100), currency=self.currency)
def test_expired_enough_by_matching(self): """ Less donated than requested but with matching- status done complete """ order = OrderFactory.create() donation = DonationFactory.create(project=self.expired_project, order=order, amount=2500) donation.save() order.locked() order.save() order.success() order.save() self.expired_project.amount_extra = Money(2500, 'EUR') self.expired_project.save() self.assertEqual(self.expired_project.payout_status, 'needs_approval') self.failUnless(self.expired_project.status == self.complete)
def test_db_zero_check(self): """Check the DB ensures non-zero leg amounts""" account1 = self.account() account2 = self.account() with db_transaction.atomic(): transaction = Transaction.objects.create() leg1 = Leg.objects.create(transaction=transaction, account=account1, amount=100) Leg.objects.create(transaction=transaction, account=account2, amount=-100) with self.assertRaises(IntegrityError): # Use update() to bypass the check in Leg.save() Leg.objects.filter(pk=leg1.pk).update(amount=Money(0, "EUR"))
def test_create_success_payment(self, charge, get_current_host): """ Test Flutterwave payment that turns to success without otp (one time pin) """ self.init_projects() order = OrderFactory.create() DonationFactory.create(amount=Money(150000, NGN), order=order) order_payment = OrderPaymentFactory.create( payment_method='flutterwaveCreditcard', order=order, integration_data=integration_data) adapter = FlutterwaveCreditcardPaymentAdapter(order_payment) authorization_action = adapter.get_authorization_action() self.assertEqual(adapter.payment.amount, '150000.00') self.assertEqual(adapter.payment.status, 'authorized') self.assertEqual(adapter.payment.transaction_reference, 'FLW001') self.assertEqual(authorization_action, {"type": "success"})
def test_model_zero_check(self): """Check the model ensures non-zero leg amounts""" account1 = self.account() account2 = self.account() with db_transaction.atomic(): transaction = Transaction.objects.create() Leg.objects.create(transaction=transaction, account=account1, amount=100) Leg.objects.create(transaction=transaction, account=account2, amount=-100) leg3 = Leg(transaction=transaction, account=account2, amount=Money(0, "EUR")) self.assertRaises(exceptions.ZeroAmountError, leg3.save)
def check_payment_status(self): # If we have a transaction reference, then use that if self.payment.transaction_reference and self.payment.transaction_reference != '4': response = self.client.get_transactions( transaction_type='Payment', transaction=self.payment.transaction_reference ) else: response = self.client.get_transactions( transaction_type='Payment', transaction_reference=self.order_payment.id ) self.payment.update_response = json.dumps(response) data = response['content'] if len(data) == 0: self.payment.status = StatusDefinition.FAILED self.payment.save() raise PaymentException('Payment could not be verified yet. Payment not found.') else: payment = data[0] # Make sure we set the right properties payment['transaction_reference'] = payment['transaction'] for k, v in payment.iteritems(): setattr(self.payment, k, v) if self.payment.transaction_amount != self.payment.order_payment.amount.amount: # Update donation amount based on the amount registered at Lipisha amount = Money(self.payment.transaction_amount, 'KES') donation = self.payment.order_payment.order.donations.all()[0] self.payment.order_payment.amount = amount donation.amount = amount donation.save() self.payment.order_payment.save() self.payment.status = self._get_mapped_status(self.payment.transaction_status) if self.payment.status in ['settled', 'authorized']: self.order_payment.set_authorization_action({'type': 'success'}) self.payment.save()
def test_create_success_payment(self, mock_client): """ Test Flutterwave payment that turns to success without otp (one time pin) """ instance = mock_client.return_value instance.create.return_value = {'order_key': 123, 'order_id': 123} instance.service.PaymentRequest.return_value = "2001! Success, Waiting Confirmation !747" instance.service.ProcessPayment.return_value = "4005! This payment is not yet Approved" integration_data = {'mobile': '123456789'} order = OrderFactory.create() DonationFactory.create(amount=Money(70, USD), order=order) order_payment = OrderPaymentFactory.create( payment_method='telesomZaad', order=order, integration_data=integration_data) adapter = TelesomPaymentAdapter(order_payment) authorization_action = adapter.get_authorization_action() self.assertEqual(int(adapter.payment.amount), 70) self.assertEqual(adapter.payment.status, 'started') self.assertEqual(adapter.payment.transaction_reference, '747') self.assertEqual( authorization_action, { "payload": { "method": "telesom-sms", "text": "Confirm the payment by SMS" }, "type": "step2", }) # Now confirm the payment by user and have gateway send a success instance.service.ProcessPayment.return_value = "2001! Your account was Credited with $5.0000 Charge fee $ 0" order_payment.integration_data = {} adapter = TelesomPaymentAdapter(order_payment) adapter.check_payment_status() authorization_action = adapter.get_authorization_action() self.assertEqual(int(adapter.payment.amount), 70) self.assertEqual(adapter.payment.status, 'settled') self.assertEqual(authorization_action, {"type": "success"})
def test_gt(self): x = MultiMoney(Money(1, 'BTC'), Money(-1, 'USD')) y = MultiMoney(Money(0.5, 'BTC')) assert not x > y one_buck_piggy = MultiMoney(Money(amount=1, currency=self.USD)) assert MultiMoney(self.one_million_bucks) > one_buck_piggy assert self.one_mixed_fortune > one_buck_piggy one_buck_thousand_bitcoin_piggy = one_buck_piggy + self.one_thousand_bitcoins assert self.one_mixed_fortune > one_buck_thousand_bitcoin_piggy assert not MultiMoney(Money(currency='USD')) > MultiMoney() assert not MultiMoney() > self.one_mixed_fortune assert not one_buck_thousand_bitcoin_piggy > self.one_mixed_fortune assert not MultiMoney() > MultiMoney() assert not MultiMoney() > MultiMoney(Money())
def test_create_otp_payment_failure(self, charge, validate, get_current_host): """ Test Flutterwave payment that needs a otp (one time pin) """ self.init_projects() order = OrderFactory.create() user = BlueBottleUserFactory(first_name=u'T\xc3\xabst user') DonationFactory.create(amount=Money(20000, NGN), order=order) order_payment = OrderPaymentFactory.create( payment_method='flutterwaveCreditcard', order=order, user=user, integration_data=integration_data) adapter = FlutterwaveCreditcardPaymentAdapter(order_payment) authorization_action = adapter.get_authorization_action() self.assertEqual(adapter.payment.amount, '20000.00') self.assertEqual(adapter.payment.status, 'started') self.assertEqual(adapter.payment.transaction_reference, 'FLW004') self.assertEqual( authorization_action, { "type": "step2", "payload": { "method": "flutterwave-otp", "text": "Kindly enter the OTP sent to 234803***9051 and henry***********ture.com." } }) # Now set the otp order_payment.integration_data = {'otp': '123456'} order_payment.save() adapter = FlutterwaveCreditcardPaymentAdapter(order_payment) with self.assertRaises(PaymentException): adapter.check_payment_status() self.assertEqual(adapter.payment.status, 'failed')
def _handle_rewards(self, data): """Expected fields for Rewards import: project (string<slug>) title (string) description (string) amount (string) text (string) """ if data['amount']: try: project = Project.objects.get(slug=data['project']) reward, _ = Reward.objects.get_or_create( project=project, title=data['title'], amount=Money(data['amount'] or 0.0, 'EUR'), limit=data['limit'] or 0) self._generic_import(reward, data, excludes=['amount', 'limit', 'project']) reward.description = reward.description[:500] reward.save() except (Project.DoesNotExist, ValueError): pass
def test_arithmetic_operations_return_real_subclass_instance(self): """ Arithmetic operations on a subclass instance should return instances in the same subclass type. """ extended_money = ExtendedMoney(amount=2, currency=self.USD) operated_money = +extended_money assert type(extended_money) == type(operated_money) operated_money = -extended_money assert type(extended_money) == type(operated_money) operated_money = ExtendedMoney(amount=1, currency=self.USD) + ExtendedMoney(amount=1, currency=self.USD) assert type(extended_money) == type(operated_money) operated_money = ExtendedMoney(amount=3, currency=self.USD) - Money(amount=1, currency=self.USD) assert type(extended_money) == type(operated_money) operated_money = (1 * extended_money) assert type(extended_money) == type(operated_money) operated_money = (extended_money / 1) assert type(extended_money) == type(operated_money) operated_money = abs(ExtendedMoney(amount=-2, currency=self.USD)) assert type(extended_money) == type(operated_money) operated_money = (50 % ExtendedMoney(amount=4, currency=self.USD)) assert type(extended_money) == type(operated_money)
def test_new_format_money(self): # Two decimal places by default assert (new_format_money(self.one_million_bucks, locale='en_US') == '$1,000,000.00') # No decimal point without fractional part assert (new_format_money(self.one_million_bucks, decimal_places=0) == '$1,000,000') # Locale format not included, should fallback to DEFAULT assert (new_format_money(self.one_million_bucks, locale='es_ES').replace( '\xa0', ' ') == '1.000.000,00 US$') # locale == pl_PL one_million_pln = Money('1000000', 'PLN') # Two decimal places by default assert (new_format_money(one_million_pln, locale='pl_PL').replace( '\xa0', ' ') == '1 000 000,00 zł') assert (new_format_money(self.one_million_bucks, locale='pl_PL').replace( '\xa0', ' ') == '1 000 000,00 USD') # No decimal point without fractional part assert (new_format_money(one_million_pln, locale='pl_PL', decimal_places=0).replace( '\xa0', ' ') == '1 000 000 zł')
def test_init_default_currency(self): one_million = self.one_million_decimal one_million_dollars = Money(amount=one_million) # No currency given! assert one_million_dollars.amount == one_million assert one_million_dollars.currency == DEFAULT_CURRENCY
def test_init_string_currency_code(self): one_million_dollars = Money(amount=self.one_million_decimal, currency='usd') assert one_million_dollars.amount == self.one_million_decimal assert one_million_dollars.currency == self.USD
def test_init(self): one_million_dollars = Money(amount=self.one_million_decimal, currency=self.USD) assert one_million_dollars.amount == self.one_million_decimal assert one_million_dollars.currency == self.USD
def setup_method(self, method): self.one_million_decimal = Decimal('1000000') self.USD = CURRENCIES['USD'] self.one_million_bucks = Money(amount=self.one_million_decimal, currency=self.USD)
def test_decimal_doesnt_use_str_when_dividing(self): m = Money('15.60', 'GBP') a = CustomDecimal('3.2') result = m / a assert result == Money('4.875', 'GBP')
def test_decimal_doesnt_use_str_when_multiplying(self): m = Money('531', 'GBP') a = CustomDecimal('53.313') result = m * a assert result == Money('28309.203', 'GBP')
def test_bool(self): assert bool(Money(amount=1, currency=self.USD)) assert not bool(Money(amount=0, currency=self.USD))