Skip to content

Commit

Permalink
Fix #35, round down money to cent
Browse files Browse the repository at this point in the history
  • Loading branch information
fangpenlin committed Aug 27, 2013
1 parent 8f49326 commit c68b7d9
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 2 deletions.
3 changes: 3 additions & 0 deletions billy/models/subscription.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from billy.models.transaction import TransactionModel
from billy.models.schedule import next_transaction_datetime
from billy.utils.generic import make_guid
from billy.utils.generic import round_down_cent


class SubscriptionCanceledError(RuntimeError):
Expand Down Expand Up @@ -126,6 +127,7 @@ def cancel(self, guid, prorated_refund=False):
# such as day or hour granularity?
rate = elapsed_seconds / total_seconds
amount = previous_transaction.amount * rate
amount = round_down_cent(amount)

tx_model = TransactionModel(self.session)
# make sure we will not refund zero dollar
Expand Down Expand Up @@ -189,6 +191,7 @@ def yield_transactions(self, now=None):
if subscription.discount is not None:
# TODO: what about float number round up?
amount *= (1 - subscription.discount)
amount = round_down_cent(amount)
# create the new transaction for this subscription
guid = tx_model.create(
subscription_guid=subscription.guid,
Expand Down
22 changes: 21 additions & 1 deletion billy/tests/test_models/test_subscription.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,27 @@ def test_subscription_cancel_with_prorated_refund_and_discount(self):
# so the amount of transaction is 7.5, and we refund half,
# then the refund amount should be 3.75
self.assertEqual(transaction.amount, decimal.Decimal('3.75'))
# TODO: what about float number round up issue?

def test_subscription_cancel_with_prorated_refund_rounding(self):
from billy.models.transaction import TransactionModel
model = self.make_one(self.session)
tx_model = TransactionModel(self.session)

with freeze_time('2013-06-01'):
with db_transaction.manager:
guid = model.create(
customer_guid=self.customer_tom_guid,
plan_guid=self.monthly_plan_guid,
)
model.yield_transactions()

# 17 / 30 days, the rate should be 0.56666...
with freeze_time('2013-06-18'):
with db_transaction.manager:
refund_guid = model.cancel(guid, prorated_refund=True)

transaction = tx_model.get(refund_guid)
self.assertEqual(transaction.amount, decimal.Decimal('5.66'))

def test_subscription_cancel_with_zero_refund(self):
model = self.make_one(self.session)
Expand Down
23 changes: 22 additions & 1 deletion billy/tests/test_utils/test_generic.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from __future__ import unicode_literals
import math
import unittest


Expand Down Expand Up @@ -28,3 +27,25 @@ def test_make_api_key(self):
# just make sure it is random
api_keys = [make_api_key() for _ in range(1000)]
self.assertEqual(len(set(api_keys)), 1000)

def test_round_down_cent(self):
from decimal import Decimal
from billy.utils.generic import round_down_cent

def assert_round_down(amount, expected):
self.assertEqual(
round_down_cent(Decimal(amount)),
Decimal(expected)
)

assert_round_down('0.0', '0.0')
assert_round_down('0.1', '0.1')
assert_round_down('0.11', '0.11')
assert_round_down('1.0', '1.0')
assert_round_down('1.12', '1.12')
assert_round_down('123.0', '123.0')
assert_round_down('0.123', '0.12')
assert_round_down('0.1234', '0.12')
assert_round_down('0.5566', '0.55')
assert_round_down('0.7788', '0.77')
assert_round_down('1.23456789', '1.23')
10 changes: 10 additions & 0 deletions billy/utils/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import os
import uuid
import decimal

B58_CHARS = b'123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
B58_BASE = len(B58_CHARS)
Expand Down Expand Up @@ -52,3 +53,12 @@ def make_api_key(size=32):
# however, this is good enough currently
random = os.urandom(size)
return b58encode(random)

def round_down_cent(amount):
"""Round down money value to cent (truncate to), for example, $5.66666
will be rounded to $5.66
:param amount: the money amount to be rounded
:return: the rounded money amount
"""
return amount.quantize(decimal.Decimal('.01'), rounding=decimal.ROUND_DOWN)

0 comments on commit c68b7d9

Please sign in to comment.