# Copyright (c) 2025 Ferrosoft GmbH. All rights reserved.
from decimal import Decimal
from django.db.models.fields import DecimalField
[docs]
class CustomDecimalField(DecimalField):
"""Base decimal field that omits ``max_digits`` / ``decimal_places`` from migrations.
Subclasses hard-code these parameters so migrations stay clean. The
``ROUND`` class attribute controls the quantization precision used by the
``round`` class method.
"""
ROUND = Decimal("1.00")
[docs]
def deconstruct(self):
name, path, args, kwargs = super().deconstruct()
del kwargs["max_digits"]
del kwargs["decimal_places"]
if kwargs.get("default") == 0:
del kwargs["default"]
return name, path, args, kwargs
[docs]
@classmethod
def round(cls, value: Decimal) -> Decimal:
return value.quantize(cls.ROUND)
[docs]
class NormalDecimalField(CustomDecimalField):
"""DecimalField with max_digits and decimal_places useful for most cases."""
def __init__(self, *args, **kwargs):
super().__init__(
*args,
max_digits=12,
decimal_places=4,
default=kwargs.pop("default", 0),
**kwargs,
)
[docs]
class BigDecimalField(CustomDecimalField):
"""DecimalField with max_digits and decimal_places useful for big values."""
def __init__(self, *args, **kwargs):
super().__init__(
*args,
max_digits=18,
decimal_places=9,
default=kwargs.pop("default", 0),
**kwargs,
)
[docs]
class TonDecimalField(CustomDecimalField):
"""DecimalField with max_digits and decimal_places useful for tonnages
not requiring any decimal places."""
ROUND = Decimal("1")
def __init__(self, *args, **kwargs):
super().__init__(
*args,
max_digits=9,
decimal_places=0,
default=kwargs.pop("default", 0),
**kwargs,
)
[docs]
class BigMoneyField(CustomDecimalField):
"""DecimalField with max_digits and decimal_places useful for big money values."""
ROUND = Decimal("1.000")
def __init__(self, *args, **kwargs):
super().__init__(
*args,
max_digits=18,
decimal_places=3,
default=kwargs.pop("default", 0),
**kwargs,
)
[docs]
class CoordinateDecimalField(CustomDecimalField):
"""DecimalField with max_digits and decimal_places useful for geographic coordinate."""
ROUND = Decimal("1.000000")
def __init__(self, *args, **kwargs):
super().__init__(
*args,
max_digits=9,
decimal_places=6,
default=kwargs.pop("default", 0),
**kwargs,
)