Skip to content
This repository has been archived by the owner on May 25, 2023. It is now read-only.

Commit

Permalink
Rounding for decimal calculations
Browse files Browse the repository at this point in the history
  • Loading branch information
edwinvandeven committed Aug 17, 2019
1 parent 3dc91d0 commit 0ae5e49
Show file tree
Hide file tree
Showing 9 changed files with 56 additions and 66 deletions.
2 changes: 1 addition & 1 deletion models/700_scheduler_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ def send_mail_failed(cuID):
payment = mollie.payments.create({
'amount': {
'currency': CURRENCY,
'value': format(invoice_amounts.TotalPriceVAT, '.2f')
'value': str(invoice_amounts.TotalPriceVAT)
},
'customerId': mollie_customer_id,
'sequenceType': 'recurring', # important
Expand Down
43 changes: 15 additions & 28 deletions models/db_tables.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# coding: utf8
import datetime
import pytz
from decimal import Decimal, ROUND_HALF_UP

from gluon.scheduler import Scheduler
from gluon import current
Expand All @@ -22,7 +23,6 @@
from general_helpers import create_locations_dict
from general_helpers import create_classtypes_dict

from decimal import Decimal, ROUND_HALF_UP

# init scheduler
scheduler = Scheduler(
Expand Down Expand Up @@ -734,7 +734,7 @@ def define_sys_organizations():
label=T('Link to Privacy notice')),
Field('PrivacyNoticeVersion',
label=T('Privacy notice version')),
Field('ReportsClassPrice', 'float',
Field('ReportsClassPrice', 'decimal(20,2)',
readable=False,
writable=False,
represent=represent_decimal_as_amount,
Expand Down Expand Up @@ -1287,7 +1287,7 @@ def define_school_classcards():
Field('Description',
represent=lambda value, row: value or '',
label=T('Description')),
Field('Price', 'float', required=True,
Field('Price', 'decimal(20,2)', required=True,
requires=IS_DECIMAL_IN_RANGE(0,99999999, dot='.',
error_message=T('Too small or too large')),
#represent = lambda value, row: SPAN(CURRSYM , ' ', format(value, '.2f')),
Expand Down Expand Up @@ -2908,7 +2908,7 @@ def define_customers_subscriptions():
Field('Note', 'text',
represent=lambda value, row: value or '',
label=T("Note")),
Field('CreditsRemaining', 'float',
Field('CreditsRemaining', 'decimal(20,2)',
readable=False,
writable=False), # no actual data is stored, but used to map raw sql into DAL
Field('PeriodCreditsAdded', 'float',
Expand Down Expand Up @@ -4671,11 +4671,8 @@ def compute_invoice_item_total_price(row):
"""
Returns the total price for an invoice item
"""
total_price_vat = Decimal(row.TotalPriceVAT)

total = Decimal(Decimal(total_price_vat - row.VAT).quantize(Decimal('.01'),
rounding=ROUND_HALF_UP))
return total
total_price = Decimal(row.TotalPriceVAT) - row.VAT
return total_price.quantize(Decimal('.01'), rounding=ROUND_HALF_UP)


def compute_invoice_item_vat(row):
Expand All @@ -4686,15 +4683,10 @@ def compute_invoice_item_vat(row):
if not tID:
vat = 0
else:
vat_rate = db.tax_rates(tID).Percentage / 100

total_price_vat = float(row.TotalPriceVAT)
vat = total_price_vat - (total_price_vat / (1 + vat_rate))

vat = Decimal(Decimal(vat).quantize(Decimal('.01'),
rounding=ROUND_HALF_UP))
vat_rate = db.tax_rates(tID).Percentage / Decimal(100)
vat = Decimal(row.TotalPriceVAT) - (Decimal(row.TotalPriceVAT) / (Decimal(1) + vat_rate))

return vat
return vat.quantize(Decimal('.01'), rounding=ROUND_HALF_UP)


def define_invoices_amounts():
Expand Down Expand Up @@ -4838,13 +4830,8 @@ def compute_receipt_item_vat(row):
if not tID:
vat = 0
else:
vat_rate = db.tax_rates(tID).Percentage / 100

total_price_vat = float(row.TotalPriceVAT)
vat = total_price_vat - (total_price_vat / (1 + vat_rate))

vat = Decimal(Decimal(vat).quantize(Decimal('.01'),
rounding=ROUND_HALF_UP))
vat_rate = db.tax_rates(tID).Percentage / Decimal(100)
vat = row.TotalPriceVAT - (row.TotalPriceVAT / (Decimal(1) + vat_rate))

return vat

Expand Down Expand Up @@ -4901,7 +4888,7 @@ def represent_tax_rate(value, row):


def define_tax_rates():
float_error = T('Please enter a value between 0 and 100')
percentage_error = T('Please enter a value between 0 and 100')

db.define_table('tax_rates',
Field('Archived', 'boolean',
Expand All @@ -4912,9 +4899,9 @@ def define_tax_rates():
Field('Name',
requires=IS_NOT_EMPTY(),
label=T('Name')),
Field('Percentage', 'float',
requires=IS_FLOAT_IN_RANGE(0,100, dot='.',
error_message=float_error),
Field('Percentage', 'decimal(20,2)',
requires=IS_DECIMAL_IN_RANGE(0,100, dot='.',
error_message=percentage_error),
comment='A percentage as numbers only is expected (without %). Use " . " for decimals.',
label=T('Percentage')),
Field('VATCodeID',
Expand Down
4 changes: 2 additions & 2 deletions modules/openstudio/os_invoice.py
Original file line number Diff line number Diff line change
Expand Up @@ -968,7 +968,7 @@ def item_add_teacher_class_attendance_credit_payment(self,

tax_rate = db.tax_rates(tax_rates_id)
if tax_rate:
percentage = float(tax_rate.Percentage / 100)
percentage = tax_rate.Percentage / Decimal(100)
price = rate * (1 + percentage)
else:
price = rate
Expand Down Expand Up @@ -1016,7 +1016,7 @@ def item_add_teacher_class_credit_travel_allowance(self,
cls = Class(clsID, date)

tax_rate = db.tax_rates(tax_rates_id)
percentage = float(tax_rate.Percentage / 100)
percentage = tax_rate.Percentage / Decimal(100)
price = amount * (1 + percentage)

# add item to invoice
Expand Down
22 changes: 12 additions & 10 deletions tests/controllers/populate_os_tables.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#!/usr/bin/env python

import datetime
from decimal import Decimal

from gluon.contrib.populate import populate

from setup_profile_tests import setup_profile_tests
Expand Down Expand Up @@ -278,8 +280,8 @@ def populate_customers_with_subscriptions(web2py,
)

# tax rates (1) = 21%
TotalPrice = round(ss_one_price / 1.21, 2)
VAT = round(ss_one_price - TotalPrice, 2)
TotalPrice = ss_one_price / Decimal(1.21)
VAT = ss_one_price - TotalPrice

web2py.db.invoices_amounts.insert(
invoices_id = iID,
Expand Down Expand Up @@ -410,8 +412,8 @@ def populate_customers_with_classcards(web2py,
)

# tax rates (1) = 21%
TotalPrice = round(scd.Price / 1.21, 2)
VAT = round(scd.Price - TotalPrice, 2)
TotalPrice = scd.Price / Decimal(1.21)
VAT = scd.Price - TotalPrice

web2py.db.invoices_amounts.insert(
invoices_id=iID,
Expand Down Expand Up @@ -486,8 +488,8 @@ def populate_customers_with_memberships(web2py,
)

# tax rates (1) = 21%
TotalPrice = round(sm.Price / 1.21, 2)
VAT = round(sm.Price - TotalPrice, 2)
TotalPrice = sm.Price / Decimal(1.21)
VAT = sm.Price - TotalPrice

web2py.db.invoices_amounts.insert(
invoices_id=iID,
Expand Down Expand Up @@ -978,8 +980,8 @@ def prepare_classes(web2py,
)

# tax rates (1) = 21%
TotalPrice = round(trial_price / 1.21, 2)
VAT = round(trial_price - TotalPrice, 2)
TotalPrice = trial_price / Decimal(1.21)
VAT = trial_price - TotalPrice

web2py.db.invoices_amounts.insert(
invoices_id=iID,
Expand Down Expand Up @@ -1023,8 +1025,8 @@ def prepare_classes(web2py,
)

# tax rates (1) = 21%
TotalPrice = round(dropin_price / 1.21, 2)
VAT = round(dropin_price - TotalPrice, 2)
TotalPrice = dropin_price / Decimal(1.21)
VAT = dropin_price - TotalPrice

web2py.db.invoices_amounts.insert(
invoices_id=iID,
Expand Down
13 changes: 6 additions & 7 deletions tests/controllers/test_04_classes_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,10 @@ def test_revenue(client, web2py):
assert client.status == 200

prices = web2py.db.classes_price(1)
assert format(prices.Trial, '.2f') in client.text
assert str(prices.Trial) in client.text

tp = web2py.db.teachers_payment_fixed_rate_default(1)
assert format(tp.ClassRate, '.2f') in client.text
assert str(tp.ClassRate) in client.text


def test_revenue_export_preview(client, web2py):
Expand All @@ -124,12 +124,11 @@ def test_revenue_export_preview(client, web2py):
url = '/classes/revenue_export_preview?clsID=1&date=2014-01-06'
client.get(url)
assert client.status == 200

prices = web2py.db.classes_price(1)
assert format(prices.Trial, '.2f') in client.text
assert str(prices.Trial) in client.text

tp = web2py.db.teachers_payment_fixed_rate_default(1)
assert format(tp.ClassRate, '.2f') in client.text
assert str(tp.ClassRate) in client.text


# def test_revenue_export(client, web2py):
Expand Down Expand Up @@ -1253,7 +1252,7 @@ def test_class_price_add(client, web2py):
assert client.status == 200

assert web2py.db(web2py.db.classes_price).count() == 2
price = format(web2py.db.classes_price(2).Dropin, '.2f')
price = str(web2py.db.classes_price(2).Dropin)

assert price in client.text

Expand Down Expand Up @@ -1287,7 +1286,7 @@ def test_class_price_edit(client, web2py):

assert web2py.db(web2py.db.classes_price).count() == 1

price = format(web2py.db.classes_price(1).Dropin, '.2f')
price = str(web2py.db.classes_price(1).Dropin)

assert price in client.text

Expand Down
8 changes: 4 additions & 4 deletions tests/controllers/test_06_school_properties_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def test_memberships(client, web2py):
sm = web2py.db.school_memberships(1)

assert sm.Name in client.text
assert format(sm.Price, '.2f') in client.text
assert str(sm.Price) in client.text


def test_membership_add(client, web2py):
Expand Down Expand Up @@ -141,7 +141,7 @@ def test_school_subscriptions_index_and_current_price(client, web2py):
assert client.status == 200
assert 'Subscriptions' in client.text

assert format(123456, '.2f') in client.text
assert str(123456) in client.text


def test_school_subscriptions_show_organization(client, web2py):
Expand Down Expand Up @@ -243,7 +243,7 @@ def test_subscription_price_add(client, web2py):
client.post(url, data=data)
assert client.status == 200
assert 'Edit subscription' in client.text # verify redirection
assert format(data['Price'], '.2f') in client.text
assert str(data['Price']) in client.text

assert web2py.db(web2py.db.school_subscriptions_price).count() == 1

Expand Down Expand Up @@ -279,7 +279,7 @@ def test_subscription_price_edit(client, web2py):
assert client.status == 200
assert 'Edit subscription' in client.text # verify redirection

assert format(data['Price'], '.2f') in client.text
assert str(data['Price']) in client.text

assert web2py.db(web2py.db.school_subscriptions_price).count() == 1

Expand Down
2 changes: 1 addition & 1 deletion tests/controllers/test_20_api_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,7 @@ def test_school_subscriptions_get_json(client, web2py):
subscription = web2py.db.school_subscriptions(1)
subscription_price = web2py.db.school_subscriptions_price(1)
assert json['data'][0]['Name'] == subscription.Name
assert json['data'][0]['Price'] == subscription_price.Price
assert json['data'][0]['Price'] == str(subscription_price.Price)


def test_school_classcards_get_json(client, web2py):
Expand Down
20 changes: 10 additions & 10 deletions tests/controllers/test_301_shop_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -535,8 +535,8 @@ def test_classes_book_options(client, web2py):

# check drop in and trial price listing
class_prices = web2py.db.classes_price(1)
assert format(class_prices.Dropin, '.2f') in client.text
assert format(class_prices.Trial, '.2f') in client.text
assert str(class_prices.Dropin) in client.text
assert str(class_prices.Trial) in client.text

# Check request review check-in option not available
assert 'Request review' not in client.text
Expand Down Expand Up @@ -568,8 +568,8 @@ def test_classes_book_options_trial_disabled_from_system_settings(client, web2py

# check drop in and trial price listing
class_prices = web2py.db.classes_price(1)
assert format(class_prices.Dropin, '.2f') in client.text
assert format(class_prices.Trial, '.2f') not in client.text
assert str(class_prices.Dropin) in client.text
assert str(class_prices.Trial) not in client.text

# Check request review check-in option not available
assert 'Request review' not in client.text
Expand Down Expand Up @@ -650,8 +650,8 @@ def test_classes_book_options_dropin_trial_membership_prices(client, web2py):

# check drop in and trial price listing
class_prices = web2py.db.classes_price(1)
assert format(class_prices.DropinMembership, '.2f') in client.text
assert format(class_prices.TrialMembership, '.2f') in client.text
assert str(class_prices.DropinMembership) in client.text
assert str(class_prices.TrialMembership) in client.text


def test_classes_book_options_not_yet_open(client, web2py):
Expand Down Expand Up @@ -1797,7 +1797,7 @@ def test_event_price(client, web2py):

# Verify regular price
wsp = web2py.db.workshops_products(1)
assert format(wsp.Price, '.2f') + '</span>' in client.text
assert str(wsp.Price) + '</span>' in client.text


def test_event_price_earlybird(client, web2py):
Expand All @@ -1821,7 +1821,7 @@ def test_event_price_earlybird(client, web2py):

# Verify regular price
wsp = web2py.db.workshops_products(1)
assert format(wsp.PriceEarlybird, '.2f') + '</span>' in client.text
assert str(wsp.PriceEarlybird) + '</span>' in client.text


def test_event_price_subscription(client, web2py):
Expand Down Expand Up @@ -1849,7 +1849,7 @@ def test_event_price_subscription(client, web2py):

# Verify regular price
wsp = web2py.db.workshops_products(1)
assert format(wsp.PriceSubscription, '.2f') + '</span>' in client.text
assert str(wsp.PriceSubscription) + '</span>' in client.text


def test_event_price_subscription_earlybird(client, web2py):
Expand Down Expand Up @@ -1881,7 +1881,7 @@ def test_event_price_subscription_earlybird(client, web2py):

# Verify regular price
wsp = web2py.db.workshops_products(1)
assert format(wsp.PriceSubscriptionEarlybird, '.2f') + '</span>' in client.text
assert str(wsp.PriceSubscriptionEarlybird) + '</span>' in client.text


def test_event_ticket_requires_complete_profile(client, web2py):
Expand Down
8 changes: 5 additions & 3 deletions tests/controllers/test_302_mollie_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import datetime
import calendar
from decimal import Decimal, ROUND_HALF_UP

from setup_profile_tests import setup_profile_tests

Expand Down Expand Up @@ -92,17 +93,18 @@ def test_order_paid_delivery_invoice(client, web2py):
ssu = web2py.db.school_subscriptions(1)
ssu_price = web2py.db.school_subscriptions_price(1)
sm = web2py.db.school_memberships(1)
donation_price = 100 # 100 for the donation is fixed in the population of the tables
donation_price = Decimal(100) # 100 for the donation is fixed in the population of the tables

period_start = datetime.date.today()
period_end = get_last_day_month(period_start)

delta = period_end - period_start
days = delta.days + 1
total_days = period_end.day
ssu_calculated_price = round(float(days) / float(total_days) * float(ssu_price.Price), 2)
ssu_calculated_price = Decimal(days) / Decimal(total_days) * Decimal(ssu_price.Price)

price = round(scd.Price + wsp.Price + class_price.Dropin + ssu_calculated_price + sm.Price + donation_price, 2)
price = scd.Price + wsp.Price + class_price.Dropin + ssu_calculated_price + sm.Price + donation_price
price = price.quantize(Decimal('.01'), rounding=ROUND_HALF_UP)

#print web2py.db().select(web2py.db.customers_orders.ALL)

Expand Down

0 comments on commit 0ae5e49

Please sign in to comment.