forked from s3lph/matemat
Moved currency formatting/parsing into generic util package
This commit is contained in:
parent
a4930d268d
commit
b1a56f59fc
9 changed files with 62 additions and 61 deletions
2
doc
2
doc
|
@ -1 +1 @@
|
|||
Subproject commit 92e168a288b396a7a97d200b80affdf8690bd03d
|
||||
Subproject commit 5335524d3e57c7551f31c7e21fc04c464b23429a
|
0
matemat/util/__init__.py
Normal file
0
matemat/util/__init__.py
Normal file
56
matemat/util/currency_format.py
Normal file
56
matemat/util/currency_format.py
Normal file
|
@ -0,0 +1,56 @@
|
|||
|
||||
|
||||
def format_chf(value: int, with_currencysign: bool = True) -> str:
|
||||
"""
|
||||
Formats a centime value into a commonly understood representation ("CHF -13.37").
|
||||
|
||||
:param value: The value to format, in centimes.
|
||||
:param with_currencysign: Whether to include the currency prefix ("CHF ") in the output.
|
||||
:return: A human-readable string representation.
|
||||
"""
|
||||
sign: str = ''
|
||||
if value < 0:
|
||||
# As // and % round towards -Inf, convert into a positive value and prepend the negative sign
|
||||
sign = '-'
|
||||
value = -value
|
||||
# Split into full francs and fractions (centimes)
|
||||
full: int = value // 100
|
||||
frac: int = value % 100
|
||||
csign: str = 'CHF ' if with_currencysign else ''
|
||||
# Put it all together; centimes are always padded with 2 zeros
|
||||
return f'{csign}{sign}{full}.{frac:02}'
|
||||
|
||||
|
||||
def parse_chf(value: str) -> int:
|
||||
"""
|
||||
Parse a currency value into machine-readable format (integer centimes). The prefix "CHF", the decimal point, and
|
||||
digits after the decimal point are optional.
|
||||
|
||||
:param value: The value to parse.
|
||||
:return: An integer representation of the value.
|
||||
:raises: Value error: If more than two digits after the decimal point are present.
|
||||
"""
|
||||
# Remove optional leading "CHF" and strip whitespace
|
||||
value = value.strip()
|
||||
if value.startswith('CHF'):
|
||||
value = value[3:]
|
||||
value = value.strip()
|
||||
if '.' not in value:
|
||||
# already is an integer; parse and turn into centimes
|
||||
return int(value, 10) * 100
|
||||
# Split at the decimal point
|
||||
full, frac = value.split('.', 1)
|
||||
if len(frac) > 2:
|
||||
raise ValueError('Needs max. 2 digits after decimal point')
|
||||
elif len(frac) < 2:
|
||||
# Right-pad fraction with zeros ("x." -> "x.00", "x.x" -> "x.x0")
|
||||
frac = frac + '0' * (2 - len(frac))
|
||||
# Parse both parts
|
||||
ifrac: int = int(frac, 10)
|
||||
ifull: int = int(full, 10)
|
||||
if ifrac < 0:
|
||||
raise ValueError('Fraction part must not be negative.')
|
||||
if full.startswith('-'):
|
||||
ifrac = -ifrac
|
||||
# Combine into centime integer
|
||||
return ifull * 100 + ifrac
|
0
matemat/util/test/__init__.py
Normal file
0
matemat/util/test/__init__.py
Normal file
|
@ -1,7 +1,7 @@
|
|||
|
||||
import unittest
|
||||
|
||||
from matemat.webserver.util import format_chf, parse_chf
|
||||
from matemat.util.currency_format import format_chf, parse_chf
|
||||
|
||||
|
||||
class TestCurrencyFormat(unittest.TestCase):
|
|
@ -17,7 +17,8 @@ import jinja2
|
|||
from matemat import __version__ as matemat_version
|
||||
from matemat.exceptions import HttpException
|
||||
from matemat.webserver import RequestArguments, PageletResponse, RedirectResponse, TemplateResponse
|
||||
from matemat.webserver.util import parse_args, format_chf
|
||||
from matemat.webserver.util import parse_args
|
||||
from matemat.util.currency_format import format_chf
|
||||
|
||||
#
|
||||
# Python internal class hacks
|
||||
|
|
|
@ -8,7 +8,7 @@ from matemat.webserver import pagelet, RequestArguments, PageletResponse, Redire
|
|||
from matemat.db import MatematDatabase
|
||||
from matemat.primitives import Product
|
||||
from matemat.exceptions import DatabaseConsistencyError, HttpException
|
||||
from matemat.webserver.util import parse_chf
|
||||
from matemat.util.currency_format import parse_chf
|
||||
|
||||
|
||||
@pagelet('/modproduct')
|
||||
|
|
|
@ -8,7 +8,7 @@ from matemat.webserver import pagelet, RequestArguments, PageletResponse, Redire
|
|||
from matemat.db import MatematDatabase
|
||||
from matemat.primitives import User
|
||||
from matemat.exceptions import DatabaseConsistencyError, HttpException
|
||||
from matemat.webserver.util import parse_chf
|
||||
from matemat.util.currency_format import parse_chf
|
||||
|
||||
|
||||
@pagelet('/moduser')
|
||||
|
|
|
@ -136,59 +136,3 @@ def parse_args(request: str, postbody: Optional[bytes] = None, enctype: str = 't
|
|||
raise ValueError(f'Unsupported Content-Type: {enctype}')
|
||||
# Return the path and the parsed arguments
|
||||
return tokens.path, args
|
||||
|
||||
|
||||
def format_chf(value: int, with_currencysign: bool = True) -> str:
|
||||
"""
|
||||
Formats a centime value into a commonly understood representation ("CHF -13.37").
|
||||
|
||||
:param value: The value to format, in centimes.
|
||||
:param with_currencysign: Whether to include the currency prefix ("CHF ") in the output.
|
||||
:return: A human-readable string representation.
|
||||
"""
|
||||
sign: str = ''
|
||||
if value < 0:
|
||||
# As // and % round towards -Inf, convert into a positive value and prepend the negative sign
|
||||
sign = '-'
|
||||
value = -value
|
||||
# Split into full francs and fractions (centimes)
|
||||
full: int = value // 100
|
||||
frac: int = value % 100
|
||||
csign: str = 'CHF ' if with_currencysign else ''
|
||||
# Put it all together; centimes are always padded with 2 zeros
|
||||
return f'{csign}{sign}{full}.{frac:02}'
|
||||
|
||||
|
||||
def parse_chf(value: str) -> int:
|
||||
"""
|
||||
Parse a currency value into machine-readable format (integer centimes). The prefix "CHF", the decimal point, and
|
||||
digits after the decimal point are optional.
|
||||
|
||||
:param value: The value to parse.
|
||||
:return: An integer representation of the value.
|
||||
:raises: Value error: If more than two digits after the decimal point are present.
|
||||
"""
|
||||
# Remove optional leading "CHF" and strip whitespace
|
||||
value = value.strip()
|
||||
if value.startswith('CHF'):
|
||||
value = value[3:]
|
||||
value = value.strip()
|
||||
if '.' not in value:
|
||||
# already is an integer; parse and turn into centimes
|
||||
return int(value, 10) * 100
|
||||
# Split at the decimal point
|
||||
full, frac = value.split('.', 1)
|
||||
if len(frac) > 2:
|
||||
raise ValueError('Needs max. 2 digits after decimal point')
|
||||
elif len(frac) < 2:
|
||||
# Right-pad fraction with zeros ("x." -> "x.00", "x.x" -> "x.x0")
|
||||
frac = frac + '0' * (2 - len(frac))
|
||||
# Parse both parts
|
||||
ifrac: int = int(frac, 10)
|
||||
ifull: int = int(full, 10)
|
||||
if ifrac < 0:
|
||||
raise ValueError('Fraction part must not be negative.')
|
||||
if full.startswith('-'):
|
||||
ifrac = -ifrac
|
||||
# Combine into centime integer
|
||||
return ifull * 100 + ifrac
|
||||
|
|
Loading…
Reference in a new issue