Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
mhieta committed Jan 16, 2024
2 parents 87e0432 + b5a3e8e commit 95e3815
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 12 deletions.
7 changes: 4 additions & 3 deletions parking_permits/models/parking_permit.py
Original file line number Diff line number Diff line change
Expand Up @@ -506,9 +506,10 @@ def get_price_change_list(self, new_zone, is_low_emission):
else:
# if the product is different or diff price is different,
# create a new price change item
price_change_vat = (diff_price * new_product.vat).quantize(
Decimal("0.0001")
)
price_change_vat = calc_vat_price(
diff_price, new_product.vat
).quantize(Decimal("0.0001"))

price_change_list.append(
{
"product": new_product.name,
Expand Down
12 changes: 11 additions & 1 deletion parking_permits/models/refund.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
from django.contrib.gis.db import models
from django.utils.translation import gettext_lazy as _

from parking_permits.utils import calc_vat_price

from .mixins import TimestampedModelMixin, UserStampedModelMixin

VAT_PERCENT = Decimal(0.24)
Expand Down Expand Up @@ -50,6 +52,10 @@ class Refund(TimestampedModelMixin, UserStampedModelMixin):
blank=True,
)

# this is hard coded at the moment. At some point should be
# a field we can calculate when the refund is generated.
vat_percent = VAT_PERCENT

class Meta:
verbose_name = _("Refund")
verbose_name_plural = _("Refunds")
Expand All @@ -59,4 +65,8 @@ def __str__(self):

@property
def vat(self):
return Decimal(self.amount) * VAT_PERCENT
"""Calculate the VAT amount.
The VAT amount is hard coded here because we do not know the % of individual
unused items in this model. In future this should probably be stored as a separate field.
"""
return calc_vat_price(self.amount, self.vat_percent)
10 changes: 6 additions & 4 deletions parking_permits/tests/models/test_parking_permit.py
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,7 @@ def test_parking_permit_change_price_list_when_prices_go_down(self):
self.assertEqual(price_change_list[0]["new_price"], Decimal("15"))
self.assertEqual(price_change_list[0]["price_change"], Decimal("-5"))
self.assertEqual(
price_change_list[0]["price_change_vat"], Decimal("-1.2")
price_change_list[0]["price_change_vat"], Decimal("-0.9677")
)
self.assertEqual(price_change_list[0]["month_count"], 2)
self.assertEqual(price_change_list[0]["start_date"], date(2021, 5, 1))
Expand All @@ -555,7 +555,7 @@ def test_parking_permit_change_price_list_when_prices_go_down(self):
self.assertEqual(price_change_list[1]["new_price"], Decimal("20"))
self.assertEqual(price_change_list[1]["price_change"], Decimal("-10"))
self.assertEqual(
price_change_list[1]["price_change_vat"], Decimal("-2.4")
price_change_list[1]["price_change_vat"], Decimal("-1.9355")
)
self.assertEqual(price_change_list[1]["month_count"], 6)
self.assertEqual(price_change_list[1]["start_date"], date(2021, 7, 1))
Expand Down Expand Up @@ -611,7 +611,7 @@ def test_parking_permit_change_price_list_when_prices_go_up(self):
self.assertEqual(price_change_list[0]["new_price"], Decimal("30"))
self.assertEqual(price_change_list[0]["price_change"], Decimal("20"))
self.assertEqual(
price_change_list[0]["price_change_vat"], Decimal("4.8")
price_change_list[0]["price_change_vat"], Decimal("3.8710")
)
self.assertEqual(price_change_list[0]["month_count"], 2)
self.assertEqual(
Expand All @@ -626,7 +626,9 @@ def test_parking_permit_change_price_list_when_prices_go_up(self):
self.assertEqual(price_change_list[1]["previous_price"], Decimal("15"))
self.assertEqual(price_change_list[1]["new_price"], Decimal("40"))
self.assertEqual(price_change_list[1]["price_change"], Decimal("25"))
self.assertEqual(price_change_list[1]["price_change_vat"], Decimal("6"))
self.assertEqual(
price_change_list[1]["price_change_vat"], Decimal("4.8387")
)
self.assertEqual(price_change_list[1]["month_count"], 6)
self.assertEqual(
price_change_list[1]["start_date"], date(CURRENT_YEAR, 7, 1)
Expand Down
16 changes: 16 additions & 0 deletions parking_permits/tests/models/test_refund.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from decimal import Decimal

from django.test import TestCase

from parking_permits.tests.factories.refund import RefundFactory


class TestRefund(TestCase):
def test_vat_zero_amount(self):
refund = RefundFactory(amount=0)
assert refund.vat == Decimal(0)

def test_vat(self):
refund = RefundFactory(amount=100)

self.assertAlmostEqual(refund.vat, Decimal(19.35), delta=Decimal("0.01"))
39 changes: 35 additions & 4 deletions parking_permits/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from datetime import datetime
from decimal import ROUND_UP, Decimal
from itertools import chain
from typing import Optional, Union

from ariadne import convert_camel_case_to_snake
from dateutil.relativedelta import relativedelta
Expand All @@ -17,6 +18,8 @@

HELSINKI_TZ = zoneinfo.ZoneInfo("Europe/Helsinki")

Currency = Optional[Union[str, float, Decimal]]


class DefaultOrderedDict(OrderedDict):
def __init__(self, default_factory=None, *a, **kw):
Expand Down Expand Up @@ -337,12 +340,40 @@ def flatten_dict(d, separator="__", prefix="", _output_ref=None) -> dict:
return output


def calc_net_price(gross_price, vat):
return Decimal(gross_price) / Decimal(1 + (vat or 0)) if gross_price else Decimal(0)
def calc_net_price(gross_price: Currency, vat: Currency) -> Decimal:
"""Returns the net price based on the gross and VAT e.g. 0.24
Net price is calculated thus:
gross / (1 + vat)
For example, gross 100 EUR, VAT 24% would be:
100 / 1.24 = ~80.64
If gross or vat is zero or None, returns zero.
"""
return (
Decimal(gross_price) / Decimal(1 + (Decimal(vat or 0)))
if gross_price and vat
else Decimal(0)
)

def calc_vat_price(gross_price, vat):
return gross_price - calc_net_price(gross_price, vat) if gross_price else Decimal(0)

def calc_vat_price(gross_price: Currency, vat: Currency) -> Decimal:
"""Returns the VAT price based on the gross and VAT e.g. 0.24
VAT price is equal to the gross minus the net.
For example, gross 100 EUR, VAT 24% would be net price of ~80.64.
VAT price would therefore be 100-80.64 = 19.36.
"""
return (
Decimal(gross_price) - calc_net_price(gross_price, vat)
if gross_price and vat
else Decimal(0)
)


def round_up(v):
Expand Down

0 comments on commit 95e3815

Please sign in to comment.