38 lines
1.3 KiB
Python
38 lines
1.3 KiB
Python
"""Exact fixed point arithmetic shared by persistence, forms and PDF output."""
|
|
from decimal import Decimal, InvalidOperation, ROUND_HALF_UP
|
|
|
|
ZERO = Decimal('0')
|
|
CENT = Decimal('0.01')
|
|
UNIT = Decimal('0.0001')
|
|
MAX_VALUE = Decimal('999999999')
|
|
MAX_TOTAL = Decimal('999999999999.99')
|
|
|
|
|
|
def number(value, label='数值', allow_zero=True):
|
|
try:
|
|
result = Decimal(str(value if value not in (None, '') else 0).strip())
|
|
except (InvalidOperation, ValueError):
|
|
raise ValueError('%s必须是有效数字' % label)
|
|
if not result.is_finite() or result < 0 or result > MAX_VALUE:
|
|
raise ValueError('%s必须在 0 到 %s 之间' % (label, MAX_VALUE))
|
|
if result != result.quantize(UNIT):
|
|
raise ValueError('%s最多保留四位小数' % label)
|
|
if not allow_zero and result == 0:
|
|
raise ValueError('%s必须大于零' % label)
|
|
return result.quantize(UNIT)
|
|
|
|
|
|
def money(value):
|
|
return Decimal(str(value or 0)).quantize(CENT, rounding=ROUND_HALF_UP)
|
|
|
|
|
|
def line_amount(qty, price):
|
|
amount = money(number(qty, '数量') * number(price, '单价'))
|
|
if amount > MAX_TOTAL:
|
|
raise ValueError('明细金额过大')
|
|
return amount
|
|
|
|
|
|
def format_price(value):
|
|
return format(Decimal(str(value or 0)), '.4f').rstrip('0').rstrip('.') or '0'
|