diff --git a/matemat/webserver/httpd.py b/matemat/webserver/httpd.py
index cd4f823..4d6d87c 100644
--- a/matemat/webserver/httpd.py
+++ b/matemat/webserver/httpd.py
@@ -17,7 +17,7 @@ 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
+from matemat.webserver.util import parse_args, format_chf
#
# Python internal class hacks
@@ -117,8 +117,9 @@ class MatematHTTPServer(HTTPServer):
# Set up the Jinja2 environment
self.jinja_env: jinja2.Environment = jinja2.Environment(
loader=jinja2.FileSystemLoader(os.path.abspath(templateroot)),
- autoescape=jinja2.select_autoescape(default=True)
+ autoescape=jinja2.select_autoescape(default=True),
)
+ self.jinja_env.filters['chf'] = format_chf
# Set up logger
self.logger: logging.Logger = logging.getLogger('matemat.webserver')
self.logger.setLevel(log_level)
diff --git a/matemat/webserver/pagelets/modproduct.py b/matemat/webserver/pagelets/modproduct.py
index 3da8ff9..e0e6814 100644
--- a/matemat/webserver/pagelets/modproduct.py
+++ b/matemat/webserver/pagelets/modproduct.py
@@ -8,6 +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
@pagelet('/modproduct')
@@ -60,8 +61,8 @@ def handle_change(args: RequestArguments, product: Product, db: MatematDatabase,
if 'name' not in args or 'pricemember' not in args or 'pricenonmember' not in args or 'stock' not in args:
return
name = str(args.name)
- price_member = int(str(args.pricemember))
- price_non_member = int(str(args.pricenonmember))
+ price_member = parse_chf(str(args.pricemember))
+ price_non_member = parse_chf(str(args.pricenonmember))
stock = int(str(args.stock))
try:
db.change_product(product,
diff --git a/matemat/webserver/pagelets/moduser.py b/matemat/webserver/pagelets/moduser.py
index 301ddde..4dda3f0 100644
--- a/matemat/webserver/pagelets/moduser.py
+++ b/matemat/webserver/pagelets/moduser.py
@@ -8,6 +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
@pagelet('/moduser')
@@ -62,7 +63,7 @@ def handle_change(args: RequestArguments, user: User, db: MatematDatabase, confi
username = str(args.username)
email = str(args.email)
password = str(args.password)
- balance = int(str(args.balance))
+ balance = parse_chf(str(args.balance))
is_member = 'ismember' in args
is_admin = 'isadmin' in args
if len(email) == 0:
diff --git a/matemat/webserver/util.py b/matemat/webserver/util.py
index c95d303..ddbb41e 100644
--- a/matemat/webserver/util.py
+++ b/matemat/webserver/util.py
@@ -136,3 +136,53 @@ 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.
+ """
+ value = value.strip()
+ if value.startswith('CHF'):
+ value = value[3:]
+ value = value.strip()
+ if '.' not in value:
+ return int(value) * 100
+ full, frac = value.split('.', 1)
+ if len(frac) > 2:
+ raise ValueError('Needs max. 2 digits after decimal point')
+ elif len(frac) < 2:
+ frac = frac + '0' * (2 - len(frac))
+ ifrac: int = int(frac)
+ ifull: int = int(full)
+ if ifrac < 0:
+ raise ValueError('Fraction part must not be negative.')
+ if ifull < 0:
+ ifrac = -ifrac
+ return ifull * 100 + ifrac
diff --git a/templates/modproduct.html b/templates/modproduct.html
index 698790f..2158139 100644
--- a/templates/modproduct.html
+++ b/templates/modproduct.html
@@ -15,10 +15,10 @@
-
+ CHF
-
+ CHF