reports: Improve formatting of non-currency commodities.
Introduce the get_commodity_format() function, which returns Babel's usual format string for currencies, but returns a version of it "merged" with the locale's currency unit pattern for other commodities. BaseODS then calls this function where needed to format amounts.
This commit is contained in:
parent
b4f2b506be
commit
de10197af7
3 changed files with 49 additions and 14 deletions
|
@ -737,7 +737,7 @@ class BaseODS(BaseSpreadsheet[RT, ST], metaclass=abc.ABCMeta):
|
|||
root: odf.element.Element,
|
||||
locale: babel.core.Locale,
|
||||
code: str,
|
||||
fmt_index: int,
|
||||
amount: DecimalCompat=0,
|
||||
properties: Optional[odf.style.TextProperties]=None,
|
||||
*,
|
||||
fmt_key: Optional[str]=None,
|
||||
|
@ -746,11 +746,7 @@ class BaseODS(BaseSpreadsheet[RT, ST], metaclass=abc.ABCMeta):
|
|||
if fmt_key is None:
|
||||
fmt_key = self.currency_fmt_key
|
||||
pattern = locale.currency_formats[fmt_key]
|
||||
fmts = pattern.pattern.split(';')
|
||||
try:
|
||||
fmt = fmts[fmt_index]
|
||||
except IndexError:
|
||||
fmt = fmts[0]
|
||||
fmt = get_commodity_format(locale, code, amount, fmt_key)
|
||||
style = self.replace_child(
|
||||
root,
|
||||
odf.number.CurrencyStyle,
|
||||
|
@ -823,7 +819,7 @@ class BaseODS(BaseSpreadsheet[RT, ST], metaclass=abc.ABCMeta):
|
|||
root, locale, code, 0, positive_properties, volatile=True,
|
||||
)
|
||||
curr_style = self._build_currency_style(
|
||||
root, locale, code, 1, negative_properties,
|
||||
root, locale, code, -1, negative_properties,
|
||||
)
|
||||
curr_style.addElement(odf.style.Map(
|
||||
condition='value()>=0', applystylename=pos_style,
|
||||
|
@ -1133,9 +1129,9 @@ class BaseODS(BaseSpreadsheet[RT, ST], metaclass=abc.ABCMeta):
|
|||
)
|
||||
return self.currency_cell(amount, **attrs)
|
||||
else:
|
||||
lines = [babel.numbers.format_currency(
|
||||
number, currency, locale=self.locale, format_type=self.currency_fmt_key,
|
||||
) for number, currency in balance.values()]
|
||||
lines = [babel.numbers.format_currency(number, currency, get_commodity_format(
|
||||
self.locale, currency, None, self.currency_fmt_key,
|
||||
)) for number, currency in balance.values()]
|
||||
attrs['stylename'] = self.merge_styles(
|
||||
attrs.get('stylename'), self.style_endtext,
|
||||
)
|
||||
|
@ -1287,6 +1283,47 @@ def account_balances(
|
|||
MutableBalance(),
|
||||
))
|
||||
|
||||
def get_commodity_format(locale: babel.core.Locale,
|
||||
code: str,
|
||||
amount: Optional[DecimalCompat]=None,
|
||||
format_type: str='accounting',
|
||||
) -> str:
|
||||
"""Return a format string for a commodity
|
||||
|
||||
Typical use looks like::
|
||||
|
||||
number, code = post.units
|
||||
fmt = get_commodity_format(locale, code)
|
||||
units_s = babel.numbers.format_currency(number, code, fmt)
|
||||
|
||||
When the commodity code refers to a real currency, you get the same format
|
||||
string provided by Babel.
|
||||
|
||||
For other commodities like stock, you get a format code built from the
|
||||
locale's currency unit pattern.
|
||||
|
||||
If ``amount`` is defined, the format string will be specifically for that
|
||||
number, whether positive or negative. Otherwise, the format string may
|
||||
define both positive and negative formats.
|
||||
"""
|
||||
fmt: str = locale.currency_formats[format_type].pattern
|
||||
if amount is not None:
|
||||
fmt, _, neg_fmt = fmt.partition(';')
|
||||
if amount < 0 and neg_fmt:
|
||||
fmt = neg_fmt
|
||||
symbol = babel.numbers.get_currency_symbol(code, locale)
|
||||
if symbol != code:
|
||||
return fmt
|
||||
else:
|
||||
long_fmt: str = babel.numbers.get_currency_unit_pattern(code, locale=locale)
|
||||
return re.sub(
|
||||
r'[#0,.\s¤]+',
|
||||
lambda match: long_fmt.format(
|
||||
match.group(0).replace('¤', '').strip(), '¤¤',
|
||||
),
|
||||
fmt,
|
||||
)
|
||||
|
||||
def normalize_amount_func(account_name: str) -> Callable[[T], T]:
|
||||
"""Get a function to normalize amounts for reporting
|
||||
|
||||
|
|
2
setup.py
2
setup.py
|
@ -5,7 +5,7 @@ from setuptools import setup
|
|||
setup(
|
||||
name='conservancy_beancount',
|
||||
description="Plugin, library, and reports for reading Conservancy's books",
|
||||
version='1.9.5',
|
||||
version='1.9.6',
|
||||
author='Software Freedom Conservancy',
|
||||
author_email='info@sfconservancy.org',
|
||||
license='GNU AGPLv3+',
|
||||
|
|
|
@ -549,9 +549,7 @@ def test_ods_writer_balance_cell_multi_currency(ods_writer):
|
|||
]]
|
||||
balance = core.Balance(amounts)
|
||||
cell = ods_writer.balance_cell(balance)
|
||||
assert cell.text == '\0'.join(babel.numbers.format_currency(
|
||||
number, currency, locale=EN_US, format_type='accounting',
|
||||
) for number, currency in amounts)
|
||||
assert cell.text == '2,500.00 RUB\0R$3,500.00'
|
||||
|
||||
@pytest.mark.parametrize('cell_source,style_name', testutil.combine_values(
|
||||
CURRENCY_CELL_DATA,
|
||||
|
|
Loading…
Reference in a new issue