27.02.26
This commit is contained in:
@@ -9,7 +9,7 @@ UNITS = (
|
|||||||
TENS = "ZERO TEN TWENTY THIRTY FORTY FIFTY SIXTY SEVENTY EIGHTY NINETY".split()
|
TENS = "ZERO TEN TWENTY THIRTY FORTY FIFTY SIXTY SEVENTY EIGHTY NINETY".split()
|
||||||
|
|
||||||
|
|
||||||
def _under_thousand(n, use_and=True):
|
def _under_thousand(n):
|
||||||
words = []
|
words = []
|
||||||
|
|
||||||
hundreds = n // 100
|
hundreds = n // 100
|
||||||
@@ -18,7 +18,7 @@ def _under_thousand(n, use_and=True):
|
|||||||
if hundreds:
|
if hundreds:
|
||||||
words.append(UNITS[hundreds])
|
words.append(UNITS[hundreds])
|
||||||
words.append("HUNDRED")
|
words.append("HUNDRED")
|
||||||
if remainder and use_and:
|
if remainder:
|
||||||
words.append("AND")
|
words.append("AND")
|
||||||
|
|
||||||
if remainder:
|
if remainder:
|
||||||
@@ -32,46 +32,108 @@ def _under_thousand(n, use_and=True):
|
|||||||
return " ".join(words)
|
return " ".join(words)
|
||||||
|
|
||||||
|
|
||||||
def number_to_words(n, decimals_mode="digit", use_and=True):
|
def integer_to_words(n):
|
||||||
"""
|
if n == 0:
|
||||||
decimals_mode:
|
return "ZERO"
|
||||||
- "digit" → ONE HUNDRED POINT TWO FIVE
|
|
||||||
- "fraction" → ONE HUNDRED AND 25/100
|
|
||||||
"""
|
|
||||||
|
|
||||||
n = Decimal(str(n)).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
|
|
||||||
|
|
||||||
integer_part = int(n)
|
|
||||||
decimal_part = int((n - integer_part) * 100)
|
|
||||||
|
|
||||||
if integer_part == 0:
|
|
||||||
words = "ZERO"
|
|
||||||
else:
|
|
||||||
parts = []
|
parts = []
|
||||||
|
|
||||||
millions = integer_part // 1_000_000
|
millions = n // 1_000_000
|
||||||
thousands = (integer_part // 1_000) % 1_000
|
thousands = (n // 1_000) % 1_000
|
||||||
remainder = integer_part % 1_000
|
remainder = n % 1_000
|
||||||
|
|
||||||
if millions:
|
if millions:
|
||||||
parts.append(_under_thousand(millions, use_and))
|
parts.append(_under_thousand(millions))
|
||||||
parts.append("MILLION")
|
parts.append("MILLION")
|
||||||
|
|
||||||
if thousands:
|
if thousands:
|
||||||
parts.append(_under_thousand(thousands, use_and))
|
parts.append(_under_thousand(thousands))
|
||||||
parts.append("THOUSAND")
|
parts.append("THOUSAND")
|
||||||
|
|
||||||
if remainder:
|
if remainder:
|
||||||
parts.append(_under_thousand(remainder, use_and))
|
parts.append(_under_thousand(remainder))
|
||||||
|
|
||||||
words = " ".join(parts)
|
return " ".join(parts)
|
||||||
|
|
||||||
# Gestion décimales
|
|
||||||
|
# ==============================
|
||||||
|
# 💰 MONETARY
|
||||||
|
# ==============================
|
||||||
|
|
||||||
|
def amount_to_currency_words(amount,
|
||||||
|
major_singular="DOLLAR",
|
||||||
|
major_plural="DOLLARS",
|
||||||
|
minor_singular="CENT",
|
||||||
|
minor_plural="CENTS"):
|
||||||
|
"""
|
||||||
|
Example:
|
||||||
|
1.20 → ONE DOLLAR AND TWENTY CENTS
|
||||||
|
2.00 → TWO DOLLARS
|
||||||
|
"""
|
||||||
|
|
||||||
|
amount = Decimal(str(amount)).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
|
||||||
|
|
||||||
|
integer_part = int(amount)
|
||||||
|
decimal_part = int((amount - integer_part) * 100)
|
||||||
|
|
||||||
|
words = []
|
||||||
|
|
||||||
|
# Major unit
|
||||||
|
major_words = integer_to_words(integer_part)
|
||||||
|
words.append(major_words)
|
||||||
|
|
||||||
|
if integer_part == 1:
|
||||||
|
words.append(major_singular)
|
||||||
|
else:
|
||||||
|
words.append(major_plural)
|
||||||
|
|
||||||
|
# Minor unit
|
||||||
if decimal_part:
|
if decimal_part:
|
||||||
if decimals_mode == "digit":
|
words.append("AND")
|
||||||
digits = " ".join(UNITS[int(d)] for d in f"{decimal_part:02d}")
|
minor_words = integer_to_words(decimal_part)
|
||||||
words = f"{words} POINT {digits}"
|
words.append(minor_words)
|
||||||
elif decimals_mode == "fraction":
|
|
||||||
words = f"{words} AND {decimal_part:02d}/100"
|
|
||||||
|
|
||||||
return words.upper()
|
if decimal_part == 1:
|
||||||
|
words.append(minor_singular)
|
||||||
|
else:
|
||||||
|
words.append(minor_plural)
|
||||||
|
|
||||||
|
return " ".join(words)
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================
|
||||||
|
# ⚖️ QUANTITY WITH UNIT
|
||||||
|
# ==============================
|
||||||
|
|
||||||
|
def quantity_to_words(quantity,
|
||||||
|
unit_singular="METRIC TON",
|
||||||
|
unit_plural="METRIC TONS"):
|
||||||
|
"""
|
||||||
|
Example:
|
||||||
|
1 → ONE METRIC TON
|
||||||
|
23 → TWENTY THREE METRIC TONS
|
||||||
|
1.5 → ONE POINT FIVE METRIC TONS
|
||||||
|
"""
|
||||||
|
|
||||||
|
quantity = Decimal(str(quantity)).normalize()
|
||||||
|
|
||||||
|
if quantity == quantity.to_integral():
|
||||||
|
integer_part = int(quantity)
|
||||||
|
words = integer_to_words(integer_part)
|
||||||
|
|
||||||
|
if integer_part == 1:
|
||||||
|
unit = unit_singular
|
||||||
|
else:
|
||||||
|
unit = unit_plural
|
||||||
|
|
||||||
|
return f"{words} {unit}"
|
||||||
|
|
||||||
|
else:
|
||||||
|
# lecture décimale simple pour quantités
|
||||||
|
integer_part = int(quantity)
|
||||||
|
decimal_str = str(quantity).split(".")[1]
|
||||||
|
|
||||||
|
words = integer_to_words(integer_part)
|
||||||
|
decimal_words = " ".join(UNITS[int(d)] for d in decimal_str)
|
||||||
|
|
||||||
|
return f"{words} POINT {decimal_words} {unit_plural}"
|
||||||
@@ -15,7 +15,7 @@ import datetime
|
|||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
from trytond.exceptions import UserWarning, UserError
|
from trytond.exceptions import UserWarning, UserError
|
||||||
from trytond.modules.purchase_trade.numbers_to_words import number_to_words
|
from trytond.modules.purchase_trade.numbers_to_words import quantity_to_words, amount_to_currency_words
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -268,14 +268,14 @@ class Sale(metaclass=PoolMeta):
|
|||||||
@property
|
@property
|
||||||
def report_qt(self):
|
def report_qt(self):
|
||||||
if self.lines:
|
if self.lines:
|
||||||
return number_to_words(self.lines[0].quantity)
|
return quantity_to_words(self.lines[0].quantity)
|
||||||
else:
|
else:
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def report_price(self):
|
def report_price(self):
|
||||||
if self.lines:
|
if self.lines:
|
||||||
return number_to_words(self.lines[0].unit_price)
|
return amount_to_currency_words(self.lines[0].unit_price)
|
||||||
else:
|
else:
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user