Source code for shuup.core.models._taxes

from django.core.exceptions import ValidationError
from django.db import models
from django.utils.translation import gettext_lazy as _
from parler.models import TranslatedFields

from shuup.core.fields import CurrencyField, InternalIdentifierField, MoneyValueField
from shuup.utils.analog import define_log_model
from shuup.utils.i18n import format_money, format_percent
from shuup.utils.properties import MoneyProperty, MoneyPropped

from ._base import ChangeProtected, TranslatableShuupModel


class Tax(MoneyPropped, ChangeProtected, TranslatableShuupModel):
    identifier_attr = "code"

    change_protect_message = _("Can't change the business critical fields of the Tax that is in use.")
    unprotected_fields = ["enabled"]

    code = InternalIdentifierField(
        unique=True,
        editable=True,
        verbose_name=_("code"),
        help_text=_("The abbreviated tax code name."),
    )

    translations = TranslatedFields(
        name=models.CharField(
            max_length=124,
            verbose_name=_("name"),
            help_text=_("The name of the tax. It is shown in order lines, in order invoices and confirmations."),
        ),
    )

    rate = models.DecimalField(
        max_digits=6,
        decimal_places=5,
        blank=True,
        null=True,
        verbose_name=_("tax rate"),
        help_text=_(
            "The percentage rate of the tax. "
            "Mutually exclusive with the flat amount tax (flat tax is rarely used "
            "and the option is therefore hidden by default; contact Shuup to enable)."
        ),
    )
    amount = MoneyProperty("amount_value", "currency")
    amount_value = MoneyValueField(
        default=None,
        blank=True,
        null=True,
        verbose_name=_("tax amount value"),
        help_text=_("The flat amount of the tax. Mutually exclusive with percentage rates tax."),
    )
    currency = CurrencyField(
        default=None,
        blank=True,
        null=True,
        verbose_name=_("currency of the amount tax"),
    )

    enabled = models.BooleanField(
        default=True,
        verbose_name=_("enabled"),
        help_text=_("Enable if this tax is valid and should be active."),
    )

[docs] def clean(self): super().clean() if self.rate is None and self.amount is None: raise ValidationError(_("Either rate or amount tax is required.")) if self.amount is not None and self.rate is not None: raise ValidationError(_("Can't have both rate and amount taxes. They are mutually exclusive.")) if self.amount is not None and not self.currency: raise ValidationError(_("Currency is required if the amount tax value is specified."))
[docs] def calculate_amount(self, base_amount): """ Calculate tax amount with this tax for a given base amount. :type base_amount: shuup.utils.money.Money :rtype: shuup.utils.money.Money """ if self.amount is not None: return self.amount if self.rate is not None: return self.rate * base_amount raise ValueError(f"Error! Calculations of the tax amount failed. Improperly configured tax: {self}.")
def __str__(self): text = super().__str__() if self.rate is not None: text += f" ({format_percent(self.rate, digits=3)})" if self.amount is not None: text += f" ({format_money(self.amount)})" return text def _are_changes_protected(self): return self.order_line_taxes.exists() class Meta: verbose_name = _("tax") verbose_name_plural = _("taxes") class TaxClass(TranslatableShuupModel): identifier = InternalIdentifierField(unique=True) translations = TranslatedFields( name=models.CharField( max_length=100, verbose_name=_("name"), help_text=_("The tax class name. Tax classes are used to control how taxes are applied to the products."), ), ) enabled = models.BooleanField( default=True, verbose_name=_("enabled"), help_text=_("Enable if this tax class is valid and should be active."), ) class Meta: verbose_name = _("tax class") verbose_name_plural = _("tax classes") class CustomerTaxGroup(TranslatableShuupModel): identifier = InternalIdentifierField(unique=True) translations = TranslatedFields( name=models.CharField( max_length=100, verbose_name=_("name"), help_text=_( "The customer tax group name. " "Customer tax groups can be used to control how taxes are applied to a set of customers. " ), ), ) enabled = models.BooleanField(default=True, verbose_name=_("enabled")) class Meta: verbose_name = _("customer tax group") verbose_name_plural = _("customer tax groups")
[docs] @classmethod def get_default_person_group(cls): obj, c = CustomerTaxGroup.objects.get_or_create( identifier="default_person_customers", defaults={"name": _("Retail Customers")}, ) return obj
[docs] @classmethod def get_default_company_group(cls): obj, c = CustomerTaxGroup.objects.get_or_create( identifier="default_company_customers", defaults={"name": _("Company Customers")}, ) return obj
TaxLogEntry = define_log_model(Tax) TaxClassLogEntry = define_log_model(TaxClass) CustomerTaxGroupLogEntry = define_log_model(CustomerTaxGroup)